11

Diverse Test-Automation Frameworks For React Native Apps - Smashing Magazine

 3 years ago
source link: http://brianyang.com/diverse-test-automation-frameworks-for-react-native-apps-smashing-magazine/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.


The bar is set high for today's mobile apps. First, apps must meet the standard of quality that app markets expect. Secondly, mobile app users are very demanding. Plenty of alternatives are available to download, so users will not tolerate a buggy app. Because mobile apps have become such a crucial part of people's lives, users won't be shy about sharing their love or hate for an app - and that feedback gets in front of hundreds of millions of users in seconds.

Further Reading on Smashing: Link

7
The increase in mobile platforms and device fragmentation. (View large version8)

The Basic Architecture Of React Native Apps

The concept of this type of "learn once, write anywhere" framework wasn't new, though; we had already seen JavaScript libraries do similar things ( Sencha, PhoneGap and Appcelerator, among others), but something was better about React that had an impact on developers' habits and how they break down an application's UI into discrete components.

Test Automation On Different Levels: Unit, Integration, Component And Functional

In this article, I'll cover test methods and automation frameworks at three levels. The primary focus is on the highest level, functional testing, but React Native apps can be tested - and testing can be automated - on at least the following levels:

  • Unit testing
    This could be even as basic as testing JavaScript objects and methods on the component level.
  • Component testing
    Each component can be tested either visually or functionally.ReactTestUtils provides a simple framework for testing React components.
  • Integration testing
    Integration testing comes next and is a phase when a group of different units are typically tested as an entity.
  • Functional testing
    Functional testing is a type of black-box testing that focuses on user requirements and interactions, and it covers all underlying software, all user interaction and the application as an entity.

In addition to ReactTestUtils, React Native provides useful unit-testing methods, but none of them thoroughly cover the application's actual logic. Therefore, mobile apps built on React Native benefit more from functional UI testing. A variety of functional test-automation frameworks are available, and we'll look at few of the most popular ones in this article.

While unit testing can be done at the component level, functional test automation provides better capabilities for testing the larger entities in a React Native app. With React Native, component logic unit testing can be done in isolation, using traditional JavaScript libraries and forcing React Native to return regular components instead of native ones. With functional test-automation frameworks, UI components are part of the app and are easy to test as a whole.

I'll separate these frameworks into cross-platform frameworks and platform-specific frameworks, as illustrated in the picture below.

Unit Testing With Jest and Jasmine Link

Jest uses the Jasmine behavior-driven framework as the basis for testing JavaScript code. Every test case starts from a describe() function call, similar to how JUnit uses the TestCase class. The describe() function takes two parameters: the description and title of the test case, and the function to be executed. The it() function includes all of the test steps and (similar to JUnit) provides a series of expect() functions.

Here is an example of a Jasmine test script for a player application.

describe("Player", function() {
            var player;
            var song;
            beforeEach(function() {
                player = new Player();
                song = new Song();
            });
            it("should be able to play a song", function() {
                        player.play(song);
                        expect(player.currentlyPlayingSong).toEqual(song); //demonstrates use of custom matcher expect(player).toBePlaying(song); }); describe("when song has been paused", function() { beforeEach(function() { player.play(song); player.pause(); }); it("should indicate the song is paused", function() { expect(player.isPlaying).toBeFalsy(); // demonstrates use of 'not' with a custom matcher expect(player).not.toBePlaying(song); }); it("should be possible to resume", function() { player.resume(); expect(player.isPlaying).toBeTruthy(); expect(player.currentlyPlayingSong).toEqual(song); }); }); // demonstrates use of spies to intercept and test method calls it("tells the current song whether the user has made it a favorite", function() { spyOn(song, 'persistFavoriteStatus'); player.play(song); player.makeFavorite(); expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true); }); //demonstrates use of expected exceptions describe("#resume", function() { it("should throw an exception if song is already playing", function() { player.play(song); expect(function() { player.resume(); }).toThrow("song is already playing"); }); }); });

This basic example shows how Jasmine can be used to test the functionality of an app, but it keeps the focus on method-level testing. In addition, React Native provides some basic capabilities for testing integrated components. This works for both native and JavaScript components and enables communication between them via a bridge.

Integration Testing Link

A basic Objective-C example for building a test skeleton of an iOS app would start like this:

@implementation ExampleTests {
    RCTTestRunner * _runner;
} - (void) setUp {
    [super setUp];
    _runner = RCTInitRunnerForApp(@ "IntegrationTestHarnessTest", nil);
} - void() testExampleTests {
    [_runner runTest: _cmd module: @ "ExampleTests"]
}
@end

For functional UI testing, I'll be covering the most prominent and most used test-automation frameworks, including Appium, Calabash, XCTest and a few others.

Using Functional Test-Automation Frameworks With React Native Apps

To streamline the app development process and to maximize testing coverage, we have numerous open-source test-automation frameworks to choose from.

In addition, a range of great platform-specific frameworks are available. Naturally, each framework has been built for a particular platform and, in most cases, is easier to adopt for that platform. In addition to Appium and Calabash, I'll cover four platform-specific frameworks in this article: Robotium and Espresso for Android, and XCTest and EarlGrey for iOS.

When it comes to test automation, bear in mind that apps built with React Native are fully native on both iOS and Android; hence, functional test-automation frameworks will work fine with them.

The example I'll use with each framework is an implementation of a very basic radio button UI.

The test snippet included in each framework section below shows how the test script deals with each UI element and how clicks and other user inputs are handled. The purpose of the examples is not to provide step-by-step instructions, but rather to compare examples and show what is available for test automation today and what programming languages can be used for testing.

Cross-Platform Frameworks

As stated, React Native is not actually a cross-platform framework, but adoption of it across other platforms is easy. In the next two sections, we'll go through two popular cross-platform test-automation frameworks for mobile testing and mobile test automation.

Appium is an open-source test-automation framework, with an inspection tool that works well for native, hybrid and mobile web apps. It uses JSONWireProtocol internally to interact with iOS and Android apps, using Selenium WebDriver. Because of this, Appium works extremely well for the mobile web as well, and the use cases are very similar if Selenium is used for web testing.

When it comes React Native-powered apps, JavaScript isn't necessarily required; tests can be written in any language. For example, Appium scripts can look like this:

driver.findElement(By.id("com.example.app:id/radio0")).click();
driver.findElement(By.id("com.example.app:id/radio1")).click();
driver.findElement(By.id("com.example.app:id/radio2")).click();
driver.findElement(By.id("com.example.app:id/editText1")).click();
driver.findElement(By.id("com.example.app:id/editText1")).sendKeys("Simple Test");
driver.findElement(By.name("Answer")).click(); // or alternatively like this: driver.findElement(By.id("com.example.app:id/button1")).click();

With iOS, Selenium WebDriver gets a command from the Appium script (for example, click()) and sends it in the form of JSON via an HTTP request to the Appium server. Appium knows the automation context and sends this command to the Instruments command server, which waits for the Instruments command client to pick it up and execute it with bootstrap.js in the iOS Instruments environment. Once the command is executed, the Instruments command client sends the message back to the Appium server, which logs everything related to the command in its console. This cycle keeps going until the test script has finished.

On Android, things work almost the same way, except that the frameworks used are Selendroid and UiAutomator. In short, Appium translates WebDriver commands to UiAutomator (API level 17 or higher) or Selendroid (API level 16 or lower) commands. On a physical device, bootstrap.jar launches a TCP server that gets commands from a TCP client. The process is similar on iOS.

Calabash Link

The example below shows how our application and its UI components (radio buttons, text field and button) would be implemented in Calabash:

Feature: Answer the question feature Scenario: As a valid user, I want to answer app question, I wait
for text "What is the best way to test application on a hundred devices?"
Then I press radio button 0 Then I press radio button 1 Then I press radio button 2 Then I enter text "Simple Test"
into field with id "editText1"
Then I press view with id "Button1"

Steps usually begin with one of the keywords given, then, when, and or but. However, they don't have to; they can use * instead.

Calabash is also widely used by non-developers, and it can be used for product specifications and documentation due to its easy-to-understand language and logic. Eventually, the features and scenarios are wrapped in Ruby code.

$ gem install calabash - android $ gem install calabash - cucumber

This will take care of installing Calabash-Android and Calabash-iOS, and your journey with test automation can begin.

Platform-Specific Frameworks

Robotium and ExtSolo (Android) Link

  • automatic scaling of x and y clicks for any display resolution;
  • multi-path drags;
  • automatic screenshot capture at moment of test failure;
  • mock locations (GPS coordinates);
  • change of Android device language;
  • control of Wi-Fi connection;

With Java code, tests are easy to build using any Java SDK and IDE. The primary function used in this example is findViewById, which finds a view that is identified by the id attribute. The UI element could be also identified by a name, class or some other attribute. Our code example with an id attribute would look like this:

solo.clickOnView(solo.findViewById("com.example.app:id/radio0"));
solo.clickOnView(solo.findViewById("com.example.app:id/radio1"));
solo.clickOnView(solo.findViewById("com.example.app:id/radio2"));
solo.enterText((EditText) solo.findViewById("com.example.app:id/editText1"), "Simple Test");
solo.clickOnView(solo.findViewById("com.example.app:id/button1"));

Robotium here is trying to locate UI elements based on the id, description and other characteristics. Unfortunately, this isn't always the best approach and does not necessarily work well with webview components. However, with the help of the ExtSolo library, users can define clicks and other interactions on UI elements that scale with the resolution. Also, hardcoding coordinates is possible, and these scale when the display resolution changes.

If you are using Robotium, then getting started with Robotium ExtSolo is easy and effortless. Just clone the repository for yourself and build the library:

$ git clone https: //github.com/bitbar/robotium-extensions $ ant clean instrument

After this, place the recently built .jar file in the libs folder in your Android Studio project, and make sure your project is linked to it. All of these great additional features and services are now in your workspace.

Espresso (Android) Link

The Espresso testing framework provides APIs for writing UI tests to simulate user interactions for an Android app. The Espresso API is lightweight and provides three main components: viewMatchers, viewActions and viewAssertions.

The beauty of Espresso is that it provides automatic synchronization of test methods and UI elements that are being tested. For example, if the test script wants to press a button but the button isn't visible on the screen yet, it will wait until this button can be pressed (i.e. it is visible and a click can happen). This makes test execution very fast because no test scripts need to include any sleep or wait commands. Also, developers do not need additional logic to handle timing-related issues.

// R class ID identifier for radio buttons onView(withId(R.id.radio0)).perform(click()); onView(withId(R.id.radio1)).perform(click()); onView(withId(R.id.radio2)).perform(click()); onView(withId(R.id.EditText1)).perform(click()); // Instead of R, we use getIdentifier onView(withId(getInstrumentation().getTargetContext().getResources() .getIdentifier("com.example.app:id/EditText1", null, null))).perform((typeText("Simple Test"))); onView(withId(getInstrumentation().getTargetContext().getResources() .getIdentifier("com.example.app:id/Button1", null, null))).perform(click());

Espresso has its own pros and cons, and due to the lightweight API, not many additional services or function calls are available to developers. For instance, you must use alternative methods to take screenshots, manage tests, output test results and more.

XCTest and KIF (iOS) Link

Let's see how our UI components would look with Objective-C:

-(void) testClicksOnRadioButtons {
    [tester tapViewWithAccessibilityLabel: @ "Radio1"];
    [tester tapViewWithAccessibilityLabel: @ "Radio2"];
    [tester tapViewWithAccessibilityLabel: @ "Radio3"];
    [tester enterText: @ "Simple Test"
        intoViewWithAccessibilityLabel: @ "editText1"
    ];
    [tester tapViewWithAccessibilityLabel: @ "Answer"];
}

Alternatively, with Swift, the test would look as simple as this:

testClicksOnRadioButtons() {
    let app = XCUIApplication() app.radiobutton[0].tap() app.radiobutton[1].tap() app.radiobutton[2].tap() app.staticTexts["Simple Test"] app.button[0].tap()
}

EarlGrey (iOS) Link

There are a lot of similarities between EarlGrey and Espresso (yes, both are developed by Google), and their characteristics make both frameworks work and execute tests quickly. Similar to Espresso, EarlGrey tests automatically wait for events (animations, network requests, etc.) before trying to interact with the UI. This makes writing tests easier because developers do not need to worry about sleep or wait commands. In addition, the code itself is easier to maintain because it provides procedural descriptions of the test steps.

EarlGrey also contains matchers that are available from the GREYMatchers class. The documentation recommends using UI elements with the accessibility parameters. To identify UI elements, developers can use grey_accessibilityID() or grey_accessibilityLabel().

-(void) testBasicSelectionAndAction {
    [
        [EarlGrey selectElementWithMatcher::grey_accessibilityID(@ "ClickHere")] performAction: grey_tap()
    ]; // Example of long press with EarlGrey matchers - (void)testLongPress { [[EarlGrey selectElementWithMatcher::grey_accessibilityLabel(@"Box")] performAction:grey_longPressWithDuration(0.5f)]; [[EarlGrey selectElementWithMatcher::grey_accessibilityLabel(@"One Long Press")] assertWithMatcher:grey_sufficientlyVisible()]; // Example of multi-select, visible click on items - (void)testCollectionMatchers { id visibleSendButtonMatcher = grey_allOf(grey_accessibilityID(@"Box"), grey_sufficientlyVisible(), nil); [[EarlGrey selectElementWithMatcher:visibleSendButtonMatcher] performAction:grey_tap()]; }

Similar to XCTest, our radio button implementation isn't that straightforward, and buttons for XCTest should be defined as iOS-supported UIElements to enable clicks and user interactions.

Conclusion

We've covered the basics of React Native applications and how they can be tested using various methods and frameworks. This comes up quite often, but the industry standards for mobile test automation at the functional UI level will work on React Native apps just as they do with any other native apps. The test-automation frameworks we've covered here are widely used for native mobile apps, hybrid apps, the mobile web as well as React Native apps.

In summary, determining the programming language that a mobile application is built on is not critical because it won't have any influence on the test-automation frameworks that it can be tested with. As discussed, plenty of powerful test-automation frameworks are available today, which React Native apps will work with when wrapped as an APK or IPA.

What are you using for React Native app testing? Weigh in with a comment below!

↑ Back to topTweet itShare on Facebook


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK