Automation tests for iOS app using Appium (Part 2)

Automation tests for iOS app using Appium (Part 2)

Introduction

In the previous post I have talked about the Appium requirements for running automation tests on iOS, project structure, and the configuration. In this post I’m going to spend more time talking about the code structure, initializing the driver, common issues that every newcomer may occur, and how to solve them. If you haven’t downloaded the code for the iOS Appium demo, you can download the code through the following link, and by going through the code you can easily understand the code segments.

Some code

First segment of the code that I’m going to discuss is the DriverHelper class, which is responsible for initializing and destroying the iOS driver. In this class there are two important annotated methods:

  • initDriver(), which is annotated with @Before annotation.
  • destroyDriver(), which is annotated with @After annotation.

In my case initDriver() method is used for initializing the driver depending on the configuration parameters in config_app.properties which I have discussed in the previous post. The purpose of the @Before annotation is used to mark the method to be executed before executing every scenario.

@Before
public void initDriver() throws MalformedURLException {
    appConfig = new AppConfig();
    capabilities = new DesiredCapabilities();
    ...
    capabilities.setCapability("noReset", true);
    capabilities.setCapability(MobileCapabilityType.UDID, appConfig.getProperty(AppConfig.PROPERTY_DEVICE_UDID));
    capabilities.setCapability(MobileCapabilityType.TAKES_SCREENSHOT, "true");
    serverUrl = new URL("http://localhost:" + appConfig.getProperty(AppConfig.PROPERTY_APPIUM_SERVER_PORT) + "/wd/hub");
    driver = new IOSDriver(serverUrl, capabilities);
}

Alright… So if the driver is initialized before each scenario execution, then we need to destroy the driver once the scenario has finished. That’s why for this case we are using the method destroyDriver() annotated with @After annotation. This annotation is used to mark the method to be executed after each scenario. In this method I have added the method for destroying the iOS driver.

@After
public void destroyDriver(){
    driver.quit();
}

There are also other cases that you can benefit from the method which is marked with the @After annotation. For example lets say you are executing some scenario, and for some reason the scenario is crashing. Most of you would say, alright that’s not a problem I can check the error log. That’s good thinking, but in most cases this is not going to be helpful. The reason for this is that you may be looking for element that is not visible due to some action in some previous step, and this is not a good thing because you are already looking in the wrong direction.

One picture is thousand words, so why spent lot of hours reading stack traces, re-running scenario, and wondering why it is not working. Probably you have noticed the method takeScreenshot(), this method is used for taking a screenshot from the current state of the device once the method is invoked.

But be careful because you don’t want to take screenshot from each scenario whether has passed or failed. You want to take screenshot only from the scenario that has failed. For this case you should use the Scenario class by invoking isFailed() method. Here is the final version of destroyDriver() method.

@After
public void destroyDriver(Scenario scenario){
    if(scenario.isFailed()){
        takeScreenshot(driver);
    }

    driver.quit();
}

What’s different and why?

If you look close in the driver initialization driver method you are going to notice that there is capability noReset added, and the driver is initialized with the IOSDriver class. The IOS driver is used to start the automation tests in the simulator / device UDID. More interesting is noReset capability, if you are curios start the automation suite without this capability.

When you start the suite, this is going to work perfectly fine for one scenario, but you are going to encounter into an issue if you want to run multiple scenarios. After each scenario the simulator is going to be reset i.e. the simulator is going to be shutdown, and new instance of the simulator is going to be created. This is pretty much boring when you have to run 50 scenario, and not to imagine 100s.

The other difference between Android and iOS is in the element location. I’m not saying that it is used different technology, but you are going to encounter a situation where the Appium driver is going to wait for long time before the element is located. In this may look really strange when running the scenario in non headless mode, and seeing that the element is there and visible, but the driver is struggling to find the element.

If you encounter this situation there is workaround for this case, it is not the perfect solution, but it is going to do finish the job until the driver behavior is improved. In my case I have resolved this by using static Thread.sleep(), and this speeds the location of the element.

Conclusion

Here I’m going to finish the post regarding the iOS driver initialization, code differences, and some tips and tricks that are going to save you a lot of headaches. In the iOS posts I’m not going to cover the relation between steps, screens, and feature files. The logic for the iOS project is the same as for the Android project, and if you are interested to check the relation read my previous post. I will see you in the last iOS part of these series 😉

 

Leave a Reply

Your email address will not be published. Required fields are marked *