Upload
infinum
View
168
Download
1
Embed Size (px)
Citation preview
ROBOLECTRIC SETUP
• 1. add gradle dependencies
• 2. write test application class
• 3. write test class
• 4. run tests
1. ADD GRADLE DEPENDENCIES
• make sure “Unit tests” is selected as Test Artifact under
Build Variants
• add the robolectric dependency in your app build.gradle file:
testCompile 'junit:junit:4.12'testCompile 'org.hamcrest:hamcrest-library:1.3' testCompile 'org.robolectric:robolectric:3.0'testCompile 'org.robolectric:shadows-support-v4:3.0'
2. WRITE TEST APPLICATION CLASS
• all unit test code goes into test flavour
• Test{applicationName} • must extend application class • runs instead of application class in tests • should inject test dependencies
3. WRITE TEST CLASS
• create new class in test flavor
• specify runner with @RunWith annotation
• add @Config annotation
• add test methods and annotate them with @Test
TEST CONFIGURATION
• constants property of @Config annotation is mandatory
• for all other options check:http://robolectric.org/configuring/
@RunWith(RobolectricGradleTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21) public class DeckardActivityTest { @Test public void testSomething() throws Exception { assertTrue(Robolectric.setupActivity(DeckardActivity.class) != null); }}
GRADLE DEPENDENCIES
• add the MockWebServer dependency in your app
build.gradle file:
• MockWebServer depends on okhttp library
• make sure that mockwebserver version matches okhttp
version
testCompile 'com.squareup.okhttp:mockwebserver:2.7.4'
USAGE
• 1. instantiate MockWebServer
• 2. call start() method
• 3. get local server url by calling url(“/“)
• 4. inject server local url into networking module
• 5. enqueue responses using enqueue() method
USAGE
• in both unit and instrumentation tests (steps 1 - 4): • prepare and inject MockWebServer in @Before method
(called before every @Test method) • stop MockWebServer in @After method (called after
every @Test method)
USAGE
• or in Robolectric unit tests (steps 1 - 4): • prepare and inject MockWebServer in:
beforeTest(Method) method in Test application • stop MockWebServer in:
afterTest(Method method) in Test application
ENQUEUEING RESPONSES
• enqueue(), MockResponse object
• https://github.com/square/okhttp/blob/master/
mockwebserver/src/main/java/com/squareup/okhttp/
mockwebserver/MockResponse.java
mockWebServer.enqueue( new MockResponse() .setResponseCode(200) .setBody("Response body"); );
LARGE RESPONSES?
• 1. store response body content to a file
• 2. put the file inside /test/resources/ directory
• 3. read the contents of the file in test:
public static String readFromFile(String filename) { InputStream is = ResourceUtils.class.getClassLoader().getResourceAsStream(filename); return convertStreamToString(is);}
CHECKING REQUESTS
• mockWebServer.getRequestCount()
• mockWebServer.takeRequest()
• mockWebServer.takeRequest(long, TimeUnit)
@Test public void nameOk() throws Exception { PokemonTestApp.getMockWebServer().enqueue( new MockResponse() .setResponseCode(200) .setBody(ResourceUtils.readFromFile("charizard.json")) ); String resourceUri = "api/v1/pokemon/6/"; Pokemon pokemon = new Pokemon(); pokemon.setResourceUri(resourceUri); Activity activity = buildActivity(pokemon); RecordedRequest request = takeLastRequest(); //Perform the assertions}
(A)SYNCHRONOUS EXECUTORS
• unit tests run in a single thread
• waiting for background threads complicates tests
• solution: Synchronous executors
(A)SYNCHRONOUS EXECUTORS
• setting executor on Retrofit:
• specify executor for networking and for callback
• for tests: both synchronous
return new RestAdapter.Builder() ... .setExecutors(new BackgroundExecutor(), new CallbackExecutor()) ... .build();
SYNCHRONOUS EXECUTOR?
• runs the queued runnable in the same thread
• simple as that:
public class SynchronousExecutor implements Executor { @Override public void execute(Runnable command) { command.run(); }}
SHADOWING A CLASS
• 1. create a custom Robolectric runner
• 2. declare shadowed classes in createClassLoaderConfig()
method
• 3. implement shadow class
• 4. run tests with custom runner
public class CustomRobolectricGradleTestRunner extends RobolectricGradleTestRunner { public CustomRobolectricGradleTestRunner(Class<?> klass) throws InitializationError { super(klass); } @Override protected AndroidManifest getAppManifest(Config config) { AndroidManifest appManifest = super.getAppManifest(config);
// needs to be the java package, not applicationId appManifest.setPackageName("co.infinum.app"); return appManifest; } @Override public InstrumentationConfiguration createClassLoaderConfig() { InstrumentationConfiguration.Builder builder = InstrumentationConfiguration.newBuilder(); builder.addInstrumentedClass(ShadowOutline.class.getName()); builder.addInstrumentedClass(ShadowAlertDialogSupportV7.class.getName()); builder.addInstrumentedClass(ShadowSpinnerView.class.getName()); return builder.build(); }}
3. IMPLEMENT SHADOW CLASS
• create a new class with @Implements(OriginalClass.class)
annotation
• add @Implementation annotation to methods that “override”
behaviour from original class
• you can leave some methods unchanged
• if you wish to use an instance of OriginalClass in your
shadow class it must be annotated with @RealObject
ADD SHADOWS TO TEST
• in order to make your shadows available in test add them to
@Config in your test: @Config(shadows = {ShadowClass.class})
@Implements(Outline.class) public class ShadowOutline { @RealObject private Outline outline; public Path path; public Rect rect; public float radius; public float alpha; @Implementation public void setConvexPath(Path convexPath) { if (path == null) { path = new Path(); } path.set(convexPath); }}
1. ADD DEPENDENCIES
• make sure “Android Instrumentation tests” is selected as Test
Artifact under Build Variants
• add the robolectric dependency in your app build.gradle file:
// EspressoandroidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1'androidTestCompile 'com.android.support.test:runner:0.4.1'androidTestCompile 'com.android.support.test:rules:0.4.1'
2. SPECIFY RUNNER
• specify default instrumentation test runner in application
build.gradle:
android { defaultConfig { testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } }
3. WRITE TEST CLASS
• create new class in test flavor
• annotate with @LargeTest
• add field of type ActivityTestRule and annotate with @Rule
- defines the activity that will run
• add test methods and annotate them with @Test
ASSERTJ ANDROID
• https://github.com/square/assertj-android
• extension of AssertJ
• you can extend and set up your own assertions
//Check that name in details is displayed properly.
assertThat(activity.findViewById(R.id.name).getVisibility()) .isEqualTo(View.VISIBLE);assertThat(((TextView) activity.findViewById(R.id.name)) .getText()).isEqualTo("Charizard");
TEST REPORT
• will be generated in:/app/build/reports/tests/{flavour}/index.html
• if tests faill, gradle will print out the location of the report in
console as well
USING MOCK WEB SERVER
• like in unit tests (steps 1 - 4):
• prepare and inject MockWebServer in @Before method
(called before every @Test method)
• stop MockWebServer in @After method (called after every
@Test method)
• note that in instrument tests there is no Test application that
you can use
RESOURCES
• http://robolectric.org/
• https://github.com/square/assertj-android
• http://www.vogella.com/tutorials/
AndroidTestingEspresso/article.html
• http://developer.android.com/training/testing/ui-
testing/espresso-testing.html
Thank you!
Visit www.infinum.co or find us on social networks:
infinum.co infinumco infinumco infinum