< Back

A guide to mobile testing: Appium and Webdriver.io

Author

Wim Raes and Muskaan Parikh

Date

29/05/2024

Share this article

As you might remember from my very first blog post written a while ago, I stated that I would write another blog post about Appium since it was such an interesting tool J. 

Some of you who landed on this page were googling something like ‘Why doesn’t Appium start?’ or ‘What are capabilities in Appium?’ 

For those of you that did the former, I have good news. And for those of you who did not and were simply curious about my second blog post: Just stick around. I am sure you will learn a thing or two. 

Appium and Webdriver.io 

To start, I will not explain what Appium is as I am sure by now that most of you already know this and because you, obviously, read my first blog post anyway ;-). I will cover some basics, using Appium in conjunction with Webdriver.io, a testing framework that uses JS. 

Start by installing Node.js using the provided link. Run the installer that fits your Operating System. Run the following commands in your terminal/powershell for verification: 

Node –v

Npm –v 

These commands will check the version of your Node.js, if it gives you an error then retry the installation. Make sure to download the installer again as it might’ve gotten corrupted during the initial download. 

You will also need a mobile device emulator. If you are working on a Mac, I suggest you use Xcode (App store) to easily set up an iOS device emulator. For Windows, the Android Studio contains a simple wizard to install emulators for various Android devices. 

After setting up Node.js and installing an emulator of your choice, let’s go ahead and install Appium and Webdriver.io. Run the following commands in your project directory for this example: 

npm i -g appium@next 
 

npm init wdio 

Note: If you have trouble installing/setting up Appium then go ahead and use the Appium Doctor package to fix all your settings in case you have not done this yet. There are quite a few settings that can give issues for Appium, and I will not go over all of these in this tutorial. The Appium Doctor package is an immense help that will help you and give suggestions on how to fix these issues. 

On a sidenote if you are having trouble using Appium in the command line, you could always install the Appium Server GUI (Graphical User Interfaces), which looks like this: 

This way, you do not have to fiddle around in the terminal to start your Appium server or see logs of what is happening. You can tinker around in the GUI in case you cannot find what you are looking for in the CLI (Command Line Interface). 

I will cover some basic steps that might seem a bit confusing at first, but are vital before doing anything with Appium.  

Desired capabilities 

Before Appium can connect to your device, the emulator needs to know what to look for! This is where the capabilities files come in. These files contain the information that Appium needs before it can find and connect to your device such as device name, orientation, platform name, and many others. 

I have set up an iPhone 8 emulator with iOS version 15.5, so let us ensure Appium knows this. My iOS capabilities file: wdio.ios.conf.js contains the following: 

const { config } = require('./wdio.shared.conf.js');
config.capabilities = [
    {
        hostname: '0.0.0.0',
        path: '/wd/hub',
        browserName: 'Safari',
        platformName: 'iOS',
        maxInstances: 1,
        
        // For W3C the appium capabilities need to have an extension prefix 'appium'
        'appium:deviceName': 'iPhone 8',
        'appium:platformVersion': '15.5',
        'appium:orientation': 'PORTRAIT',
        'appium:automationName': 'XCUITest',
        'appium:newCommandTimeout': 2000,
        
    },
];

exports.config = config;

As you can see in the example above, there is more than just the device name and iOS version required in the capabilities. Some of these are optional: Orientation could be either ‘PORTRAIT’ or ‘LANDSCAPE’, or you could just leave it out altogether.

A particularly important one is the automation name: The name of the automation engine that Appium needs to use. These differ depending on which platform you are automating on Android uses ‘UiAutomator2’ and iOS ‘XCUITest’.

A full list of all the possible desired capabilities can be found in the Appium documentation.

For Android users the setup is quite similar to iOS with some minor changes. Take for instance that you are automating in Android then you would make a wdio.android.conf.js file. This would have the following content;

const { config } = require('./wdio.shared.conf.js');

