Back to Resources

Blog

Posted March 16, 2023

How to Call the TestFairy SDK with Appium

Our mobile beta testing solution can help mobile developers and SDETs improve their mobile test automation. In this article, we cover how to make Appium scripts talk to the TestFairy SDK.

quote

We’ve been busy making our mobile beta testing solution better not only for mobile developers but also for SDETs who are looking for innovative ways to make their mobile test automation awesome. The challenge is how to make Appium scripts talk to the TestFairy SDK. All of the example code in this post is available as open source, so you can copy and adapt it to your needs.

Environment

We’ll be using WebdriverIO with JavaScript but all examples can be ported to other clients and languages with little to no change. The only requirement in our drivers is the ability to invoke mobile: startService via execute() which is already available on the latest UIAutomator2 drivers. Our test bed app is also included in the repo.

How it Works

Our solution is based on the principle that Appium can start Android services by constructing intents over adb. These intents can hold extra arguments for services to interpret. We’ve created a simple service to parse incoming intent data to decide which TestFairy SDK method to invoke. 

Every time Appium starts our service, the service reads the intent, invokes the TestFairy SDK and stops itself once the work is done. The service we launch will not interrupt any of the running activities, giving us the ability to use it without change across various types of apps including video games, social networking, and camera apps. 

Here are the steps you need to take:

  1. Copy the “Appium calls TestFairy” file into your Android app project.

  2. Add this line to your app manifest:

<service android:exported="true" android:name=".TestFairyService" />

Add these helpers to your Appium script. As an example, we provide begin(), stop() and addEvent() but you can easily copy one of these to add an invocation for other methods as you need. Make sure you update TestFairyService to parse your newly added invocations. Update these functions with your package name by replacing: com.example.appiumcallstestfairy/.TestFairyService with com.your.package.name/.TestFairyService

1
/**
2
* Starts recording a TestFaiy session.
3
*
4
* @param client wdio client
5
* @param appToken TestFairy app token can be found at https://app.testfairy.com/settings
6
*/
7
async function begin(client, appToken) {
8
client.startActivity()
9
const args = Buffer.from(JSON.stringify(['begin', appToken])).toString('base64');
10
await client.execute('mobile: startService', {
11
intent: '--es "args" "' + args + '" com.example.appiumcallstestfairy/.TestFairyService',
12
});
13
}
14
/**
15
* Sends a string event to currently recorded session. It will show up in the session timeline.
16
* Multiple sessions that has the same event can be searched at https://app.testfairy.com/sessions
17
*
18
* @param client wdio client
19
* @param event Some string value to represent a significant event happening during tests
20
*/
21
async function addEvent(client, event) {
22
const args = Buffer.from(JSON.stringify(['addEvent', event])).toString('base64');
23
await client.execute('mobile: startService', {
24
intent: '--es "args" "' + args + '" com.example.appiumcallstestfairy/.TestFairyService',
25
});
26
}
27
/**
28
* Stops recording a TestFairy session.
29
*
30
* @param client wdio client
31
*/
32
async function stop(client) {
33
const args = Buffer.from(JSON.stringify(['stop'])).toString('base64');
34
await client.execute('mobile: startService', {
35
intent: '--es "args" "' + args + '" com.example.appiumcallstestfairy/.TestFairyService',
36
});
37
}
38

Then, invoke TestFairy methods wherever you need them.

1
// Test suite
2
describe('Create TestFairy session', function () {
3
let client;
4
before(async function () {
5
client = await webdriverio.remote(androidOptions);
6
});
7
it('should create and destroy a session', async function () {
8
const res = await client.status();
9
assert.isObject(res.build);
10
const current_package = await client.getCurrentPackage();
11
assert.equal(current_package, 'com.example.appiumcallstestfairy');
12
// Start a session
13
console.log("Starting a TestFairy session");
14
await begin(client, "SDK-XXXXXXX"); // TestFairy app token can be found at https://app.testfairy.com/settings
15
// Make your assertions
16
// Mark significant points in time during the session to be able to use them later in your TestFairy dashboard for search or preview
17
await addEvent(client, "Initial assertions passed, this will show up in session timeline");
18
// Make your assertions
19
// Stop session
20
await stop(client);
21
console.log("Ending TestFairy session");
22
const delete_session = await client.deleteSession();
23
assert.isNull(delete_session);
24
});
25
});
26

What’s Inside the TestFairyService?

It is a small service which parses the bundled Intent extra. For simplicity, we pass all arguments as a single string value, json encoded in base64.

1
@Override
2
public int onStartCommand(Intent intent, int flags, int startId) {
3
Bundle extras = intent.getExtras();
4
if (extras != null && extras.containsKey("args")) {
5
try {
6
final String args = extras.getString("args");
7
final JSONArray argsArray = new JSONArray(new String(Base64.decode(args, Base64.DEFAULT), StandardCharsets.UTF_8));
8
switch (argsArray.getString(0)) {
9
case "begin":
10
TestFairy.begin(getApplicationContext(), argsArray.getString(1));
11
break;
12
case "addEvent":
13
TestFairy.addEvent(argsArray.getString(1));
14
break;
15
case "stop":
16
TestFairy.stop();
17
break;
18
default:
19
break;
20
}
21
} catch (JSONException t) {
22
Log.w("TestFairyService", "Can't invoke TestFairy", t);
23
}
24
}
25
stopSelf();
26
return super.onStartCommand(intent, flags, startId);
27
}
28

Once the call is done, we stop the service so that it does not consume more resources than necessary.

Win

Here you can find the complete example, including the test app. Let us know if we can improve it further. Until then, stay safe and enjoy hacking!

This article was originally published in August 2021 and has been updated in March 2023.

Diego Perini
Sr. Software Engineer
Published:
Mar 16, 2023
Share this post
Copy Share Link

Want to know more? Request a demo today.

DISCOVER

© 2023 Sauce Labs Inc., all rights reserved. SAUCE and SAUCE LABS are registered trademarks owned by Sauce Labs Inc. in the United States, EU, and may be registered in other jurisdictions.