How to use additionalBrowserCleanup method of net.serenitybdd.junit.runners.SerenityRunner class

Best Serenity JUnit code snippet using net.serenitybdd.junit.runners.SerenityRunner.additionalBrowserCleanup

Run Serenity JUnit automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

copy
1package net.serenitybdd.junit.runners;
2
3import com.google.inject.Injector;
4import com.google.inject.Module;
5import net.serenitybdd.core.Serenity;
6import net.serenitybdd.core.environment.WebDriverConfiguredEnvironment;
7import net.serenitybdd.core.injectors.EnvironmentDependencyInjector;
8import net.thucydides.core.annotations.ManagedWebDriverAnnotatedField;
9import net.thucydides.core.annotations.ManualTestMarkedAsError;
10import net.thucydides.core.annotations.ManualTestMarkedAsFailure;
11import net.thucydides.core.annotations.TestCaseAnnotations;
12import net.thucydides.core.batches.BatchManager;
13import net.thucydides.core.batches.BatchManagerProvider;
14import net.thucydides.core.guice.Injectors;
15import net.thucydides.core.guice.webdriver.WebDriverModule;
16import net.thucydides.core.model.TestOutcome;
17import net.thucydides.core.pages.Pages;
18import net.thucydides.core.reports.AcceptanceTestReporter;
19import net.thucydides.core.reports.ReportService;
20import net.thucydides.core.steps.PageObjectDependencyInjector;
21import net.thucydides.core.steps.StepAnnotations;
22import net.thucydides.core.steps.StepEventBus;
23import net.thucydides.core.steps.StepFactory;
24import net.thucydides.core.steps.stepdata.StepData;
25import net.thucydides.core.tags.TagScanner;
26import net.thucydides.core.tags.Taggable;
27import net.thucydides.core.webdriver.*;
28import net.thucydides.junit.listeners.JUnitStepListener;
29import org.junit.runner.Description;
30import org.junit.runner.notification.Failure;
31import org.junit.runner.notification.RunNotifier;
32import org.junit.runners.BlockJUnit4ClassRunner;
33import org.junit.runners.model.FrameworkMethod;
34import org.junit.runners.model.InitializationError;
35import org.junit.runners.model.Statement;
36import org.openqa.selenium.WebDriver;
37import org.slf4j.Logger;
38import org.slf4j.LoggerFactory;
39
40import java.io.File;
41import java.util.Collection;
42import java.util.List;
43import java.util.Map;
44import java.util.Optional;
45import java.util.function.Consumer;
46
47import static net.serenitybdd.core.Serenity.initializeTestSession;
48import static net.thucydides.core.ThucydidesSystemProperty.TEST_RETRY_COUNT;
49import static org.apache.commons.lang3.StringUtils.isEmpty;
50
51/**
52 * A test runner for WebDriver-based web tests. This test runner initializes a
53 * WebDriver instance before running the tests in their order of appearance. At
54 * the end of the tests, it closes and quits the WebDriver instance.
55 * The test runner will by default produce output in XML and HTML. This
56 * can extended by subscribing more reporter implementations to the test runner.
57 *
58 * @author johnsmart
59 */
60public class SerenityRunner extends BlockJUnit4ClassRunner implements Taggable {
61
62    /**
63     * Provides a proxy of the ScenarioSteps object used to invoke the test steps.
64     * This proxy notifies the test runner about individual step outcomes.
65     */
66    private StepFactory stepFactory;
67    private Pages pages;
68    private final WebdriverManager webdriverManager;
69    private String requestedDriver;
70    private ReportService reportService;
71    private final TestConfiguration theTest;
72    private FailureRerunner failureRerunner;
73    /**
74     * Special listener that keeps track of test step execution and results.
75     */
76    private JUnitStepListener stepListener;
77
78    private PageObjectDependencyInjector dependencyInjector;
79
80    private FailureDetectingStepListener failureDetectingStepListener;
81
82
83    /**
84     * Retrieve the runner getConfiguration().from an external source.
85     */
86    private DriverConfiguration configuration;
87
88    private TagScanner tagScanner;
89
90    private BatchManager batchManager;
91
92    private final Logger logger = LoggerFactory.getLogger(SerenityRunner.class);
93
94    public Pages getPages() {
95        return pages;
96    }
97
98    /**
99     * Creates a new test runner for WebDriver web tests.
100     *
101     * @param klass the class under test
102     * @throws InitializationError if some JUnit-related initialization problem occurred
103     */
104    public SerenityRunner(final Class<?> klass) throws InitializationError {
105        this(klass, Injectors.getInjector(new WebDriverModule()));
106    }
107
108    /**
109     * Creates a new test runner for WebDriver web tests.
110     *
111     * @param klass the class under test
112     * @param module used to inject a custom Guice module
113     * @throws InitializationError if some JUnit-related initialization problem occurred
114     */
115    public SerenityRunner(Class<?> klass, Module module) throws InitializationError {
116        this(klass, Injectors.getInjector(module));
117    }
118
119    public SerenityRunner(final Class<?> klass,
120                          final Injector injector) throws InitializationError {
121        this(klass,
122                ThucydidesWebDriverSupport.getWebdriverManager(),
123                injector.getInstance(DriverConfiguration.class),
124                injector.getInstance(BatchManager.class)
125        );
126    }
127
128    public SerenityRunner(final Class<?> klass,
129                          final WebDriverFactory webDriverFactory) throws InitializationError {
130        this(klass, webDriverFactory, WebDriverConfiguredEnvironment.getDriverConfiguration());
131    }
132
133    public SerenityRunner(final Class<?> klass,
134                          final WebDriverFactory webDriverFactory,
135                          final DriverConfiguration configuration) throws InitializationError {
136        this(klass,
137                webDriverFactory,
138                configuration,
139                new BatchManagerProvider(configuration).get()
140        );
141    }
142
143    public SerenityRunner(final Class<?> klass,
144                          final WebDriverFactory webDriverFactory,
145                          final DriverConfiguration configuration,
146                          final BatchManager batchManager) throws InitializationError {
147        this(klass,
148                ThucydidesWebDriverSupport.getWebdriverManager(webDriverFactory, configuration),
149                configuration,
150                batchManager
151        );
152    }
153
154    public SerenityRunner(final Class<?> klass, final BatchManager batchManager) throws InitializationError {
155        this(klass,
156                ThucydidesWebDriverSupport.getWebdriverManager(),
157                WebDriverConfiguredEnvironment.getDriverConfiguration(),
158                batchManager);
159    }
160
161    public SerenityRunner(final Class<?> klass,
162                          final WebdriverManager webDriverManager,
163                          final DriverConfiguration configuration,
164                          final BatchManager batchManager) throws InitializationError {
165        super(klass);
166
167        this.theTest = TestConfiguration.forClass(klass).withSystemConfiguration(configuration);
168        this.webdriverManager = webDriverManager;
169        this.configuration = configuration;
170        this.requestedDriver = getSpecifiedDriver(klass);
171        this.tagScanner = new TagScanner(configuration.getEnvironmentVariables());
172        this.failureDetectingStepListener = new FailureDetectingStepListener();
173        this.failureRerunner = new FailureRerunnerXml(configuration);
174
175        if (TestCaseAnnotations.supportsWebTests(klass)) {
176            checkRequestedDriverType();
177        }
178
179        this.batchManager = batchManager;
180
181        batchManager.registerTestCase(klass);
182
183    }
184
185
186    private String getSpecifiedDriver(Class<?> klass) {
187        if (ManagedWebDriverAnnotatedField.hasManagedWebdriverField(klass)) {
188            return ManagedWebDriverAnnotatedField.findFirstAnnotatedField(klass).getDriver();
189        } else {
190            return null;
191        }
192    }
193
194    /**
195     * The Configuration class manages output directories and driver types.
196     * They can be defined as system values, or have sensible defaults.
197     * @return the current configuration
198     */
199    protected DriverConfiguration getConfiguration() {
200        return configuration;
201    }
202
203    /**
204     * Batch Manager used for running tests in parallel batches
205     * @return the current batch manager object
206     */
207    protected BatchManager getBatchManager() {
208        return batchManager;
209    }
210
211    /**
212     * Ensure that the requested driver type is valid before we start the tests.
213     * Otherwise, throw an InitializationError.
214     */
215    private void checkRequestedDriverType() {
216        if (requestedDriverSpecified()) {
217            SupportedWebDriver.getDriverTypeFor(requestedDriver);
218        } else {
219            getConfiguration().getDriverType();
220        }
221    }
222
223    private boolean requestedDriverSpecified() {
224        return !isEmpty(this.requestedDriver);
225    }
226
227    public File getOutputDirectory() {
228        return getConfiguration().getOutputDirectory();
229    }
230
231    /**
232     * To generate reports, different AcceptanceTestReporter instances need to
233     * subscribe to the listener. The listener will tell them when the test is
234     * done, and the reporter can decide what to do.
235     * @param reporter an implementation of the AcceptanceTestReporter interface.
236     */
237    public void subscribeReporter(final AcceptanceTestReporter reporter) {
238        getReportService().subscribe(reporter);
239    }
240
241    public void useQualifier(final String qualifier) {
242        getReportService().useQualifier(qualifier);
243    }
244
245    /**
246     * Runs the tests in the acceptance test case.
247     */
248
249    @Override
250    public void run(final RunNotifier notifier) {
251        if (skipThisTest()) { return; }
252
253        try {
254            RunNotifier localNotifier = initializeRunNotifier(notifier);
255            StepEventBus.getEventBus().registerListener(failureDetectingStepListener);
256
257            super.run(localNotifier);
258            fireNotificationsBasedOnTestResultsTo(notifier);
259        } catch (Throwable someFailure) {
260            someFailure.printStackTrace();
261            throw someFailure;
262        } finally {
263            notifyTestSuiteFinished();
264            generateReports();
265            Map<String, List<String>> failedTests = stepListener.getFailedTests();
266            failureRerunner.recordFailedTests(failedTests);
267            dropListeners(notifier);
268            StepEventBus.getEventBus().dropAllListeners();
269        }
270    }
271
272
273
274    private Optional<TestOutcome> latestOutcome() {
275        if (StepEventBus.getEventBus().getBaseStepListener().getTestOutcomes().isEmpty()) {
276            return Optional.empty();
277        }
278        return Optional.of(StepEventBus.getEventBus().getBaseStepListener().getTestOutcomes().get(0));
279    }
280
281    private void fireNotificationsBasedOnTestResultsTo(RunNotifier notifier) {
282        if (!latestOutcome().isPresent()) {
283            return;
284        }
285    }
286
287    private void notifyTestSuiteFinished() {
288        try {
289            if (dataDrivenTest()) {
290                StepEventBus.getEventBus().exampleFinished();
291            } else {
292                StepEventBus.getEventBus().testSuiteFinished();
293            }
294        } catch (Throwable listenerException) {
295            // We report and ignore listener exceptions so as not to mess up the rest of the test mechanics.
296            logger.error("Test event bus error: " + listenerException.getMessage(), listenerException);
297        }
298    }
299
300    private boolean dataDrivenTest() {
301        return this instanceof TestClassRunnerForParameters;
302    }
303
304    private void dropListeners(final RunNotifier notifier) {
305        JUnitStepListener listener = getStepListener();
306        notifier.removeListener(listener);
307        getStepListener().dropListeners();
308    }
309
310    protected void generateReports() {
311        generateReportsFor(getTestOutcomes());
312    }
313
314    private boolean skipThisTest() {
315        return testNotInCurrentBatch();
316    }
317
318    private boolean testNotInCurrentBatch() {
319        return (batchManager != null) && (!batchManager.shouldExecuteThisTest(getDescription().testCount()));
320    }
321
322    /**
323     * The Step Listener observes and records what happens during the execution of the test.
324     * Once the test is over, the Step Listener can provide the acceptance test outcome in the
325     * form of an TestOutcome object.
326     * @return the current step listener
327     */
328    protected JUnitStepListener getStepListener() {
329        if (stepListener == null) {
330            buildAndConfigureListeners();
331        }
332        return stepListener;
333    }
334
335    protected void setStepListener(JUnitStepListener stepListener) {
336        this.stepListener = stepListener;
337    }
338
339    private void buildAndConfigureListeners() {
340
341        initStepEventBus();
342        if (webtestsAreSupported()) {
343            ThucydidesWebDriverSupport.initialize(requestedDriver);
344            WebDriver driver = ThucydidesWebDriverSupport.getWebdriverManager().getWebdriver();
345            initPagesObjectUsing(driver);
346            setStepListener(initListenersUsing(getPages()));
347            initStepFactoryUsing(getPages());
348        } else {
349            setStepListener(initListeners());
350            initStepFactory();
351        }
352    }
353
354    private RunNotifier initializeRunNotifier(RunNotifier notifier) {
355        notifier.addListener(getStepListener());
356        return notifier;
357    }
358
359    private int maxRetries() {
360        return TEST_RETRY_COUNT.integerFrom(configuration.getEnvironmentVariables(), 0);
361    }
362
363
364    protected void initStepEventBus() {
365        StepEventBus.getEventBus().clear();
366    }
367
368    private void initPagesObjectUsing(final WebDriver driver) {
369        pages = new Pages(driver, getConfiguration());
370        dependencyInjector = new PageObjectDependencyInjector(pages);
371    }
372
373    protected JUnitStepListener initListenersUsing(final Pages pageFactory) {
374
375        return JUnitStepListener.withOutputDirectory(getConfiguration().getOutputDirectory())
376                .and().withPageFactory(pageFactory)
377                .and().withTestClass(getTestClass().getJavaClass())
378                .and().build();
379    }
380
381    protected JUnitStepListener initListeners() {
382        return JUnitStepListener.withOutputDirectory(getConfiguration().getOutputDirectory())
383                .and().withTestClass(getTestClass().getJavaClass())
384                .and().build();
385    }
386
387    private boolean webtestsAreSupported() {
388        return TestCaseAnnotations.supportsWebTests(this.getTestClass().getJavaClass());
389    }
390
391    private void initStepFactoryUsing(final Pages pagesObject) {
392        stepFactory = StepFactory.getFactory().usingPages(pagesObject);
393    }
394
395    private void initStepFactory() {
396        stepFactory = StepFactory.getFactory();
397    }
398
399    private ReportService getReportService() {
400        if (reportService == null) {
401            reportService = new ReportService(getOutputDirectory(), getDefaultReporters());
402        }
403        return reportService;
404    }
405
406    /**
407     * A test runner can generate reports via Reporter instances that subscribe
408     * to the test runner. The test runner tells the reporter what directory to
409     * place the reports in. Then, at the end of the test, the test runner
410     * notifies these reporters of the test outcomes. The reporter's job is to
411     * process each test run outcome and do whatever is appropriate.
412     *
413     * @param testOutcomeResults the test results from the previous test run.
414     */
415    private void generateReportsFor(final List<TestOutcome> testOutcomeResults) {
416        getReportService().generateReportsFor(testOutcomeResults);
417        getReportService().generateConfigurationsReport();
418    }
419
420    @Override
421    protected void runChild(FrameworkMethod method, RunNotifier notifier) {
422
423        TestMethodConfiguration theMethod = TestMethodConfiguration.forMethod(method);
424
425        clearMetadataIfRequired();
426        resetStepLibrariesIfRequired();
427
428        if(!failureRerunner.hasToRunTest(method.getDeclaringClass().getCanonicalName(),method.getMethod().getName()))
429        {
430            return;
431        }
432
433        if (shouldSkipTest(method)) {
434            return;
435        }
436
437        if (theMethod.isManual()) {
438            markAsManual(method).accept(notifier);
439            return;
440        } else if (theMethod.isPending()) {
441            markAsPending(method);
442            notifier.fireTestIgnored(describeChild(method));
443            return;
444        } else {
445            processTestMethodAnnotationsFor(method);
446        }
447
448        initializeTestSession();
449        prepareBrowserForTest();
450        additionalBrowserCleanup();
451
452        performRunChild(method, notifier);
453
454        if (failureDetectingStepListener.lastTestFailed() && maxRetries() > 0) {
455            retryAtMost(maxRetries(), new RerunSerenityTest(method, notifier));
456        }
457    }
458
459    private void retryAtMost(int remainingTries,
460                             RerunTest rerunTest) {
461        if (remainingTries <= 0) { return; }
462
463        logger.info(rerunTest.toString() + ": attempt " + (maxRetries() - remainingTries));
464        StepEventBus.getEventBus().cancelPreviousTest();
465        rerunTest.perform();
466
467        if (failureDetectingStepListener.lastTestFailed()) {
468            retryAtMost(remainingTries - 1, rerunTest);
469        } else {
470            StepEventBus.getEventBus().lastTestPassedAfterRetries(remainingTries,
471                                                                  failureDetectingStepListener.getFailureMessages(),failureDetectingStepListener.getTestFailureCause());
472        }
473    }
474
475
476    private void performRunChild(FrameworkMethod method, RunNotifier notifier) {
477        super.runChild(method, notifier);
478    }
479
480    interface RerunTest {
481        void perform();
482    }
483
484    class RerunSerenityTest implements RerunTest {
485        private final FrameworkMethod method;
486        private final RunNotifier notifier;
487
488        RerunSerenityTest(FrameworkMethod method, RunNotifier notifier) {
489            this.method = method;
490            this.notifier = notifier;
491        }
492
493        @Override
494        public void perform() {
495            performRunChild(method, notifier);
496        }
497
498        @Override
499        public String toString() {
500            return "Retrying " + method.getDeclaringClass() + " " + method.getMethod().getName();
501        }
502    }
503
504    private void clearMetadataIfRequired() {
505        if (theTest.shouldClearMetadata()) {
506            Serenity.getCurrentSession().clearMetaData();
507        }
508    }
509
510    private void resetStepLibrariesIfRequired() {
511        if (theTest.shouldResetStepLibraries()) {
512            stepFactory.reset();
513        }
514    }
515
516    protected void additionalBrowserCleanup() {
517        // Template method. Override this to do additional cleanup e.g. killing IE processes.
518    }
519
520    private boolean shouldSkipTest(FrameworkMethod method) {
521        return !tagScanner.shouldRunMethod(getTestClass().getJavaClass(), method.getName());
522    }
523
524    private void markAsPending(FrameworkMethod method) {
525        testStarted(method);
526        StepEventBus.getEventBus().testPending();
527        StepEventBus.getEventBus().testFinished();
528    }
529
530    private Consumer<RunNotifier> markAsManual(FrameworkMethod method) {
531        TestMethodConfiguration theMethod = TestMethodConfiguration.forMethod(method);
532
533        testStarted(method);
534        StepEventBus.getEventBus().testIsManual();
535        StepEventBus.getEventBus().getBaseStepListener().latestTestOutcome().ifPresent(
536                outcome -> outcome.setResult(theMethod.getManualResult())
537        );
538        switch(theMethod.getManualResult()) {
539            case SUCCESS:
540                StepEventBus.getEventBus().testFinished();
541                return (notifier -> notifier.fireTestFinished(Description.EMPTY));
542            case FAILURE:
543                Throwable failure = new ManualTestMarkedAsFailure(theMethod.getManualResultReason());
544                StepEventBus.getEventBus().testFailed(failure);
545                return (notifier -> notifier.fireTestFailure(
546                        new Failure(Description.createTestDescription(method.getDeclaringClass(), method.getName()),failure)));
547            case ERROR:
548            case COMPROMISED:
549            case UNSUCCESSFUL:
550                Throwable error = new ManualTestMarkedAsError(theMethod.getManualResultReason());
551                StepEventBus.getEventBus().testFailed(error);
552                return (notifier -> notifier.fireTestFailure(
553                        new Failure(Description.createTestDescription(method.getDeclaringClass(), method.getName()),error)));
554            case IGNORED:
555                StepEventBus.getEventBus().testIgnored();
556                return (notifier -> notifier.fireTestIgnored(Description.createTestDescription(method.getDeclaringClass(), method.getName())));
557            case SKIPPED:
558                StepEventBus.getEventBus().testSkipped();
559                return (notifier -> notifier.fireTestIgnored(Description.createTestDescription(method.getDeclaringClass(), method.getName())));
560            default:
561                StepEventBus.getEventBus().testPending();
562                return (notifier -> notifier.fireTestIgnored(Description.createTestDescription(method.getDeclaringClass(), method.getName())));
563        }
564    }
565
566    private void testStarted(FrameworkMethod method) {
567        getStepListener().testStarted(Description.createTestDescription(method.getMethod().getDeclaringClass(), testName(method)));
568    }
569
570    /**
571     * Process any Serenity annotations in the test class.
572     * Ignored tests will just be skipped by JUnit - we need to ensure
573     * that they are included in the Serenity reports
574     * If a test method is pending, all the steps should be skipped.
575     */
576    private void processTestMethodAnnotationsFor(FrameworkMethod method) {
577        if (isIgnored(method)) {
578            testStarted(method);
579            StepEventBus.getEventBus().testIgnored();
580            StepEventBus.getEventBus().testFinished();
581        }
582    }
583
584    protected void prepareBrowserForTest() {
585        if (theTest.shouldClearTheBrowserSession()) {
586            WebdriverProxyFactory.clearBrowserSession(getDriver());
587        }
588    }
589
590    /**
591     * Running a unit test, which represents a test scenario.
592     */
593    @Override
594    protected Statement methodInvoker(final FrameworkMethod method, final Object test) {
595
596        if (webtestsAreSupported()) {
597            injectDriverInto(test);
598            initPagesObjectUsing(driverFor(method));
599            injectAnnotatedPagesObjectInto(test);
600            initStepFactoryUsing(getPages());
601        }
602
603        injectScenarioStepsInto(test);
604        injectEnvironmentVariablesInto(test);
605        useStepFactoryForDataDrivenSteps();
606
607        Statement baseStatement = super.methodInvoker(method, test);
608        return new SerenityStatement(baseStatement, stepListener.getBaseStepListener());
609    }
610
611    private void useStepFactoryForDataDrivenSteps() {
612        StepData.setDefaultStepFactory(stepFactory);
613    }
614
615    /**
616     * Instantiate the @Managed-annotated WebDriver instance with current WebDriver.
617     * @param testCase A Serenity-annotated test class
618     */
619    protected void injectDriverInto(final Object testCase) {
620        TestCaseAnnotations.forTestCase(testCase).injectDrivers(ThucydidesWebDriverSupport.getDriver(),
621                                                                ThucydidesWebDriverSupport.getWebdriverManager());
622        dependencyInjector.injectDependenciesInto(testCase);
623    }
624
625    protected WebDriver driverFor(final FrameworkMethod method) {
626        if (TestMethodAnnotations.forTest(method).isDriverSpecified()) {
627            String testSpecificDriver = TestMethodAnnotations.forTest(method).specifiedDriver();
628            return getDriver(testSpecificDriver);
629        } else {
630            return getDriver();
631        }
632    }
633
634    /**
635     * Instantiates the @ManagedPages-annotated Pages instance using current WebDriver.
636     * @param testCase A Serenity-annotated test class
637     */
638    protected void injectScenarioStepsInto(final Object testCase) {
639        StepAnnotations.injector().injectScenarioStepsInto(testCase, stepFactory);
640    }
641
642    /**
643     * Instantiates the @ManagedPages-annotated Pages instance using current WebDriver.
644     * @param testCase A Serenity-annotated test class
645         */
646    protected void injectAnnotatedPagesObjectInto(final Object testCase) {
647        StepAnnotations.injector().injectAnnotatedPagesObjectInto(testCase, pages);
648    }
649
650    protected void injectEnvironmentVariablesInto(final Object testCase) {
651        EnvironmentDependencyInjector environmentDependencyInjector = new EnvironmentDependencyInjector();
652        environmentDependencyInjector.injectDependenciesInto(testCase);
653    }
654
655    protected WebDriver getDriver() {
656        return (isEmpty(requestedDriver)) ? ThucydidesWebDriverSupport.getWebdriverManager().getWebdriver()
657                : ThucydidesWebDriverSupport.getWebdriverManager().getWebdriver(requestedDriver);
658    }
659
660    protected WebDriver getDriver(final String driver) {
661        return (isEmpty(driver)) ? ThucydidesWebDriverSupport.getWebdriverManager().getWebdriver()
662                                 : ThucydidesWebDriverSupport.getWebdriverManager().getWebdriver(driver);
663    }
664
665    /**
666     * Find the current set of test outcomes produced by the test execution.
667     * @return the current list of test outcomes
668     */
669    public List<TestOutcome> getTestOutcomes() {
670        return getStepListener().getTestOutcomes();
671    }
672
673    /**
674     *  @return The default reporters applicable for standard test runs.
675     */
676    protected Collection<AcceptanceTestReporter> getDefaultReporters() {
677        return ReportService.getDefaultReporters();
678    }
679}
680
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Trigger additionalBrowserCleanup code on LambdaTest Cloud Grid

Execute automation tests with additionalBrowserCleanup on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)