DEV Community

Fernando Martín Ortiz
Fernando Martín Ortiz

Posted on

Mobile e2e tests using WebdriverIO and Appium

Context

So this is new for me. I normally write posts explaining things I've been doing in a daily basis for many years. However, I started working in a new project something like a month ago.
This post is about end-to-end tests. In the team I'm now taking part of, everyone is responsible for writing e2e tests for iOS/Android/Web. I've never done that before, I have to confess. I've done Unit tests many many times, but I've never written so many UI tests.
Appium, WebdriverIO and Jasmine are the tools this team has chosen to write these tests, and for good reasons. Let's first distinguish briefly among the different types of automated tests. Particularly between e2e and unit tests.

Unit tests

These tests are responsible for validating that a single unit is working properly. You can think of a unit as a class or function. These tests are written in an isolated fashion. I mean, if the rest of the system is full of bugs and nothing else work, if this unit work, the test will pass. They are also repeatable. They don't depend on anything else, really. Anytime you run the test, if the code hasn't changed, the test will report the same result.
These tests are intimately related to the code quality of your project. If your code is clean, these tests should be relatively easy to write. When writing unit tests in iOS, you usually use XCTest or Quick

End to end tests

These tests are based on a user's perspective. You put in the shoes of the final user and start the app, and tap on the buttons, and fill in textfields, everything a final user would do when using your app. You test flows and everything in a real world scenario.
These are the tests we will be talking about in this article. Xcode introduces a framework for writing e2e tests (also known as UI tests), called XCUITest.

Why not XCUITest?

XCUITest is a Xcode-only framework. I could write tests in XCUITest and run them to validate my app is working as intented, but I would need to also write tests, maybe in Espresso to test the Android side. If I also have a web frontend, I'd need to use Selenium Webdriver or any other library to write test for that side of the app.
That's why Appium and WebdriverIO come handy. They abstract the differences among the different e2e testing libraries, allowing us to write all the test in Javascript (a very well known language), and only writing platform-specific code when needed.

Appium and WebdriverIO

Here is where the things became a bit cloudy for me at the beginning so I'll be adding links along the process. Appium is basically a service that connects a testing process with a device. It installs an application in the device, called Webdriveragent, and then communicates with the device using HTTP calls. So Appium is the service that allows the communication in the first place.
WebdriverIO is the framework itself that we use to write tests for the different platforms. We write code for WebdriverIO and then Appium executes those tests in the devices specified in the configuration.

appium-webdriverio-arch

An example

Well, enough theory for now. I've prepared a quick, small example so you can understand hands-on how all of this work.
So, go and clone/download the code in this repo.
This is the simplest app you could think of. It's a Counter app, that has a label in the center of the screen, a button to "add" and a button to "sub". As simple as that.
A think that could draw your attention is this fragment:

valueLabel.accessibilityIdentifier = "value_label"
addButton.accessibilityIdentifier = "add_button"
subButton.accessibilityIdentifier = "sub_button"
Enter fullscreen mode Exit fullscreen mode

We're setting accessibilityIdentifier to all of these views. Although not needed, it's convenient when it comes to automation.

The Appium inspector

Now, download Appium Desktop from this link.

Appium Desktop is a very convenient app that can launch an Appium server, install and connect to the app running in your device and simulator and inspect it. It's not absolutely needed, but it's convenient.
Once you've downloaded it, you can go and launch it.

image

Click on Start Server with the default arguments. It will start the Appium server in the default host and port http://localhost:4723.

Then, click on Start Inspector Session (the small magnifying glass icon in the top right corner).

image

It will open a new window where you can set the "Desired Capabilities" for the test session. The Desired Capabilities documentation is in this link. They are basically the configuration that says in which device the tests would need to run, which platform is it, the testing framework (XCUITest in our case), etc.
Here is a basic example of it, you can replace the empty JSON for this:

{
  "platformVersion": "14.4",
  "autoGrantPermissions": "true",
  "udid": "AD7D6FF7-9B4C-4AB5-AD63-39736DA357F6",
  "automationName": "XCUITest",
  "deviceName": "iPod touch (7th generation)",
  "bundleId": "fmo.Testable",
  "platformName": "iOS",
  "app": "/Users/macbook/Developer/pruebas/appium_nodejs/__test__/app/Testable.app"
}
Enter fullscreen mode Exit fullscreen mode

Of course you will need to replace the "app" path (more on this later).
For the udid and deviceName, open a terminal and run this:

xcrun xctrace list devices

You'll be provided with a list of devices on which you can run your tests

image

That's my list. In my case, I just focused on running the tests on an iPod Touch Simulator:

iPod touch (7th generation) (14.4) (AD7D6FF7-9B4C-4AB5-AD63-39736DA357F6)

Where AD7D6FF7-9B4C-4AB5-AD63-39736DA357F6 is the udid, iPod touch (7th generation) is the deviceName, and 14.4 is the platformVersion.
The bundleId is the bundle identifier of your iOS project.
So, what is that app thing? Well, it's the .app file that is the product, result of building your project. If you build (Cmd + B) the project I've linked above using Xcode, you'll end by having a .app file in the products group, see:

image

You need to point to that file.

Now, open an iPod Touch or the simulator you would like to run your tests on, and start the session in the Appium Desktop app.

This should install the WebDriverAgent app in the simulator and install the .app in the simulator. In the Appium Desktop app, you should be able to see the app running in your simulator and you should be able to inspect what's being rendered. You are also able to record tests using this visual environment, but that isn't what we're doing in this example.

image

WebdriverIO configuration

