Cucumber & TestNG - создает отчет только для первого класса - PullRequest
0 голосов
/ 26 апреля 2019

Я изо всех сил пытаюсь выполнить мой тест на огурец параллельно и получить отчеты для них обоих.

У меня есть класс одного бегуна (BrowserRunner), который получает "браузер" из testNG xml. В этом классе я делаю несколько неприятных вуду, чтобы изменить CucumberOptions во время выполнения и установить различные пути вывода для отчета Cucumber. Все работает хорошо, если работает по порядку один за другим, однако, если он выполняется параллельно, тот, кто вызывает tearDownClass() как 2-й, 3-й и т. Д., Получает исключение нулевого указателя:

java.lang.NullPointerException
    at cucumber.runtime.formatter.TestNGFormatter.handleTestCaseStarted(TestNGFormatter.java:132)
    at cucumber.runtime.formatter.TestNGFormatter.access$100(TestNGFormatter.java:43)
    at cucumber.runtime.formatter.TestNGFormatter$2.receive(TestNGFormatter.java:64)
    at cucumber.runtime.formatter.TestNGFormatter$2.receive(TestNGFormatter.java:61)
    at cucumber.runner.AbstractEventPublisher.send(AbstractEventPublisher.java:45)
    at cucumber.runner.AbstractEventPublisher.sendAll(AbstractEventPublisher.java:52)
    at cucumber.runner.CanonicalOrderEventPublisher.handle(CanonicalOrderEventPublisher.java:18)
    at cucumber.runtime.formatter.Plugins$1.receive(Plugins.java:55)
    at cucumber.runner.AbstractEventPublisher.send(AbstractEventPublisher.java:38)
    at cucumber.runner.AbstractEventBus.send(AbstractEventBus.java:9)
    at cucumber.runner.TimeServiceEventBus.send(TimeServiceEventBus.java:3)
    at cucumber.api.testng.TestNGCucumberRunner.finish(TestNGCucumberRunner.java:77)
    at BrowserRunner.tearDownClass(BrowserRunner.java:150)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:124)
    at org.testng.internal.MethodInvocationHelper.invokeMethodConsideringTimeout(MethodInvocationHelper.java:59)
    at org.testng.internal.Invoker.invokeConfigurationMethod(Invoker.java:458)
    at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:222)
    at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:142)
    at org.testng.internal.TestMethodWorker.invokeAfterClassMethods(TestMethodWorker.java:214)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
    at org.testng.TestRunner.privateRun(TestRunner.java:648)
    at org.testng.TestRunner.run(TestRunner.java:505)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:455)
    at org.testng.SuiteRunner.access$000(SuiteRunner.java:40)
    at org.testng.SuiteRunner$SuiteWorker.run(SuiteRunner.java:489)
    at org.testng.internal.thread.ThreadUtil$1.call(ThreadUtil.java:52)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Основная идея заключается в том, что у меня есть набор тестов, и я хочу запускать их во многих браузерах. Может быть, может быть лучшее решение? Однако я бы хотел придерживаться Cucumber, поскольку мне нравится, как пишутся тесты, и как легко их можно читать.

tests.xml:

<suite name="Paratest" verbose="1" thread-count="2" parallel="tests" configfailurepolicy="continue">
    <listeners>
        <listener class-name="org.uncommons.reportng.HTMLReporter" />
        <listener class-name="org.uncommons.reportng.JUnitXMLReporter" />
    </listeners>
    <parameter name="environment" value="TEST"></parameter>

    <test name="Chrome">
        <parameter name="browser" value="chrome" />
        <classes>
            <class name="BrowserRunner">
                <methods>
                    <include name="scenario"/>
                </methods>
            </class>
        </classes>
    </test>
    <test name="Firefox">
        <parameter name="browser" value="firefox" />
        <classes>
            <class name="BrowserRunner">
                <methods>
                    <include name="scenario"/>
                </methods>
            </class>
        </classes>
    </test>

