Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unit testing

Unit testing

Introduction to Unit testing tools in Android

Dmytro Khmelenko

March 03, 2017
Tweet

More Decks by Dmytro Khmelenko

Other Decks in Programming

Transcript

  1. Unit testing is a software testing method by which individual

    units of source code, program modules are tested to determine whether they are fit for use 2
  2. JUnit is a simple framework to write repeatable tests •

    Annotations (@Test, @Ignore) • Assertions (assertEquals(), assertThat()) • Test Runners • Categories • … and more 3
  3. 4 @Test public void testLoginParsing(){ CoreUserResponse coreUserResponse = readCoreUserDataFromFile("/user-v1.json"); assertNotNull(coreUserResponse.getCoreUser());

    assertNotNull(coreUserResponse.getAuth()); assertEquals("DAT", coreUserResponse.getCoreUser().getFirstName()); assertEquals("[email protected]", coreUserResponse.getCoreUser().getEmail()); assertFalse(coreUserResponse.getCoreUser().getEmailsAllowed()); }
  4. Its main goal is to improve test code readability and

    make maintenance of tests easier. • Extended Hamcrest matchers (esp. for arrays & iterables) • Extended comparisons • Collecting assertion errors • Testing Guava 5
  5. 6 @Test public void testParseClaim() { Claim claim = loadClaimFromFile(“/claims.json");

    assertThat(claim.productType()).isEqualTo("training-coach"); assertThat(claim.subscription()).isNotNull(); assertThat(claim.subscription().status()).isEqualTo("active"); }
  6. 7 @Test public void performedAtParsedCorrectly(){ List<LeaderboardItem> leaderboardItems = loadLeaderBoardFromFile(); assertThat(leaderboardItems).isNotNull()

    .hasSize(1); SavedTraining firstTraining = getLeaderFirstTraining(leaderboardItems.get(0)); SimpleDateFormat simpleDateFormat = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); assertThat(firstTraining.getPerformedAt()).isNotNull() .withDateFormat(simpleDateFormat) .isEqualTo("2014-07-21T13:10:00.389Z"); }
  7. A dependency is an object that can be used (a

    service). An injection is the passing of a dependency to a dependent object (a client) that would use it. 8
  8. 9 public class ReleaseCorePaymentManager implements CorePaymentManager { private final PaymentApi

    paymentApi; private final GooglePaymentManager googlePaymentManager; public ReleaseCorePaymentManager(PaymentApi paymentApi, GooglePaymentManager googlePaymentManager) { this.paymentApi = paymentApi; this.googlePaymentManager = googlePaymentManager; } }
  9. Mocking framework that allows the creation of double (mock) objects

    for unit testing. Main purpose is Test-Driven-Development (TDD) and Behavior-Driven-Development (BDD) 10
  10. • mock() – creates a mock object of the class.

    Default behavior of the class is do nothing, if no stub. • spy() – creates a real object of the class. Default behavior of the class is the call real method, if no stub. 11
  11. 12 public class TestReleaseCorePaymentManager { private ReleaseCorePaymentManager underTest; private GooglePaymentManager

    googlePaymentManager; private PaymentApi mockPaymentApi; @Before public void setUp(){ mockPaymentApi = mock(PaymentApi.class); googlePaymentManager = mock(GooglePaymentManager.class); underTest = new ReleaseCorePaymentManager(mockPaymentApi, googlePaymentManager); } }
  12. 13 @RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = BuildConfig.MIN_SDK_VERSION) public class

    TestGooglePaymentManager { private static final String TEST_PUBLIC_KEY = "TestPublicKey"; private GooglePaymentManager underTest; @Before public void setUp() throws Exception { Context context = ShadowApplication.getInstance().getApplicationContext(); underTest = spy(new GooglePaymentManager(context, TEST_PUBLIC_KEY)); }
  13. 14 @RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = BuildConfig.MIN_SDK_VERSION) public class

    TestGooglePaymentManager { private static final String TEST_PUBLIC_KEY = "TestPublicKey"; private IabHelper iabHelper; private GooglePaymentManager underTest; @Before public void setUp() throws Exception { Context context = ShadowApplication.getInstance().getApplicationContext(); underTest = spy(new GooglePaymentManager(context, TEST_PUBLIC_KEY)); iabHelper = mock(IabHelper.class); when(underTest.getIabHelper()).thenReturn(iabHelper); } }
  14. • thenReturn() – stubbing the response of non-void function of

    the mock. • thenThrow() – stubbing throwing exception on the call of the function. • doAnswer() – stubbing void methods of the mock. 15
  15. 16 @Test public void testGetInAppProductsSuccess() { List<Product> products = loadProductsFromFile();

    when(mockPaymentApi.getProducts()).thenReturn(products); Inventory inventory = loadInventoryFromFile(); when(googlePaymentManager.fetchInAppInventory()).thenReturn(inventory); InAppProductContainder inAppProducts = underTest.getInAppProductContainer(); // asserts… }
  16. 17 @Test public void testFetchInAppInventoryFailed() { IabResult exceptionResult = new

    IabResult(IabHelper.ERROR_FAILED, "error"); IabException exception = new IabException(exceptionResult); List<String> justProductIds = new ArrayList<>(); IabHelper helper = underTest.getIabHelper(); when(helper.queryInventory(true, justProductIds)).thenThrow(exception); underTest.fetchInAppInventory(justProductIds); // asserts, verification… }
  17. 18 public class IabHelper { public void startSetup(IabHelperListener listener) {

    // establishing connection with service listener.onIabSetupFinished(new IabResult(RESPONSE_RESULT_OK, "Success")); } } @Test public void testSetupSuccess() { final IabResult result = new IabResult(IabHelper.RESPONSE_RESULT_OK, "Success"); IabHelper helper = underTest.getIabHelper(); doAnswer(new Answer<Void>() { public Void answer(InvocationOnMock invocation) throws Throwable { ((IabHelperListener) invocation.getArguments()[0]).onSetupFinished(result); return null; } }).when(helper).startSetup(isA(IabHelperListener.class)); underTest.setup(); // asserts, verification… }
  18. • eq() – checks that the argument equals to object

    according to its equals() method. • isA() – checks that the argument is an instance of T, implying it is non-null. • any() – doesn’t do type checks with the given parameter, it is only there to avoid casting in your code. 19
  19. 20 @Test public void testUserLoginSuccess() { User user = getUserFromFile();

    when(mMockUserManager.login(eq(USERNAME), eq(PASSWORD))) .thenReturn(Observable.just(user)); // calls and verification } eq() checks that the argument equals to object according to its equals() method
  20. isA(T.class) checks that the argument is an instance of T,

    implying it is non-null. 21 @Test public void testGetInAppProductsSuccess() { List<Product> product = getProductsFromFile(); when(mockPaymentApi.getProducts(eq("en"), isA(BrandType.class))).thenReturn(product); // calls and verification }
  21. Any() doesn’t do type checks with the given parameter, it

    is only there to avoid casting in your code. This might however change (type checks could be added) in a future major release. 22 @Test public void testGetInAppProductsSuccess() throws Exception { List<Product> product = getProductsFromFile(); when(mockPaymentApi.getProducts(eq("en"), any(BrandType.class))).thenReturn(product); // calls and verification }
  22. • ArgumentCaptor – captures argument values for further assertions. •

    verify() – checks interactions on the mock object. 23
  23. 24 @Test public void testGetProductsSuccess() { ProductResponse response = loadProductResponseFromFile();

    when(apiService.getProducts(any(String.class)) .thenReturn(Observable.just(response)); ProductsRequest request = new ProductsRequest(BrandType.TRAINING, "en"); underTest.getProducts(request); ArgumentCaptor<String> brandType = ArgumentCaptor.forClass(String.class); verify(apiService).getProducts(brandType.capture()); assertThat(brandType.getValue()).isNotNull(); assertThat(brandType.getValue()).isEqualTo("training-coach"); }
  24. 25 @Test public void testPurchaseVerifyCoach() { Observable<Claim> claims = loadClaimsFromFile();

    when(mockPaymentApi.getClaims()).thenReturn(claims); underTest.purchase(BrandType.TRAINING, product); verify(mockPaymentApi, times(1)).getClaims(); verifyNoMoreInteractions(mockPaymentApi); }
  25. • You will want to test the observables, meaning not

    only the observables you built, but also the resulting composition of the various operators you may want to apply to them. • Given a certain observable (or its mock), you will want to test how the rest of your application behaves while triggered by the subscription. 26
  26. 28 @Test public void testPurchaseCoachExists() throws Exception { SkuDetails product

    = getProductFromFile(); Observable<Claim> claims = getDataFromFile(); when(mockPaymentApi.getClaims()).thenReturn(claims); Receipt receipt = getExpectedReceiptFromFile(); Observable<Receipt> purchaseObs = underTest.purchase(BrandType.TRAINING, product); TestSubscriber<Receipt> subscriber = new TestSubscriber<>(); purchaseObs.subscribe(subscriber); subscriber.assertValue(receipt); subscriber.assertCompleted(); }
  27. 29 @Test public void testPurchaseCoachExists() throws Exception { SkuDetails product

    = getProductFromFile(); Observable<Claim> claims = getDataFromFile(); when(mockPaymentApi.getClaims()).thenReturn(claims); Receipt receipt = getExpectedReceiptFromFile(); Observable<Receipt> purchaseObs = underTest.purchase(BrandType.TRAINING, product); TestSubscriber<Receipt> subscriber = new TestSubscriber<>(); purchaseObs.subscribe(subscriber); subscriber.assertValue(receipt); subscriber.assertCompleted(); Receipt actualReceipt = subscriber.getOnNextEvents().get(0); assertThat(actualReceipt.isPurchased()).isFalse(); }
  28. 30 @Test public void testGetInappProductFails() { PaymentException exception = new

    PaymentException( PaymentErrorCode.BACKEND_PRODUCTS_ARE_EMPTY); when(mockPaymentApi.getProducts("en", BrandType.TRAINING)) .thenReturn(Observable.error(exception); Observable<InAppProductContainer> inAppProducts = underTest.getInAppProductContainer(BrandType.TRAINING); TestSubscriber<InAppProductContainer> subscriber = new TestSubscriber<>(); inAppProducts.subscribe(subscriber); PaymentException actualEx = (PaymentException)subscriber.getOnErrorEvents().get(0); assertEquals(PaymentErrorCode.BACKEND_PRODUCTS_ARE_EMPTY, actualEx.getErrorCode()); subscriber.assertNotCompleted(); }