Home > Blockchain >  TestNG - how to run the same method at the end of each test
TestNG - how to run the same method at the end of each test

Time:12-21

I have something like:

@Test(priority = 1)
public void test1() {
    testSomething1();
    Assert.assertFalse(errorsExists());
}
    
@Test(priority = 2)
public void test2() {
    testSomething2();
    Assert.assertFalse(errorsExists());
}
    
@Test(priority = 3)
public void test3() {
    testSomething3();
    Assert.assertFalse(errorsExists());
}

and I would like to move Assert.assertFalse(errorsExists()) to BaseTestCase or to TestListener so I will not have to pass it every time at the end of the test. I tried to move it to TestsListener to onFinish but method errorsExists() requires driver and I have problems to get it in there.

Update: I want to method errorsExists() influence test result. Lets say that in test2 method errorsExists return true -> I want to have following results: test1 passed test2 failed test3 passed

so as far as I know I cannot put this method to any @After annotations and I cannot put it to onTestFailure or onTestSuccess in TestListener

CodePudding user response:

You may use the IHookable interface to achieve this. This is usually (according to the documentation), used to do some operations before the test start. But it works fine for operations at the end of each test as well.

Create a BaseTest which implements this interface and let your test classes extend the BaseTest.

public class BaseTest implements IHookable {

    @Override
    public void run(IHookCallBack cb, ITestResult testResult) {
        cb.runTestMethod(testResult); // invokes the actual test case
        Assert.assertFalse(errorsExists());
    }
}

CodePudding user response:

You should be able to move it to a new method annotated with @AfterMethod. Any method annotated with @AfterMethod will be executed after each test method is executed. Something like this should work:

@Test(priority = 1)
public void test1() {
  testSomething1();
  //Assert.assertFalse(errorsExists());
}

@Test(priority = 2)
public void test2() {
  testSomething2;
  //Assert.assertFalse(errorsExists());
}

@Test(priority = 3)
public void test3() {
  testSomething3();
  //Assert.assertFalse(errorsExists());
}

@AfterMethod
public void assertError() {
  Assert.assertFalse(errorsExists());
}

CodePudding user response:

If you are running each of your @Test methods in parallel, then you can achieve this in the following way. But the pre-requisite is, each @Test should have its own WebDriver instance. You can use ThreadLocal variable to achieve this. Eg:

public class Driver {

    public static ThreadLocal<AppiumDriver> driver = new ThreadLocal<>();
  
    public static AppiumDriver getDriver() {
        return driver.get();
    }

public synchronized void initalizeDriver(String deviceName, String platformVersion, String port, String udid) {
        if (deviceName.contains("iPhone")) {
            setIOSCapabilities(driver, deviceName, platformVersion, port, udid);
        } else if (deviceName.equalsIgnoreCase("pcloudy-apple")) {
            setPcloudyIOSCapabilties(driver,platformVersion);
        } else {
            if (deviceName.contains("R-")) {
                setAndroidCapabilities(driver, deviceName, platformVersion, port, udid);
            } else if (deviceName.equalsIgnoreCase("pcloudy-Android")) {
                setPcloudyAndroidCapabilties(driver,platformVersion);
            } else {
                startAndroidEmulator(deviceName, udid);
                setAndroidCapabilities(driver, deviceName, platformVersion, port, udid);
            }
        }
    }
}

public void setAndroidCapabilities(ThreadLocal<AppiumDriver> driver, String device, String platformVersion, String port, String udid) {

        try {
            File sourceDir = new File("src");
            File app = new File(sourceDir, ANDROID_APP_PATH);

            DesiredCapabilities desiredCapabilties = new DesiredCapabilities();
            desiredCapabilties.setCapability(MobileCapabilityType.DEVICE_NAME, device);
            
            desiredCapabilties.setCapability(AndroidMobileCapabilityType.IGNORE_UNIMPORTANT_VIEWS,true);
            driver.set(new AndroidDriver<>(new URL(APPIUM_DEFAULT_URL), desiredCapabilties));
        } catch (Exception e) {
            log.error(e.getMessage());
            log.error("Exception in launching the Android Driver "   e);
            e.printStackTrace();
        }
    }

Method inside the base class to start a new driver instance for each @Test method

@BeforeMethod(alwaysRun = true)
@Parameters(value={"deviceName","platformVersion","port","udid"})
public synchronized void executeBeforeTest(String deviceName, String platformVersion,
                                               @Optional("Port Required") String port, @Optional("UDID Required") String udid){
        Driver driverObj = new Driver();
        Thread.currentThread().setName(deviceName);
        driverObj.initalizeDriver(deviceName,platformVersion,port,udid);
        if(Driver.getDriver() == null) {
            driverObj.initalizeDriver(deviceName,platformVersion,port,udid);
        }
    }

Finally the TestListener class you are looking for,

public class TestListener extends UITest implements ITestListener {
@Override
    public void onTestFailure(ITestResult iTestResult) {
        log.info("Driver getDriver() value is :: " Driver.getDriver().toString());
        log.info("Driver getSessionId() value is :: " Driver.getDriver().getSessionId().toString());
            if (Driver.getDriver() != null || Driver.getDriver().getSessionId() != null) {
                try {
                    if (Driver.getDriver() instanceof IOSDriver) {
                        JavascriptExecutor js = (JavascriptExecutor) Driver.getDriver();
                        Map<String, Object> params = new HashMap<>();
                        params.put("bundleId", DeviceCapabalities.IOS_APP_BUNDLE_ID);
                        final Long state = (Long) js.executeScript("mobile: queryAppState", params);
                        System.out.println("Application state code is :"   state);
                        if (state == 1) {
                            log.error("Application has Crashed");
                        }
                    } else {
                        if (!(((AndroidDriver) (Driver.getDriver())).currentActivity().contains("app.name"))) {
                            log.error("Application has Crashed");
                        }
                    }
                    saveScreenshot(Driver.getDriver(),iTestResult);
                } catch (Exception e) {
                    log.error("Exception :: "   e);
                    e.printStackTrace();
                    Assert.fail("Test case failed due to exception "   e);
                }
            }
    }

CodePudding user response:

You can invoke some code and modify test results using IInvokedMethodListener.

Note, that this is a very simple implementation just based on your code example. It may need some improvements.

Test Class:

@Listeners(ErrorsExistsListener.class)
class MyTest {

    @Test(priority = 1)
    public void test1() {
        testSomething1();
    }

    @Test(priority = 2)
    public void test2() {
        testSomething2;
    }

    @Test(priority = 3)
    public void test3() {
        testSomething3();
    }

    boolean errorsExists() {
        // some logic
    }
    

}

Test Listener:

class ErrorsExistsListener implements IInvokedMethodListener {

    @Override
    void afterInvocation(IInvokedMethod method, ITestResult result) {
        if (method.isTestMethod() && result.getStatus() != ITestResult.FAILURE) {
            Object[] instances = result.getTestClass().getInstances(false);
            MyTest myTestClass = (MyTest) instances[0]; // you'll get exception here if set this listener for non-MyTest class.
            try {
                Assert.assertFalse(myTestClass.errorsExists());
            } catch (AssertionError e) {
                result.setStatus(ITestResult.FAILURE);
                result.setThrowable(e);
            }
        }
    }
}
  • Related