config.capabilities = [

    {
        hostname: '<local ip address>',
        path: '/wd/hub',
        browserName: 'Chrome',
        platformName: 'Android',
        maxInstances: 1,

        // For W3C the appium capabilities need to have an extension prefix 'appium'
        'appium:deviceName': '<Android device name>',
        'appium:platformVersion': '<platform version of device>',
        'appium:orientation': 'PORTRAIT',
        'appium:automationName': 'uiautomator2',
        'appium:newCommandTimeout': 30000,
        "appium:sdk": "<location of Android sdk>"
    },
];

exports.config = config;

Note: If you are testing a mobile app, then you would want to change the ‘path’ capability to ‘app’ and pass the location of your app file.

You might have noticed your IDE giving you some errors about the first line in the aforementioned file, how can he require a file that does not exist and what is this wdio.shared.conf.js file anyway?

In my setup I have made sure I have two separate config files for my devices: one for Android, and one for iOS. I also have one file where all the shared config properties are present. It would be bad practice to have the same configurations saved twice on two separate files so we will create one shared configurations file that both devices can access.

The config files will be accessed by Appium through our terminal commands. We will tell Appium which config file to use and the config file will contain which tests to run.

This is how I have set up the shared configurations file wdio.shared.conf.js. Take notice of the exports keyword, this is mandatory, so our device configurations files have access to these shared configurations:

exports.config = {
    port: 4723,
    specs: [
        './specs/example-test.spec.js’
    ],
    services: [
        ['appium'],
    ],
    wait: '2000',
    interval: '100',
    logLevel: 'info',
    // bail (default is 0 - don't bail, run all tests).
    bail: 0,
    // Default timeout for all waitFor* commands.
    waitforTimeout: 10000,
    // Default timeout in milliseconds for request
    // if browser driver or grid doesn't send response
    connectionRetryTimeout: 20000,
    // Default request retries count
    connectionRetryCount: 3,
    framework: 'mocha',
    // Test reporter for stdout.
    // The only one supported by default is 'dot'
    // see also: https://webdriver.io/docs/dot-reporter
    reporters: ['spec'],
    mochaOpts: {
        ui: 'bdd',
        timeout: 60000
    },

}

You should notice that the error your IDE gave you, about the file not existing, should now be gone! If this is not the case, then please go over the previous steps again and make sure to double check you used the correct file names and exported the shared configuration.

In this file I tell Webdriver.io which port to use, what test files to run, and what service to use for running them: Appium.

If you followed my example to the letter, then you should go ahead and make a directory for your project called: specs. Fill this directory with the tests you would like to automate. In our example config file, we referred to the following file: example-test.spec.js. Let us create this file and write something simple to check if our framework works!

equire('webdriverio');


describe('b.ignited test suite', () => {
it('can visit b.ignited website', async () => {
await browser.navigateTo('https://bignited.be/');
await expect(browser).toHaveUrlContaining('bignited');
})
})

Putting it all together

Now that all our files have been created, we can run them by using the following Appium and Webdriver.io commands. Open an extra terminal window and type the following commands (assuming you installed Appium and Webdriver.io in the first steps using NPM!):

appium

npx wdio run [path.name.of.your.config.file.js]

In our case, if you used the same file names I have mentioned before, this is:

npx wdio run wdio.ios.conf.js

If you managed to setup Appium and configure the correct capabilities, then you will see Webdriver pass these on through to Appium and then the magic happens:

Of course, you should see your emulator perform all the tests that you told Webdriver to run. If this did not work, then you might want to check the more common errors:

  1. Check if Appium is up and running, using the GUI if necessary.

  2. Make sure Webdriver.io CLI is installed.

  3. Use the correct port in the config file

  4. Specs are in the right location in case you are passing them in the config file

  5. For Android Device emulators: make sure the emulator is running + correct Chrome webdrivers are installed, check the error messages in your terminal window or use Appium Doctor!


Note: If you are using a Mac computer, then the iOS Device emulator will start by itself whenever the iOS config file is called.

You can encounter some weird shenanigans when Appium is running in the background, such as sessions not correctly terminating, emulator doing stuff in the background while your tests have finished, etc. This is unfortunately a side product of Appium being… Appium. I have had my fair share of issues trying to figure out how Appium worked and most of them were solved by just restarting and trying again.

I hope these basic steps were useful to you. Now, go forth and automate!