Since joining Sauce Labs earlier this year, we’ve been busy making TestFairy better not only for mobile developers but also for SDETs that are looking for innovative ways to make their mobile test automation awesome. So, the challenge is howe to make Appium scripts talk with TestFairy SDK. All example code presented in this post is available as open source, code is open source and free for you to copy and adapt 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 to have 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 will read the intent, invoke TestFairy SDK and stop itself once the work is done. The service we launch will not interrupt any of the running activities, giving us ability to use it without change across various kind of apps such as video games, social networking and camera apps. Here is what you need to do.
Add these helpers to your Appium script. As example, we provide begin(), stop() and addEvent() but you can easily copy one of these to add 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".
/**
* Starts recording a TestFaiy session.
*
* @param client wdio client
* @param appToken TestFairy app token can be found at https://app.testfairy.com/settings
// Test suite
describe('Create TestFairy session', function () {
let client;
before(async function () {
client = await webdriverio.remote(androidOptions);
});
it('should create and destroy a session', async function () {
const res = await client.status();
assert.isObject(res.build);
const current_package = await client.getCurrentPackage();
assert.equal(current_package, 'com.example.appiumcallstestfairy');
// Start a session
console.log("Starting a TestFairy session");
await begin(client, "SDK-XXXXXXX"); // TestFairy app token can be found at https://app.testfairy.com/settings
// Make your assertions
// Mark significant points in time during the session to be able to use them later in your TestFairy dashboard for search or preview
await addEvent(client, "Initial assertions passed, this will show up in session timeline");
// Make your assertions
// Stop session
await stop(client);
console.log("Ending TestFairy session");
const delete_session = await client.deleteSession();
assert.isNull(delete_session);
});
});
// Test suite
describe('Create TestFairy session', function () {
let client;
before(async function () {
client = await webdriverio.remote(androidOptions);
});
it('should create and destroy a session', async function () {
const res = await client.status();
assert.isObject(res.build);
const current_package = await client.getCurrentPackage();
assert.equal(current_package, 'com.example.appiumcallstestfairy');
// Start a session
console.log("Starting a TestFairy session");
await begin(client, "SDK-XXXXXXX"); // TestFairy app token can be found at https://app.testfairy.com/settings
// Make your assertions
// Mark significant points in time during the session to be able to use them later in your TestFairy dashboard for search or preview
await addEvent(client, "Initial assertions passed, this will show up in session timeline");
// Make your assertions
// Stop session
await stop(client);
console.log("Ending TestFairy session");
const delete_session = await client.deleteSession();
assert.isNull(delete_session);
});
});
What is inside 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.
@Override
publicintonStartCommand(Intent intent, int flags, int startId){
Bundle extras = intent.getExtras();
if(extras != null && extras.containsKey("args")){
try{
finalString args = extras.getString("args");
final JSONArray argsArray = newJSONArray(newString(Base64.decode(args, Base64.DEFAULT), StandardCharsets.UTF_8));
Starting with Gradle 7, Android suggests the use of centralized repository declarations in settings.gradle over project or module level build.gradle declarations. If you created a...