</suite>

Класс BrowserRunner:

@CucumberOptions(
        plugin = {"html:target/cucumber-html-report-IDENTIFICATION",
                "json:target/cucumber-IDENTIFICATION.json",
                "testng:target/testng-IDENTIFICATION.txt"},
        strict = true,
        features = {"src/test/resources/Login.feature"}
)
public class BrowserRunner {
    private TestNGCucumberRunner testNGCucumberRunner;

    private static final String ANNOTATIONS = "annotations";
    public static final String ANNOTATION_DATA = "annotationData";
    private String browser;

    @BeforeSuite
    public void beforeSuite() {
        System.out.println("BeforeSuite");
    }

    @Parameters("browser")
    @BeforeClass(alwaysRun = true)
    public void setUpClass(String browser) throws Exception {

        this.browser = browser;
        System.out.println("Debug1. Thread.currentThread(): " + Thread.currentThread().getId() + " " + browser);
        Class clazzToBeModified = BrowserRunner.class;

        Method method = Class.class.getDeclaredMethod(ANNOTATION_DATA, null);
        method.setAccessible(true);
        //Since AnnotationData is a private class we cannot create a direct reference to it. We will have to
        //manage with just Object
        Object annotationData = method.invoke(clazzToBeModified);
        //We now look for the map called "annotations" within AnnotationData object.
        Field annotations = annotationData.getClass().getDeclaredField(ANNOTATIONS);
        annotations.setAccessible(true);
        Map<Class<? extends Annotation>, Annotation> map =
                (Map<Class<? extends Annotation>, Annotation>) annotations.get(annotationData);

        Annotation toBeModified = map.get(CucumberOptions.class);

        CucumberOptions newAnno = new CucumberOptions() {

            @Override
            public Class<? extends Annotation> annotationType() {
                return null;
            }

            @Override
            public boolean dryRun() {
                return false;
            }

            @Override
            public boolean strict() {
                return false;
            }

            @Override
            public String[] features() {
                return new String[]{"src/test/resources/Login.feature"};
            }

            @Override
            public String[] glue() {
                return new String[0];
            }

            @Override
            public String[] extraGlue() {
                return new String[]{"src/test/java/Hooks.java"};
            }

            @Override
            public String[] tags() {
                return new String[]{"@smoke"};
            }

            @Override
            public String[] plugin() {
                return new String[]{"html:target/cucumber-html-report-" + browser,
                        "json:target/cucumber-" + browser + ".json",
                        "testng:target/testng-" + browser + ".txt"};
            }

            @Override
            public boolean monochrome() {
                return false;
            }

            @Override
            public String[] name() {
                return new String[0];
            }

            @Override
            public SnippetType snippets() {
                return SnippetType.UNDERSCORE;
            }

            @Override
            public String[] junit() {
                return new String[0];
            }
        };

        map.put(CucumberOptions.class, newAnno);


        testNGCucumberRunner = new TestNGCucumberRunner(this.getClass());

    }

    @Test(groups = "Cucumber", description = "Runs Cucumber Feature", dataProvider = "scenarios")
    public void scenario(PickleEventWrapper pickleEvent, CucumberFeatureWrapper cucumberFeature) throws Throwable {

        System.out.println("Debug2. Thread.currentThread(): " + Thread.currentThread().getId() + " " + browser);
        testNGCucumberRunner.runScenario(pickleEvent.getPickleEvent());
    }

    @DataProvider
    public Object[][] scenarios() {
        return testNGCucumberRunner.provideScenarios();
    }

    @AfterClass(alwaysRun = true)
    public void tearDownClass() throws Exception {
        System.out.println("Debug3. Thread.currentThread(): " + Thread.currentThread().getId() + " " + browser);
        if (testNGCucumberRunner != null) {
            testNGCucumberRunner.finish();
        }
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...