Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: same behavior for firstTimeOnly and lastTimeOnly in @Before/AfterMethod even when invocationCount is 1 or data provider test driven parallel or not #1711

Open
7 tasks
ghenadied opened this issue Mar 2, 2018 · 3 comments

Comments

@ghenadied
Copy link

ghenadied commented Mar 2, 2018

TestNG Version

Note: only the latest version is supported

Expected behavior

It would be really useful if firstTimeOnly and lastTimeOnly would be enhanced to work with parallel dataproviders in the same way they work for invocationCount greater than 1. So the result would be to run BeforeMethod/AfterMethod exactly once for parallel enabled data provider, before the first run/after the last run of the test method using that data provider. It makes more sense in the context of parallel data providers than on sequential ones. But it may be enhanced to have effect regardless of parallel state of data provider. Also, it should behave well also when test class is instantiated with factory multiple times and group-by-instances set to true.

Actual behavior

Is the issue reproductible on runner?

  • Shell
  • Maven
  • Gradle
  • Ant
  • Eclipse
  • IntelliJ
  • NetBeans

Test case sample

Please, share the test case (as small as possible) which shows the issue

@juherr
Copy link
Member

juherr commented Mar 2, 2018

Could you share some sample for each use case you imagine? They will be functional tests when we will implement it.

@ghenadied
Copy link
Author

ghenadied commented Mar 6, 2018

Hi,

Here it is:

  public class MySecondTestClass {

    @BeforeMethod(firstTimeOnly = true)
    public void beforeMethod() {
        System.out.println("do nothing in beforeMethod");
        System.out.println("  Thread in beforeMethod: " + Thread.currentThread().getId());
    }

    @AfterMethod(lastTimeOnly = true)
    public void afterMethod() {
        System.out.println("do nothing in afterMethod");
        System.out.println("  Thread in afterMethod: " + Thread.currentThread().getId());
    }

    @Test
    public void testSecondMethod1() {
        System.out.println("    Inside test method testSecond from second class");
        System.out.println("Thread in testSecondMethod1: " + Thread.currentThread().getId());
        assert true;
    }

    @Test(groups = "broken")
    public void testSecondMethod2() {
        System.out.println("    Thread in testSecondMethod2: " + Thread.currentThread().getId());
        fail("    inside testSecondMethod2 fail");
    }

    /**
     * test with parameter
     * @param param
     */
    @Test(dataProvider = "dp3")
    public void testSecondMethod3(String param) {
        System.out.println("    testSecondMethod3 param: " + param);
        System.out.println("    Thread in testSecondMethod3: " + Thread.currentThread().getId());
        assert true;
    }

    /**
     * data provider
     */
    @DataProvider(name = "dp3", parallel = true)
    public Object[][] getDP() {
        return new Object[][] {
                {
                        "T"
                },
                {
                        "F"
                }
        };
    }
  }

Output:

  MySecondTestClass.beforeMethod()[pri:0, instance:tests.MySecondTestClass@1209079] 18911353
    MySecondTestClass.testSecondMethod1()[pri:0, instance:tests.MySecondTestClass@1209079] 18911353
  MySecondTestClass.afterMethod()[pri:0, instance:tests.MySecondTestClass@1209079] 18911353

  MySecondTestClass.beforeMethod()[pri:0, instance:tests.MySecondTestClass@1209079] 18911353
    MySecondTestClass.testSecondMethod2()[pri:0, instance:tests.MySecondTestClass@1209079] 18911353
  MySecondTestClass.afterMethod()[pri:0, instance:tests.MySecondTestClass@1209079] 18911353


  MySecondTestClass.beforeMethod()[pri:0, instance:tests.MySecondTestClass@1209079] 18911353
  MySecondTestClass.beforeMethod()[pri:0, instance:tests.MySecondTestClass@1209079] 18911353

    MySecondTestClass.testSecondMethod3(java.lang.String)[pri:0, instance:tests.MySecondTestClass@1209079]F  18911353
    MySecondTestClass.testSecondMethod3(java.lang.String)[pri:0, instance:tests.MySecondTestClass@1209079]T  18911353

  MySecondTestClass.afterMethod()[pri:0, instance:tests.MySecondTestClass@1209079] 18911353
  MySecondTestClass.afterMethod()[pri:0, instance:tests.MySecondTestClass@1209079] 18911353

Having firstTimeOnly = true in BeforeMethod and lastTimeOnly = true in AfterMethod would have expected effect that these configuration methods will be run once for testSecondmethod3. It should work also for non-parallel data provider and regardless of invocationCount.

It is quite interesting that it works well (6.11) on non-parallel data provider:

MySecondTestClass.beforeMethod()[pri:0, instance:tests.MySecondTestClass@1df28bb] 31402171
    MySecondTestClass.testSecondMethod1()[pri:0, instance:tests.MySecondTestClass@1df28bb] 31402171
  MySecondTestClass.afterMethod()[pri:0, instance:tests.MySecondTestClass@1df28bb] 31402171

  MySecondTestClass.beforeMethod()[pri:0, instance:tests.MySecondTestClass@1df28bb] 31402171
    MySecondTestClass.testSecondMethod2()[pri:0, instance:tests.MySecondTestClass@1df28bb] 31402171
  MySecondTestClass.afterMethod()[pri:0, instance:tests.MySecondTestClass@1df28bb] 31402171


  MySecondTestClass.beforeMethod()[pri:0, instance:tests.MySecondTestClass@1df28bb] 31402171

    MySecondTestClass.testSecondMethod3(java.lang.String)[pri:0, instance:tests.MySecondTestClass@1df28bb]T  31402171
    MySecondTestClass.testSecondMethod3(java.lang.String)[pri:0, instance:tests.MySecondTestClass@1df28bb]F  31402171

  MySecondTestClass.afterMethod()[pri:0, instance:tests.MySecondTestClass@1df28bb] 31402171

@ghenadied
Copy link
Author

ghenadied commented Mar 6, 2018

Hi,

Imagine having a test class with a dozen of test methods, each iterating multiple times based on data providers and a BeforeMethod, all this leading to a number of 10k+ iterations. So far it works well and quite quickly when run sequentially considering each test makes some actions (stimulus) and then queries some data from data containers (response) with timeout (few seconds). In cases there are some issues, from few minutes this test class can take hours because of queries waiting for valid data until timeout expires. BeforeMethod is used to clear data containers before each iteration so the queries are not looking into a lot of unnecessary data from previous tests. Logging would be also affected in this case of failures as it will log the data queries did not match on (huge amount of data for each iteration, if keeping all data from containers) for debug purposes. Making parallel data providers will solve a lot of time in such cases but this implies removing config method BeforeMethod since it may clear data container (a single provider) another thread is querying on.
Maybe it's more clear now.

Thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants