📜 ⬆️ ⬇️

Testing on Android: Robolectric + Jenkins + JaСoСo

Testing Android applications is a big and capacious topic, you can talk about it endlessly. We at Rambler & Co autotest love, write and actively use for all of our applications. In this article we will explain how to get and analyze the results of testing android (and not only) applications. Let us show you how to set up Robolectric, JaCoCo and Jenkins so that it is like this:



Robolectric

Robolectric is a library that allows you to run tests for android applications on a local JVM. Yes, yes, exactly, no need to wait until apk is loaded and installed, until the application is launched on the phone, just press start and the JVM quickly scrolls through all tests. Android environment is emulated, there is access to the main functions.

Robolectric is actively developing, but it still has many problems, so we use robolectric to test business objects, application logic, data storage and processing. Where pure jUnit is not enough, but the real device is still not needed. For ui testing, we recommend espresso from google .
There are quite a few materials in the network about this wonderful library in Russian, so we will attach a small instruction on tuning.
')
Installation

With the release of version 3.0, the installation of the library fits into one line (previously a plug-in was also required), add to dependencies:

testCompile 'org.robolectric:robolectric:3.0' 


Please note that we specified testCompile, and not androidTestCompile. testCompile indicates that these dependencies are needed for Unit Tests, and androidTestCompile for Android Instrumentation Test. Select build Variants, Test Artifact - Android Instrumentation Test in the window, wait for the studio to update, and voila ... the tests are gone! What to do?
Build variants



The point is that for unit tests (by default) src / test is used, and for android test - src / androidTest. Create the following folders in the src \ folder: \ test \ java and \ test \ resources. The first is used for tests, the second for resources. Here is an example of access to resources:

 InputStream stream = getClass().getClassLoader().getResourceAsStream("habr.txt"); 


First test

It is very simple to write tests for robolectric: we create tests, we write code, we use annotations. Activation access through setupActivity \ buildActivity. More details with all the features can be found on the website, robolectric.org/writing-a-test

 @RunWith(RobolectricTestRunner.class) public class MyActivityTest { @Test public void clickingButton_shouldChangeResultsViewText() throws Exception { MyActivity activity = Robolectric.setupActivity(MyActivity.class); Button button = (Button) activity.findViewById(R.id.button); TextView results = (TextView) activity.findViewById(R.id.results); button.performClick(); assertThat(results.getText().toString()).isEqualTo("Robolectric Rocks!"); } } 


Another example (www.vogella.com)
 package com.vogella.android.test.robolectric; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.*; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import com.example.BuildConfig; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.annotation.Config; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.shadows.ShadowToast; import android.content.Intent; import android.widget.Button; @RunWith(RobolectricGradleTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21, manifest = "src/main/AndroidManifest.xml") public class MyActivityTest { private MainActivity activity; @Test public void shouldHaveHappySmiles() throws Exception { String hello = new MainActivity().getResources().getString(R.string.hello_world); assertThat(hello, equalTo("Hello world!")); } @Before public void setup() { activity = Robolectric.buildActivity(MainActivity.class) .create().get(); } @Test public void checkActivityNotNull() throws Exception { assertNotNull(activity); } @Test public void buttonClickShouldStartNewActivity() throws Exception { Button button = (Button) activity.findViewById(R.id.button2); button.performClick(); Intent intent = Robolectric.shadowOf(activity).peekNextStartedActivity(); assertEquals(SecondActivity.class.getCanonicalName(), intent.getComponent().getClassName()); } @Test public void testButtonClick() throws Exception { MainActivity activity = Robolectric.buildActivity(MainActivity.class) .create().get(); Button view = (Button) activity.findViewById(R.id.button1); assertNotNull(view); view.performClick(); assertThat(ShadowToast.getTextOfLatestToast(), equalTo("Lala")); } } 



Examples of robolectric tests (from the robolectric team):
github.com/robolectric/robolectric-samples

Examples of robolectric + espresso tests:
github.com/robolectric/deckard

Running tests

To run the tests, we need a new configuration. Click the right mouse button on the test, select Create “testName”, if necessary, change the configuration, click “OK”, the test is ready. Attention, you may need to add an application folder (\ app) to the working directory. You can run all the tests, or tests from a separate package.
Creating a configuration


Next, run the test:
Test run


And we get the result in the run - window:

Test results


Run all tests from the console:
gradlew test


JaCoCo

Mention about this wonderful tool in runet is also very few, correct.
JaCoCo is used to calculate and display the code coverage of tests.
For example, as in the screenshot:
JaCoCo code test coverage



Website: www.eclemma.org/jacoco
To add a library to android, you need to update your gradle file:

 apply plugin: 'jacoco' jacoco { toolVersion = "0.7.1.201405082137" } def coverageSourceDirs = [ '../app/src' ] task jacocoTestReport(type: JacocoReport, dependsOn: "testDebug") { group = "Reporting" description = "Generate Jacoco coverage reports" classDirectories = fileTree( dir: '../app/build/intermediates/classes', excludes: ['**/R.class', '**/R$*.class', '**/*$ViewInjector*.*', '**/BuildConfig.*', '**/Manifest*.*'] ) additionalSourceDirs = files(coverageSourceDirs) sourceDirectories = files(coverageSourceDirs) executionData = files('../app/build/jacoco/testDebug.exec') reports { xml.enabled = true html.enabled = true } } 


Be careful when specifying executionData. This is the place to store the exec file, which contains all the data about code coverage by tests.

Now you can test your work with the gradlew jacocoTestReport command. Reports should be in the folder: build \ reports \ tests
If it does not start, run the command:
gradlew clean assemble test jacocoTestReport

Test results are presented in the form of an html report, screenshots:
Sample reports



For Jenkins, a code coverage trend is added:
Sample Jenkins Reports




Jenkins setup

It is assumed that you already have a customized Jenkins. If not, then here is a good article about the initial configuration: habrahabr.ru/post/205308
We will “pump” our Jenkins a bit: let's teach him to run tests, build reports and check the percentage of code coverage:

Jenkins without plugins



Jenkins with plugins




Setup processing results

1) Enable archiving of artifacts and Publish html reports plugin
2) Configure jUnit plugin (or xUnit Plugin ).
3) Turn on the JaCoCo Plugin . We need to specify paths for exec files, to classes and resources. Almost the same as we specified in build.gradle

Configure Publish html reports and save artifacts



Configure jUnit test result report and JaCoCo Plugin



Using these tools will allow you to reduce the time and reduce costs in writing and analyzing tests. Thanks for attention!

Source: https://habr.com/ru/post/266837/


All Articles