-
Notifications
You must be signed in to change notification settings - Fork 1k
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
BeforeMethod not as expected when skip a test (Data Provider usage) #1991
Comments
The latest released version of TestNG is |
package co.testng.bug;
import org.testng.SkipException;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import junit.framework.AssertionFailedError;
public class DemoBug {
@BeforeMethod(alwaysRun=true)
public void setup(Object[] o) {
String[] s = (String[])o[0];
System.out.println("printed from setup:"+s[0]);
if(s[0].equals("Billy")) throw new SkipException("doesnt matter");
}
@Test(dataProvider="dataIssuer")
public void test(String[] arg) {
System.out.println("printed from test:"+arg[0]);
}
@AfterMethod(alwaysRun=true)
public void teardown(){
System.out.println("---teardown---");
}
@DataProvider(name="dataIssuer")
public Object[][] issueData(){
String[][] data = {{"John"},{"Billy"},{"Jimmy"}};
return data;
}
} |
that's the simplest code that shows the issue |
TestNG is skiping the third running of the test as well |
The javadocs for Not quite sure I understand what is the expectation here. |
Is there a chance that you have misinterpreted the functionality of It's used to tell TestNG that "always run a particular configuration method irrespective of what group I chose to run". |
doesn't matter. the fact is that the test methods are not running after a SkipExeption in @BeforeMethod and this is not a desired behavior since the test should be skipped only for that specific data set and the check is performed in the before method. |
It does matter. You are explicitly causing a test to be skipped by throwing an exception from its configuration method. Data driven tests is not "n" tests. It's just one test running with "n" sets of data. So I don't see why TestNG should try and execute the next iteration when a config method failed for a previous iteration. AFAIK TestNG is working as designed here. You can try setting |
no... still not running. output is here: |
it skips the third execution of the test as well |
Then in that case, TestNG is working just as I had explained. You cannot have TestNG retry the next iteration of a data driven test method even though the previous iteration was skipped due to a configuration failure. |
hmmm... the configuration failure refers at a specific configuration per method. it could fail for one set of data but not for all. in this case it loses the power of data driven framework since each test execution could be a separate test case as in this case. |
yes, it's working as you explained but not as it's useful for a test framework. everything has an explanation but not every behavior is a desired one! |
@docian - Thats one way of looking at it. I dont think I believe what you want here is an import org.testng.IHookCallBack;
import org.testng.IHookable;
import org.testng.ITestResult;
import org.testng.SkipException;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DemoBug implements IHookable {
@Test(dataProvider = "dataIssuer")
public void test(String arg) {
System.out.println("printed from test:" + arg);
}
@AfterMethod(alwaysRun = true)
public void teardown() {
System.out.println("---teardown---");
}
@DataProvider(name = "dataIssuer")
public Object[][] issueData() {
return new Object[][] {{"John"}, {"Billy"}, {"Jimmy"}};
}
@Override
public void run(IHookCallBack callBack, ITestResult testResult) {
Object[] parameters = callBack.getParameters();
if (parameters[0].toString().equalsIgnoreCase("Billy")) {
testResult.setThrowable(new SkipException("Causing skip for " + parameters[0]));
testResult.setStatus(ITestResult.SKIP);
} else {
callBack.runTestMethod(testResult);
}
}
} Here's the output (I am using TestNG
|
This is a bit hard to explain. TestNG doesn't create multiple instance of the class DemoBug for each parameter group. It just run the test method by passing different parameter group with the same instance of the class DemoBug. However, it will call @BeforeMethod for each parameter group. And further more, as long as one calling of @BeforeMethod is failed, then it will mark that the configuration calling is failed and the execution of the test method (here, it's test(String arg)) is skipped - that's a bit not reasonable and should be fixed. However, it's not related to the attribute alwaysRun=true. |
Do you want to help me understand why you feel its not reasonable and should be fixed? Why do you expect this configuration method to retry every attempt of the same test (Internally how TestNG manages a data driven test is not something which IMO a user needs to be worried about) but for a different parameter ? |
Due to bad comment editor, I create a wiki page for explanation. Please go to https://github.com/cbeust/testng/wiki/Issue1991:-DataProvider-Execution for details. |
It's still not clear for me as to what is the expectations here. Can you please elaborate. On a side note I noticed that you aren't using AFAIK if a |
thank you, it solved the problem. |
Closing this issue based on the OPs inputs. |
BTW, what's the criteria of the tickets closing? As long as the user's problem is solved by any workaround or all problems and confusions are clarified? |
yes, i agree that it is a workaround but I haven't enough time to explain what is obvious. |
@WengM please help submit a test that can be run to simulate the problem ( preferably with assertions instead of print statements). Also please make sure you are using |
If I understand well the issue, a before method run should be linked to the test method run. But at the same time, TestNG, by design, use the same instance for each run and allows users to share a context from one run to others. Maybe a configuration for the behavior could be a good compromise but in my opinion, the current implementation is the best one for the moment. I propose to keep the issue open. |
I've just noticed the issue is talking about skip tests and not failing configuration methods. Maybe the behavior can be different between failing and skip. |
@juherr - IMO there is no issue here, which is what I have tried explaining before :) I am not entirely sure as to why should a |
I think you should make a sharp distinction between "failed" and "skipped" status of a test. That's why I'm saying that a skipped test shouldn't drive to a skipping of the following tests. |
Yes, I agree with that. I don't find a use case where we'd like to skip all methods after the first skipped method. |
The before method is the best place to skip a test when the test has a bug "hanged" on it. I'm integrating the TestNG with DevOps Azure VSTS and at the beginning I'm making such a check. If the test has an Active or New bug "hanged" on it I have to skip the test (it doesn't make sense to execute it). In this way I can push the limits of the QA automation process and to minimize the manual operation. The benefits are multiple. |
If we go back to the example that you shared in you would notice that a configuration method is throwing an exception, which means it's failing. So the test method is getting skipped. Its not the skipped status of a test method that is causing the rest of the iterations to be skipped, but here the rest of the iterations of a test method are being skipped because a configuration failed. A test method can get skipped due to 2 reasons.
So there's a very clear distinction that TestNG is already doing. What you are asking for is that TestNG, tries running the remaining iterations of a test even though a particular iteration was skipped due to a configuration failure. What I was basically trying to say was that TestNG does not retry running failed configuration methods. That has been the basic behavior till now. The question now boils down to : Should TestNG support retrying a failed configuration method The reason I say this is because, if TestNG were to attempt to continue running the next iteration, then it would need to run the Hope that clears out what I have been trying to say. |
And that is why I also felt (just as @juherr calls out in his comment in
Where is this tag coming from? If its from a data provider, then I would look at it as just meta data that helps in making some decision. And that decision needn't be from a configuration method (because you aren't setting up anything per se via your configuration method) but merely are looking for a mechanism to intercept the data with which a test is going to be executed and do something prior to the test method actually consuming that data/meta data. I believe that |
Here's another sample that does the same thing but makes use of a TestNG listener (which is also another way of abstracting out the decision of when to run a test and when to skip it) package com.rationaleemotions.github.issue1991;
import com.rationaleemotions.github.issue1991.Demo2.DecisionMaker;
import org.testng.IInvokedMethod;
import org.testng.IInvokedMethodListener;
import org.testng.ITestResult;
import org.testng.SkipException;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(DecisionMaker.class)
public class Demo2 {
@Test(dataProvider = "dataIssuer")
public void test(String arg) {
System.out.println("printed from test:" + arg);
}
@AfterMethod(alwaysRun = true)
public void teardown() {
System.out.println("---teardown---");
}
@DataProvider(name = "dataIssuer")
public Object[][] issueData() {
return new Object[][] {{"John"}, {"Billy"}, {"Jimmy"}};
}
public static class DecisionMaker implements IInvokedMethodListener {
@Override
public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
if (method.isConfigurationMethod()) {
return;
}
Object[] parameters = testResult.getParameters();
if (parameters[0].toString().equalsIgnoreCase("Billy")) {
testResult.setThrowable(new SkipException("Causing skip for " + parameters[0]));
testResult.setStatus(ITestResult.SKIP);
}
}
}
} And here's the output for the same
Listeners give you a much more control in terms of separating out the concerns, and also gives you the luxury of wire on, wire off (Listeners can be wire in at will using the |
I've tried to show you a valid use case. From my point of view I can find many workarounds but I've tried to find out whether there is a way to fulfill my goals. When I'm throwing a SkipException I'm waiting to find it in the reports as well. That's why I am doing this way. Of course I can build a lot around the TestNG library but this is not the idea. |
Add demo code for forum ticket testng-team/testng#1991 Change-Id: I3f3f28de9f4114171344343878f6649bba17e8aa
Actually, TestNG does run the @BeforeMethod for each iteration no matter in which iteration it's failed due to any reason. I already tested this in both 7.0.0-beta1 and 6.14.3. Of course, it's a waste because the test for the remaining iterations will be skipped due to the failure happened in previous iteration. However, because data can be injected into @BeforeMethod, it could work differently in different iterations for different data group - for some data group, it may fail and for others it may pass. Thus, the failure in one iteration should not impact other iterations. I already summarized in the wiki page:Issue1991:-DataProvider-Execution:
|
@krmahadevan Your workaround is right and listeners are often a recommended solution. |
I've built my test suite for this direction. @BeforeMethod is used to setup objects while the injected object[] contains parameters for test logic. @AfterMethod is responsible to dispose all objects which are instantiated at @BeforeMethod. Though all parameterized tests share the same flow. My team consider them as separate tests. |
I have an appium test suite. I have many tests that have some common steps(interaction with the app). So, I have a BeforeMethod annotated method that contains those common steps. |
hi has this been solved is a latest update TestNG version 7.4.0 i’m having a similar yet simpler issue then i have 2 methods that are both tests @test
public test1(){
} @test
public test2(){
} on run it will throw the skip but then every subsequent function will also throw the skip i would think the expected outcome would pick back up in the when i run a class that uses the listener |
I have revisited this issue using TestNG Setting Test Class usedimport org.testng.ITestResult;
import org.testng.SkipException;
import org.testng.annotations.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DemoBug {
private static final List<String> msgs = new ArrayList<>();
@BeforeMethod(alwaysRun = true)
public void setup(ITestResult result) {
Object[] s = result.getParameters();
String msg = String.format("[BEFORE-METHOD] method: %s(), Parameters: %s",
result.getMethod().getMethodName(),
Arrays.toString(s));
msgs.add(msg);
if (s[0].equals("Billy")) {
throw new SkipException("doesnt matter");
}
}
@Test(dataProvider = "dataIssuer")
public void test(String name) {
String msg = String.format("[TEST-METHOD] method: test(), Parameters: %s", name);
msgs.add(msg);
}
@AfterMethod(alwaysRun = true)
public void teardown(ITestResult result) {
Object[] s = result.getParameters();
String msg = String.format("[AFTER-METHOD] method: %s(), Parameters: %s", result.getMethod().getMethodName(),
Arrays.toString(s));
msgs.add(msg);
}
@DataProvider(name = "dataIssuer")
public Object[][] issueData() {
return new Object[][]{
{"John"},
{"Billy"},
{"Jimmy"}
};
}
@AfterSuite(alwaysRun = true)
public void dumpLogs() {
msgs.forEach(System.out::println);
}
} Suite xml file used<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="2980_suite" verbose="2" configfailurepolicy="continue">
<test name="2980_test">
<classes>
<class name="com.rationaleemotions.github.issue1991.DemoBug"/>
</classes>
</test>
</suite> Execution Output...
... TestNG 7.10.2 by Cédric Beust (cedric@beust.com)
...
Test ignored.
SKIPPED CONFIGURATION: @BeforeMethod com.rationaleemotions.github.issue1991.DemoBug.setup([TestResult name={null} status=CREATED method=DemoBug.test(java.lang.String)[pri:0, instance:com.rationaleemotions.github.issue1991.DemoBug@482cd91f] output={null}])
PASSED: com.rationaleemotions.github.issue1991.DemoBug.test("Jimmy")
PASSED: com.rationaleemotions.github.issue1991.DemoBug.test("John")
SKIPPED: com.rationaleemotions.github.issue1991.DemoBug.test("Billy")
org.testng.SkipException: doesnt matter
at com.rationaleemotions.github.issue1991.DemoBug.setup(DemoBug.java:23)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:141)
at org.testng.internal.invokers.MethodInvocationHelper.invokeMethodConsideringTimeout(MethodInvocationHelper.java:71)
at org.testng.internal.invokers.ConfigInvoker.invokeConfigurationMethod(ConfigInvoker.java:400)
at org.testng.internal.invokers.ConfigInvoker.invokeConfigurations(ConfigInvoker.java:333)
at org.testng.internal.invokers.TestInvoker.runConfigMethods(TestInvoker.java:833)
at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:600)
at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:230)
at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:63)
at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:992)
at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:203)
at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:154)
at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:134)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.testng.TestRunner.privateRun(TestRunner.java:739)
at org.testng.TestRunner.run(TestRunner.java:614)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:421)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:413)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:373)
at org.testng.SuiteRunner.run(SuiteRunner.java:312)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:95)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1274)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1208)
at org.testng.TestNG.runSuites(TestNG.java:1112)
at org.testng.TestNG.run(TestNG.java:1079)
at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:65)
at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:105)
===============================================
2980_test
Tests run: 1, Failures: 0, Skips: 1
Configuration Failures: 0, Skips: 1
===============================================
[BEFORE-METHOD] method: test(), Parameters: [John]
[TEST-METHOD] method: test(), Parameters: John
[AFTER-METHOD] method: test(), Parameters: [John]
[BEFORE-METHOD] method: test(), Parameters: [Billy]
[AFTER-METHOD] method: test(), Parameters: [Billy]
[BEFORE-METHOD] method: test(), Parameters: [Jimmy]
[TEST-METHOD] method: test(), Parameters: Jimmy
[AFTER-METHOD] method: test(), Parameters: [Jimmy]
===============================================
2980_suite
Total tests run: 3, Passes: 2, Failures: 0, Skips: 1
Configuration Failures: 0, Skips: 1
===============================================
Process finished with exit code 0
|
TestNG Version
6.14.3
Expected behavior
If one BeforeMethod skips (and Test skips as well) I'm expecting that for the next cycle (alwaysRun=true) if the Conditional method runs well the test to be performed with a new data row. The test method has an associated DataProvider.
Actual behavior
If one BeforeMethod skips nothing is executed from now on as Tests annotated method(s).
Is the issue reproductible on runner?
Test case sample
The text was updated successfully, but these errors were encountered: