Uncategorized

Detox – React-Native Automation Testing

(Detox) => {React-Native-Automation-Testing}

Detox is from the Engineering Company Wix  who I need to shout out as one of the coolest software engineering companies out there. Detox is an Automation Testing tool or library for testing both Android and iOS apps, which have been written using the iOS and Android development library React-Native.

Detox refer to this tool as …

Gray box end-to-end testing and automation library for mobile apps.

rn-detox
Screen Shot 2019-07-24 at 7.08.58 pm
Screen Shot 2019-07-24 at 10.22.50 pm

AppCart.io

The app under test is AppCart.io, this is a relatively complex app, which contains multiple services and integrates with woo-commerces shopping platform. There are around 100 components and 30 screens. There is connectivity to a payment gateway and certain personal data is fully encrypted.

The usage case for Detox, was to generate a series of end-2-end tests, which would deliver pathways through the application, giving me (the developer+tester) confidence that the app would deliver key requirements to the client. This again is me.

A note from me on end-2-end testing. I personally would rather have a much more granular defined set of features, which when combined can mimic certain pathways. However when your developer, tester and building an app for a business. Time is everything and therefore testing can be left to the last minute etc.

For this reason, this is why 100% recommend a set of end-2-end tests. 

Defining Pathways

When I first thought about defining pathways through the application, I wanted to make sure that I both create a highly repeatable set of tests which were also robust. I also wanted to design pathways, which also in isolation delivered a a feature.

I knew for instance that styling might change, but the overall functionality of the application would stay constant. I also new that the application had simple routes and slightly more complex routes. Lastly the application has a range of products, which will have different properties.

React-Native components such as View and Text come a property of testID. This allows test automation software such as Detox to use these props to query and pass instructions to.

e2e example under considerations

This is one of the simplest journeys, however there are several considerations.

1. The product to be added, simple vs complex variation

2. What is the end of the journey

3. What should we check on the way, if anything. Explicit Vs Explicit.

 

1. Should we chain this test with the previous?

2. Should we use a different product?

Once I had established the tests, which I wanted to cover, I could apply adding in the testID prop. I’m not going to go into to much detail about setting up Detox configurations, as theres plenty of information available on the detox website. Except that in the packages.json this is where the test-runner is specified. In the example below, the test runner that I’m using in Mocha. A similar test runner is Jest, which is Facebooks own.

"detox": {
"configurations": {
"ios.sim.debug": {
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/AppCart.app",
"build": "xcodebuild -project ios/AppCart.xcodeproj -scheme AppCart -configuration Debug -destination 'platform=iOS Simulator,name=iPhone 7' -derivedDataPath ios/build",
"type": "ios.simulator",
"name": "iPhone 7"
}
},
"test-runner": "mocha"
}

The first test was Adding product to basket, below is the final code.

it('should add product to cart', async () => {
await element(by.id('index_0')).tap();
await element(by.text('Demo product')).tap();
await waitFor(element(by.id('add_to_bag'))).toBeVisible().whileElement(by.id('product_details_scroll')).scroll(50, 'down');
await element(by.id('add_to_bag')).tap();
await element(by.id('rightNavButton')).atIndex(0).tap();
await expect(element(by.id('cart_item')).atIndex(0)).toHaveText("Demo product");
});

If we start with the first line of detox.

await element(by.id('index_0')).tap();
appcart-1
appcart-2

We can break down the line to multiple parts.

  1. await
    • JS mechanism to wait for the async event to return
  2. element
    • This is the Detox element of UI Object, such as UIText
  3. by
    • Similar to selenium, this will recognise the object. In this instance id, detox provides a list of types, such as text. These are also known as Matchers. 
  4. tap
    • The event which will be applied to the element once its returned
The actual element I want to click is the first category. In the images, this is the Sale category and also in the image, you can see some of React-Native inspector to help you find those elements.

Because this is a Category within a ScrollView, within a ListView – not an unusual hierarchy, means I either have to navigate through the parent child layer or, I can apply a testID to each generated Category.

renderRow={(category, sectionId, rowId) =>
<ProductCatItem category={category} index={rowId}/>
}

I’m passing through a prop of index, which is generated from my renderRow.

<TouchableOpacity testID={ 'index_' + this.props.index }

Therefore in my Touchable I can set the testID and append an index.

Another interesting feature of Detox is the ability to wait and provide an event whilst doing so.

await waitFor(element(by.id('add_to_bag'))).toBeVisible().whileElement(by.id('product_details_scroll')).scroll(50, 'down');

This is really two events.

  • Waiting
  • Looping

The waits for Add to Bag to come into view, using the ScrollView (product_details_scroll), the instructions will navigate down by 50pts until it appears. I did notice that when it could’t find the object, as I had provided the incorrect By, it just kept on trying scroll. I’m not aware of a timeout option here, something to investigate!

At which point the next instruction will tap the button.

await element(by.id('add_to_bag')).tap();

The last bit if redux I found useful, at this early stage was the atIndex method.

await element(by.id('rightNavButton')).atIndex(0).tap();

Whilst I was able to apply an index to my own ListView component, its sometimes not possible with imported libraries. In this instance Detox through an error for multiple objects. Therefore I can provide an atIndex(0), which will select the first one.

Finally the assertion.

await expect(element(by.id('cart_item')).atIndex(0)).toHaveText("Demo product"); 

Detox has multiple expectations, in this instance I’m just looking for the first CartItem to contain ‘Demo product’

Summary

So far I’ve found Detox to be a really great library for automating your react-native app. Although early days, the API seems to take ideas from multiple sources, such as selenium and expectations. Its also generated you a parent/child screen dump, similar to html page source if your not familiar. This can help you find your object, which means you can modify your Matcher type.

Some future plans I’ve got for Detox is parallel execution, as running Detox can be a little slow and execution in an CI environment, ideally a cloud provider such as Travis.

Finally I’ve mainly concentrated on iOS, however I did a small amount with Android and for a react-native developer, like myself who concentrates mainly on iOS. The advantage of using Detox is i can execute against Android and in theory discover issues, I may not of picked up.

More info:

Leave a Reply

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