1

I'm using TestNG plugin for Eclipse, and I'm trying to keep the test running independent of the result. I tried with invocation count but it's only for a set number, I used IAnnotationTransformer to change it, but it is also invoked only once (the transformer).

    @Override
    public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
    // TODO Auto-generated method stub
    IAnnotationTransformer.super.transform(annotation, testClass, testConstructor, testMethod);
    System.out.println("Transform started");
    annotation.setInvocationCount(2);
    }

So after this it runs two times, but I need the test to run unknown number of times dependent on a calculation. I know I can calculate it and use it as a constant value, but I would like to keep incrementing the invocation count as long as the timer is not up.

I have also tried it with IRetryAnalyer but that is only for failed cases, and making the test appear as failed is too hacky for me.

I tried using DataProvider but it seems it is also invoked only once, where my idea was for the data provider to check the timer and return an array with an extra empty param.

Another idea was to implement increment logic in afterInvocation but that also failed :(

    @Override
    public void afterInvocation(IInvokedMethod method, ITestResult testResult, ITestContext context) {
    // TODO Auto-generated method stub
    IInvokedMethodListener.super.afterInvocation(method, testResult, context);
    method.getTestMethod().setInvocationCount(getCount() + 1);
    }

So, I'm out of options. The end goal is to re-run the test (pass or fail) for let's say 24 hours, the invocation number is unknown since a single test can last from 45 minutes up to 3 hours.

EDIT 1: Retry logic in TestInvoker.class ===> handleInvocationResults()

    boolean willRetry =
        retryAnalyzer != null
            && status == ITestResult.FAILURE
            && failure.instances != null
            && retryAnalyzer.retry(testResult);

I managed to get a part of desired behavior by implementing my listener. As you can see from the beforeInvocation I kill the runtime, which results in teardown methods and procedures not to get called.

I managed another solution with @beforeMethod but that way the remaining invocations are just skipped which is also out of the question since there is no need in invoking them after the timer is out.

public class CoreListener implements IAnnotationTransformer, IInvokedMethodListener, ITestListener {

    // The invocation count has to be higher than is realistically possible to invoke within a week
    private final int unrealisticInvocationCount = 1000;

    private static final long durationTime = 3000;
    private static long startTime = 0;

    public CoreListener() {
        CoreListener.startTime = System.currentTimeMillis();
    }

    @Override
    public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
        IAnnotationTransformer.super.transform(annotation, testClass, testConstructor, testMethod);
        System.err.println("Transformer invoked from CoreListener");
        annotation.setInvocationCount(unrealisticInvocationCount);

    }

    @Override
    public void beforeInvocation(IInvokedMethod method, ITestResult testResult, ITestContext context) {
        if (method.getTestMethod().isTest()) {
            if (!isAllowedToRun()) {
                Runtime.getRuntime().exit(0);
            }
            System.err.println("beforeInvocation invoked from StabilityTimingListener");
            System.out.println("Is allowed to run: " + isAllowedToRun());
        }
    }

    @Override
    public void afterInvocation(IInvokedMethod method, ITestResult testResult, ITestContext context) {
        System.err.println("After invocation invoked from StabilityTimingListener");
    }

    public boolean isAllowedToRun() {
        if (System.currentTimeMillis() > startTime + durationTime) {
            return false;
        }
        return true;
    }

}
smamusa
  • 31
  • 7
  • How or where is transform called from? You could set a timer in the code that calls it – aran Feb 07 '21 at 19:36
  • transform is called from a listener class included in the test suite XML file – smamusa Feb 07 '21 at 19:39
  • Of course, but the problem is re-running the test. I'm not aware of any mechanic (method) to run the again. The only method I'm aware of is run(IHookCallback callBack, ITestResult testResult) – smamusa Feb 07 '21 at 19:46
  • hmmm I see.. Would it be possible to implement the timer inside the transform method? I mean, reusing the same arguments `ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod` again and again? – aran Feb 07 '21 at 19:48
  • I could implement the timer, or any other calculation logic, but the thing is this transform method is invoked only once before the test starts, so the invocation count appears to be unchangeable once the first test starts. – smamusa Feb 07 '21 at 19:57
  • Take a look here -- seems to be the way to specify the invocation count. May this help https://stackoverflow.com/questions/26128289/testng-how-can-i-run-same-test-case-multiple-times – aran Feb 07 '21 at 19:59
  • I'll check it out, but these all seem to calculate a number of repetitions and then provide it. They don't seem to change dynamically during the run, although I'll have to test it first. – smamusa Feb 07 '21 at 20:52
  • No luck with this either, seem the only way to actually implement is to rewrite TestNG logic responsible for handling the invocations. – smamusa Feb 08 '21 at 19:52
  • I will try to post something now, wait...hope it helps – aran Feb 08 '21 at 21:12
  • Hey, I cannot guarantee a proper result but...could you try below's answer? – aran Feb 08 '21 at 22:39
  • I added a comment explaining why I couldn't use the suggestion but the comment was removed. – smamusa Feb 09 '21 at 15:28

0 Answers0