If you were able to follow up until now, well done! You've just run Appium for the first time, and inspected your app in the Appium Inspector. Now, stop the Appium Desktop server, and let's start with the actual tests.
Before going on, you'll need to install nodejs, preferably by using nvm, although that's out of the scope for this tutorial.
Once you've that installed, let's create a new folder and open the terminal inside it.
We need to start a new Nodejs app, so I'll run npm init -y to accept the default arguments.

image

Then, let's install the dependencies we'll need: npm i --save-dev @wdio/cli appium

We'll use the @wdio/cli package to run the configure the project. So, after the npm install finishes, let's run the wdio (WebdriverIO) cli: npx wdio config

I'll run the tests on my local machine:
image

Using Jasmine:
image

Running the automation commands synchronously:
image

Without any compiler step:
image

Accepting the default location for the tests:
image

I'll let WebdriverIO to create some example tests for me:
image

I will be using Page objects! In case you didn't know this pattern, I'll absolutely recommend you to go and read Martin Fowler's article:
image

Using the default location for the page objects:
image

Using the Spec reporter:
image

Selecting Appium as the service for my test setup:
image

And using the default base url:
image

Just that. WebdriverIO will install some packages:
image

And we'll be ready to go.

Project Configuration

Now, If you open the generated project using Visual Studio Code: code .

image

There are some important things to notice:

  • Inside the test folder, you have pageobjects and specs.
  • As said, Page Objects are a pattern described by Martin Fowler. They are basically, objects that will let us access to the visual components in our app, and interact with them from inside our actual tests. You don't assert from these objects! This is important!
  • The specs are the actual tests. Here you can ask your page objects to do the interactions and then you put the assertions inside the specs to validate if the app is working as intended.
  • wdio.conf.js is the configuration file for your tests. Let's enter on this file.

There are a looot of configurations you can modify on the wdio.conf.js file, and they are well documented!

image

But what we need to change first is the capabilities key:
image

This is sooo web. Let's remove this configuration and replace it with the JSON configuration we used in Appium Desktop. But first let's add the .app file in a folder inside our new project, and replace the path in the app key in wdio.conf.js.
So this is how the capabilities key will look like:
image

The actual test

Finally! This has been a lot of configuration steps and theory but at the end, we're ready to write our actual tests.
First, let's remove our existing Page Object, and create a new Page Object in a file called counter.page.js.

class CounterPage {
    get subButton() { return $("~sub_button"); }
    get addButton() { return $("~add_button"); }
    get valueLabel() { return $("~value_label"); }
    get value() { return this.valueLabel.getText(); }

    sum() {
        this.addButton.click();
    }

    sub() {
        this.subButton.click();
    }
}

module.exports = new CounterPage();
Enter fullscreen mode Exit fullscreen mode

The CounterPage class is basically a couple of getters, for different views and values, and some functions to interact with those views.

That $(...) jQuery-like selector is what we use to access to the elements in the screen. If we write $("~SomeIdentifier"), we're searching for an element in the screen with the accessibilityIdentifier attribute with value "SomeIdentifier", and that's exactly what we've done in our project. You can check for other ways to access these views using the Appium Desktop Inspector and checking the official documentation.

The .click() function is self-explanatory, but we've a lot of different ways to interact with our views.

Now, let's remove the example spec and write a new one called counter.e2e.js, with this content:

const CounterPage = require('../pageobjects/counter.page');

describe('The counter screen', () => {
    it('should sum correctly', () => {
        CounterPage.sum();
        CounterPage.sum();
        CounterPage.sub();
        CounterPage.sum();

        expect(CounterPage.value).toBe("2");
    });
});
Enter fullscreen mode Exit fullscreen mode

This is also self-explanatory if you've written js tests in the past. If not, this is similar to also the Quick framework in Swift. What we're doing is creating a test suite called The counter screen where we have a single test case called should sum correctly.

Inside this test case, we'll ask the CounterPage object to click on the "sum" button twice, then on the "sub" button and a last time in the "sum" button again. At the end, if we summed three and subbed one, the value label should have the text "2".

Let's run this using wdio:

npx wdio wdio.conf.js

This will start the app in your simulator and run the commands one by one, and finally show a report in the console:

image

To sum up

This has been longer than what I initially thought. And again, I'm not an expert at all, I'm just describing what I've learned this week, and it blowed my mind, to be honest. I think it's fascinating and really interesting and I wanted to share what I know with you. If you have any suggestion or any question or comment, please ask them in the comments section, I'll be pleased to answer what I know (or researching with you).

Interesting links

Apart from the official docs I've linked along the article, I'd really recommend you to go and check the courses in the Test Automation University. These folks have really useful content. My favorites so far are:

Top comments (4)

Collapse
 
srikanteswartalluri profile image
Srikanteswararao Talluri

Thanks for the wonderful write up. I am facing issue while I bring up appium service and automatically starting the webdriver agent on the iPad. Do you have any troubleshooting steps handy?
Right now, I am working that around by starting the appium server in the background and webdriver agent manually on the iPad, then passing on the webdriver agent URL in the capabilities.

Collapse
 
queznister profile image
QuezNister

In this blog, I want to include good information on how to incorporate automated tests for React native apps using Appium and WebdriverIO with a Node.js framework. fall out of love spell

Collapse
 
rama873 profile image
rama873

can we achieve cross platform test with WebdriverIo and Appium.

like single test can run on both IOS and Andriod(Assuming both have same accessibilityID's)

Collapse
 
renatosantanaoliveira profile image
Renato Santana

Can you help me configuration wdio with iOS. The configuration above did not run in simulator iOS in my machine.