Android Testing
Sean Tsai
2017
JUnit
Mockito
PowerMock EasyMock
Robolectric
AssertJ
HamcrestTest
Espresso
What are all this…?
JUnit
● Unit testig framework for Java
● Stable Version - 4
● JUnit 4 - Still the standard version for Android
● Ports
○ C (CUnit)
○ JavaScript (JSUnit)
○ Objective-C (OCUnit)
○ Ruby (Test::Unit)
○ …
JUnit 5
● Released on September 10, 2017
● JUnit Platform + JUnit Jupiter + JUnit Vintage
● Requires Java 8 at runtime
● Support for lambda expressions to be used in assertions
● New Annotations:
○ @BeforeEach, @AfterEach (equivalent to the previous @Before and @After)
○ @BeforeAll, @AfterAll (equivalent to the previous @BeforeClass and @AfterClass)
○ @Disabled (equivalent to the previosu @Ignore)
● Assumptions
● Tagging
● Nested Tests
● Repeated Tests
● Dependency Injection for Constructors and Methods
● Parameterized Tests
JUnit Example
@Test
public void isVIP() throws Exception {
Customer customer = new Customer(1, "Sean");
customer.setLevel(11);
Assert.assertTrue(CustomerUtils.isVIP(customer));
}
EasyMock
import static org.easymock.classextension.EasyMock.*;
List mock = createNiceMock(List.class);
expect(mock.get(0)).andStubReturn("one");
expect(mock.get(1)).andStubReturn("two");
mock.clear();
replay(mock);
● Current version: 3.4 (2001 - 2015)
● 3.5 (2017)
● The framework allows the creation of test double objects for the purpose of TDD or BDD.
Mockito
● From 2008 - Now
● Based on EasyMock
● Current version: Mockito 2
○ Mock make engine: CGLIB -> ByteBuddy
○ Remove Hamcrest from dependacy.
○ Java 8 support.
○ anyX() and any(SomeType.class) matchers now reject nulls and check type
Ref: https://github.com/mockito/mockito
Mockito Example - Let's verify some behaviour!
public class MockitoTest {
@Test
public void test1_verify() {
//mock creation
List mockedList = mock(List.class);
//using mock object
mockedList.add("one");
mockedList.clear();
//verification
verify(mockedList).add("one");
verify(mockedList).clear();
}
}
Mockito Example - How about some stubbing?
@Test (expected = RuntimeException.class)
public void test2_stub() {
//You can mock concrete classes, not just interfaces
LinkedList mockedList = mock(LinkedList.class);
//stubbing
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
//following prints "first"
System.out.println(mockedList.get(0));
//following throws runtime exception
System.out.println(mockedList.get(1));
//following prints "null" because get(999) was not stubbed
System.out.println(mockedList.get(999));
verify(mockedList).get(0);
}
Mockito Example - Argument matchers
@Test
public void test3_argumentMatchers() {
//You can mock concrete classes, not just interfaces
LinkedList mockedList = mock(LinkedList.class);
//stubbing using built-in anyInt() argument matcher
when(mockedList.get(anyInt())).thenReturn("element");
//stubbing using custom matcher (let's say isValid() returns your own matcher implementation):
// when(mockedList.contains(argThat(isValid()))).thenReturn("element");
//following prints "element"
System.out.println(mockedList.get(999));
//you can also verify using an argument matcher
verify(mockedList).get(anyInt());
//argument matchers can also be written as Java 8 Lambdas
// verify(mockedList).add(argThat(someString -> someString.length() > 5));
}
Mockito Example - Verifying exact number of invocations
@Test
public void test4_verifyingExactNumberOfInvocations() {
//You can mock concrete classes, not just interfaces
LinkedList mockedList = mock(LinkedList.class);
//using mock
mockedList.add("once");
mockedList.add("twice");
mockedList.add("twice");
mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");
//following two verifications work exactly the same - times(1) is used by default
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
//exact number of invocations verification
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");
//verification using never(). never() is an alias to times(0)
verify(mockedList, never()).add("never happened");
//verification using atLeast()/atMost()
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("three times");
verify(mockedList, atMost(5)).add("three times");
Mockito Example - Stubbing void methods with
exceptions
@Test (expected = RuntimeException.class)
public void test5_StubbingVoidMethodsWithExceptions() {
//You can mock concrete classes, not just interfaces
LinkedList mockedList = mock(LinkedList.class);
doThrow(new RuntimeException()).when(mockedList).clear();
//following throws RuntimeException:
mockedList.clear();
}
Mockito Example - Verification in order
@Test
public void test6_verificationInOrder () {
// A. Single mock whose methods must be invoked in a particular order
List singleMock = mock(List.class);
//using a single mock
singleMock.add( "was added first" );
singleMock.add( "was added second" );
//create an inOrder verifier for a single mock
InOrder inOrder = inOrder(singleMock) ;
//following will make sure that add is first called with "was added first, then with "was added second"
inOrder.verify(singleMock).add( "was added first" );
inOrder.verify(singleMock).add( "was added second" );
// B. Multiple mocks that must be used in a particular order
List firstMock = mock(List.class);
List secondMock = mock(List.class);
//using mocks
firstMock.add( "was called first" );
secondMock.add( "was called second" );
//create inOrder object passing any mocks that need to be verified in order
InOrder inOrder2 = inOrder(firstMock , secondMock) ;
//following will make sure that firstMock was called before secondMock
inOrder2.verify(firstMock).add( "was called first" );
inOrder2.verify(secondMock).add( "was called second" );
}
There are a lot more useful methods...
Ref: http://static.javadoc.io/org.mockito/mockito-core/2.10.0/org/mockito/Mockito.html#verification
PowerMock
● Enable mocking of static methods, constructors, final classes and methods, private methods.
● Depends on Junit and Mockito.
● Support Mockito 2.8.x with PowerMock 1.7.0-1.7.1.
Ref:
1. https://github.com/powermock/powermock/wiki/Downloads
2. https://github.com/powermock/powermock/wiki/Mockito
PowerMock Example - Mock static
@RunWith(PowerMockRunner.class)
@PrepareForTest(CustomerUtils.class)
public class OrderTest {
private Customer customer;
@Before
public void setUp() throws Exception {
customer = new Customer(1, "Sean");
customer.setLevel(11);
}
@Test
public void getTotal() throws Exception {
PowerMockito.mockStatic(CustomerUtils.class);
Mockito.when(CustomerUtils.isVIP(any(Customer.class))).thenReturn(true);
Order order = new Order(1, customer);
order.setOriginalTotal(10);
Assert.assertEquals(8f, order.getTotal());
}
}
Roboletric
● A unit test framework that de-fangs the Android SDK jar so you can test-drive the development of
your Android app.
○ SDK, Resources, & Native Method Emulation
○ Run Tests Outside of the Emulator
○ No Mocking Frameworks Required
Roboletric Example
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
public class MainActivityTest {
@Test
public void test_textView() {
MainActivity activity = Robolectric.setupActivity(MainActivity.class);
TextView results = (TextView) activity.findViewById(R.id.text);
Assert.assertEquals("Hello World!", results.getText().toString());
}
}
Espresso
@Test
public void greeterSaysHello() {
onView(withId(R.id.name_field)).perform(typeText("Steve"));
onView(withId(R.id.greet_button)).perform(click());
onView(withText("Hello Steve!")).check(matches(isDisplayed()));
}
● Android UI tests.
Test Double
Dummy Object
public Customer createDummyCustomer() {
County county = new County("Essex");
City city = new City("Romford", county);
Address address = new Address("1234 Bank Street", city);
Customer customer = new Customer("john", "dobie", address);
return customer;
}
@Test
public void addCustomerTest() {
Customer dummy = createDummyCustomer();
AddressBook addressBook = new AddressBook();
addressBook.addCustomer(dummy);
assertEquals(1, addressBook.getNumberOfCustomers());
}
Dummy Object - Using Mockito
@Test
public void addCustomerWithDummyTest() {
Customer dummy = mock(Customer.class);
AddressBook addressBook = new AddressBook();
addressBook.addCustomer(dummy);
Assert.assertEquals(1, addressBook.getNumberOfCustomers());
}
Test stub - Responder
public class SimplePricingService implements PricingService {
PricingRepository repository;
public SimplePricingService(PricingRepository pricingRepository) {
this.repository = pricingRepository;
}
@Override
public Price priceTrade(Trade trade) {
return repository.getPriceForTrade(trade);
}
@Override
public Price getTotalPriceForTrades(Collection<trade> trades) {
Price totalPrice = new Price();
for (Trade trade : trades) {
Price tradePrice = repository.getPriceForTrade(trade);
totalPrice = totalPrice.add(tradePrice);
}
return totalPrice;
}
}
Test stub - Responder
@Test
public void testGetHighestPricedTrade() throws Exception {
Price price1 = new Price(10);
Price price2 = new Price(15);
Price price3 = new Price(25);
PricingRepository pricingRepository = mock(PricingRepository.class);
when(pricingRepository.getPriceForTrade(any(Trade.class)))
.thenReturn(price1, price2, price3);
PricingService service = new SimplePricingService(pricingRepository);
Price totalPrice = service.getTotalPricedTrade(getTrades());
assertEquals(50, totalPrice.getAmount());
}
Mock Object
@Mock
TradeRepository tradeRepository;
@Mock
AuditService auditService;
@Test
public void testAuditLogEntryMadeForNewTrade() throws Exception {
Trade trade = new Trade("Ref 1", "Description 1");
when(tradeRepository.createTrade(trade)).thenReturn(anyLong());
TradingService tradingService
= new SimpleTradingService(tradeRepository, auditService);
tradingService.createTrade(trade);
verify(auditService).logNewTrade(trade);
}
Test Spy
@Test
public void spy() {
List list = new LinkedList();
List spy = spy(list);
//optionally, you can stub out some methods:
when(spy.size()).thenReturn(100);
//using the spy calls *real* methods
spy.add("one");
spy.add("two");
//prints "one" - the first element of a list
System.out.println(spy.get(0));
//size() method was stubbed - 100 is printed
System.out.println(spy.size());
//optionally, you can verify
verify(spy).add("one");
verify(spy).add("two");
}
Fake Object
public void testReadWrite_inMemory() throws Exception{
// Setup:
FlightMgmtFacadeImpl facade = new FlightMgmtFacadeImpl();
facade.setDao(new InMemoryDatabase());
// Exercise:
AirPort yyc = facade.createAirport("YYC", "Calgary", "Calgary");
AirPort lax = facade.createAirport("LAX", "LAX Intl", "LA");
facade.createFlight(yyc, lax);
List flights = facade.getFlightsByOriginAirport(yyc);
// Verify:
assertEquals( "# of flights", 1, flights.size());
Flight flight = (Flight) flights.get(0);
assertEquals( "origin", "YYC", flight.getOrigin().getCode());
}
Fake Object
public class InMemoryDatabase implements FlightDao {
private List airports = new Vector();
public Airport createAirport(String airportCode, String name, String nearbyCity)
throws DataException, InvalidArgumentException {|-------10-------- 20-------- 30-------- 40-------- 50-------- 60----
assertParamtersAreValid(airportCode, name, nearbyCity);
assertAirportDoesntExist(airportCode);
Airport result = new Airport(getNextAirportId(),airportCode, name, createCity(nearbyCity));
airports.add(result);
return result;
}
public Airport getAirportByPrimaryKey(BigDecimal airportId)
throws DataException, InvalidArgumentException {
assertAirportNotNull(airportId);
Airport result = null;
Iterator i = airports.iterator();
while (i.hasNext()) {
Airport airport = (Airport) i.next();
if (airport.getId().equals(airportId)) {
return airport;
}
}
throw new DataException("Airport not found:" + airportId);
}
}
Q & A

Android testing

  • 1.
  • 2.
  • 3.
    What are allthis…?
  • 4.
    JUnit ● Unit testigframework for Java ● Stable Version - 4 ● JUnit 4 - Still the standard version for Android ● Ports ○ C (CUnit) ○ JavaScript (JSUnit) ○ Objective-C (OCUnit) ○ Ruby (Test::Unit) ○ …
  • 5.
    JUnit 5 ● Releasedon September 10, 2017 ● JUnit Platform + JUnit Jupiter + JUnit Vintage ● Requires Java 8 at runtime ● Support for lambda expressions to be used in assertions ● New Annotations: ○ @BeforeEach, @AfterEach (equivalent to the previous @Before and @After) ○ @BeforeAll, @AfterAll (equivalent to the previous @BeforeClass and @AfterClass) ○ @Disabled (equivalent to the previosu @Ignore) ● Assumptions ● Tagging ● Nested Tests ● Repeated Tests ● Dependency Injection for Constructors and Methods ● Parameterized Tests
  • 6.
    JUnit Example @Test public voidisVIP() throws Exception { Customer customer = new Customer(1, "Sean"); customer.setLevel(11); Assert.assertTrue(CustomerUtils.isVIP(customer)); }
  • 7.
    EasyMock import static org.easymock.classextension.EasyMock.*; Listmock = createNiceMock(List.class); expect(mock.get(0)).andStubReturn("one"); expect(mock.get(1)).andStubReturn("two"); mock.clear(); replay(mock); ● Current version: 3.4 (2001 - 2015) ● 3.5 (2017) ● The framework allows the creation of test double objects for the purpose of TDD or BDD.
  • 8.
    Mockito ● From 2008- Now ● Based on EasyMock ● Current version: Mockito 2 ○ Mock make engine: CGLIB -> ByteBuddy ○ Remove Hamcrest from dependacy. ○ Java 8 support. ○ anyX() and any(SomeType.class) matchers now reject nulls and check type Ref: https://github.com/mockito/mockito
  • 9.
    Mockito Example -Let's verify some behaviour! public class MockitoTest { @Test public void test1_verify() { //mock creation List mockedList = mock(List.class); //using mock object mockedList.add("one"); mockedList.clear(); //verification verify(mockedList).add("one"); verify(mockedList).clear(); } }
  • 10.
    Mockito Example -How about some stubbing? @Test (expected = RuntimeException.class) public void test2_stub() { //You can mock concrete classes, not just interfaces LinkedList mockedList = mock(LinkedList.class); //stubbing when(mockedList.get(0)).thenReturn("first"); when(mockedList.get(1)).thenThrow(new RuntimeException()); //following prints "first" System.out.println(mockedList.get(0)); //following throws runtime exception System.out.println(mockedList.get(1)); //following prints "null" because get(999) was not stubbed System.out.println(mockedList.get(999)); verify(mockedList).get(0); }
  • 11.
    Mockito Example -Argument matchers @Test public void test3_argumentMatchers() { //You can mock concrete classes, not just interfaces LinkedList mockedList = mock(LinkedList.class); //stubbing using built-in anyInt() argument matcher when(mockedList.get(anyInt())).thenReturn("element"); //stubbing using custom matcher (let's say isValid() returns your own matcher implementation): // when(mockedList.contains(argThat(isValid()))).thenReturn("element"); //following prints "element" System.out.println(mockedList.get(999)); //you can also verify using an argument matcher verify(mockedList).get(anyInt()); //argument matchers can also be written as Java 8 Lambdas // verify(mockedList).add(argThat(someString -> someString.length() > 5)); }
  • 12.
    Mockito Example -Verifying exact number of invocations @Test public void test4_verifyingExactNumberOfInvocations() { //You can mock concrete classes, not just interfaces LinkedList mockedList = mock(LinkedList.class); //using mock mockedList.add("once"); mockedList.add("twice"); mockedList.add("twice"); mockedList.add("three times"); mockedList.add("three times"); mockedList.add("three times"); //following two verifications work exactly the same - times(1) is used by default verify(mockedList).add("once"); verify(mockedList, times(1)).add("once"); //exact number of invocations verification verify(mockedList, times(2)).add("twice"); verify(mockedList, times(3)).add("three times"); //verification using never(). never() is an alias to times(0) verify(mockedList, never()).add("never happened"); //verification using atLeast()/atMost() verify(mockedList, atLeastOnce()).add("three times"); verify(mockedList, atLeast(2)).add("three times"); verify(mockedList, atMost(5)).add("three times");
  • 13.
    Mockito Example -Stubbing void methods with exceptions @Test (expected = RuntimeException.class) public void test5_StubbingVoidMethodsWithExceptions() { //You can mock concrete classes, not just interfaces LinkedList mockedList = mock(LinkedList.class); doThrow(new RuntimeException()).when(mockedList).clear(); //following throws RuntimeException: mockedList.clear(); }
  • 14.
    Mockito Example -Verification in order @Test public void test6_verificationInOrder () { // A. Single mock whose methods must be invoked in a particular order List singleMock = mock(List.class); //using a single mock singleMock.add( "was added first" ); singleMock.add( "was added second" ); //create an inOrder verifier for a single mock InOrder inOrder = inOrder(singleMock) ; //following will make sure that add is first called with "was added first, then with "was added second" inOrder.verify(singleMock).add( "was added first" ); inOrder.verify(singleMock).add( "was added second" ); // B. Multiple mocks that must be used in a particular order List firstMock = mock(List.class); List secondMock = mock(List.class); //using mocks firstMock.add( "was called first" ); secondMock.add( "was called second" ); //create inOrder object passing any mocks that need to be verified in order InOrder inOrder2 = inOrder(firstMock , secondMock) ; //following will make sure that firstMock was called before secondMock inOrder2.verify(firstMock).add( "was called first" ); inOrder2.verify(secondMock).add( "was called second" ); }
  • 15.
    There are alot more useful methods... Ref: http://static.javadoc.io/org.mockito/mockito-core/2.10.0/org/mockito/Mockito.html#verification
  • 16.
    PowerMock ● Enable mockingof static methods, constructors, final classes and methods, private methods. ● Depends on Junit and Mockito. ● Support Mockito 2.8.x with PowerMock 1.7.0-1.7.1. Ref: 1. https://github.com/powermock/powermock/wiki/Downloads 2. https://github.com/powermock/powermock/wiki/Mockito
  • 17.
    PowerMock Example -Mock static @RunWith(PowerMockRunner.class) @PrepareForTest(CustomerUtils.class) public class OrderTest { private Customer customer; @Before public void setUp() throws Exception { customer = new Customer(1, "Sean"); customer.setLevel(11); } @Test public void getTotal() throws Exception { PowerMockito.mockStatic(CustomerUtils.class); Mockito.when(CustomerUtils.isVIP(any(Customer.class))).thenReturn(true); Order order = new Order(1, customer); order.setOriginalTotal(10); Assert.assertEquals(8f, order.getTotal()); } }
  • 18.
    Roboletric ● A unittest framework that de-fangs the Android SDK jar so you can test-drive the development of your Android app. ○ SDK, Resources, & Native Method Emulation ○ Run Tests Outside of the Emulator ○ No Mocking Frameworks Required
  • 19.
    Roboletric Example @RunWith(RobolectricTestRunner.class) @Config(constants =BuildConfig.class) public class MainActivityTest { @Test public void test_textView() { MainActivity activity = Robolectric.setupActivity(MainActivity.class); TextView results = (TextView) activity.findViewById(R.id.text); Assert.assertEquals("Hello World!", results.getText().toString()); } }
  • 20.
    Espresso @Test public void greeterSaysHello(){ onView(withId(R.id.name_field)).perform(typeText("Steve")); onView(withId(R.id.greet_button)).perform(click()); onView(withText("Hello Steve!")).check(matches(isDisplayed())); } ● Android UI tests.
  • 21.
  • 22.
    Dummy Object public CustomercreateDummyCustomer() { County county = new County("Essex"); City city = new City("Romford", county); Address address = new Address("1234 Bank Street", city); Customer customer = new Customer("john", "dobie", address); return customer; } @Test public void addCustomerTest() { Customer dummy = createDummyCustomer(); AddressBook addressBook = new AddressBook(); addressBook.addCustomer(dummy); assertEquals(1, addressBook.getNumberOfCustomers()); }
  • 23.
    Dummy Object -Using Mockito @Test public void addCustomerWithDummyTest() { Customer dummy = mock(Customer.class); AddressBook addressBook = new AddressBook(); addressBook.addCustomer(dummy); Assert.assertEquals(1, addressBook.getNumberOfCustomers()); }
  • 24.
    Test stub -Responder public class SimplePricingService implements PricingService { PricingRepository repository; public SimplePricingService(PricingRepository pricingRepository) { this.repository = pricingRepository; } @Override public Price priceTrade(Trade trade) { return repository.getPriceForTrade(trade); } @Override public Price getTotalPriceForTrades(Collection<trade> trades) { Price totalPrice = new Price(); for (Trade trade : trades) { Price tradePrice = repository.getPriceForTrade(trade); totalPrice = totalPrice.add(tradePrice); } return totalPrice; } }
  • 25.
    Test stub -Responder @Test public void testGetHighestPricedTrade() throws Exception { Price price1 = new Price(10); Price price2 = new Price(15); Price price3 = new Price(25); PricingRepository pricingRepository = mock(PricingRepository.class); when(pricingRepository.getPriceForTrade(any(Trade.class))) .thenReturn(price1, price2, price3); PricingService service = new SimplePricingService(pricingRepository); Price totalPrice = service.getTotalPricedTrade(getTrades()); assertEquals(50, totalPrice.getAmount()); }
  • 26.
    Mock Object @Mock TradeRepository tradeRepository; @Mock AuditServiceauditService; @Test public void testAuditLogEntryMadeForNewTrade() throws Exception { Trade trade = new Trade("Ref 1", "Description 1"); when(tradeRepository.createTrade(trade)).thenReturn(anyLong()); TradingService tradingService = new SimpleTradingService(tradeRepository, auditService); tradingService.createTrade(trade); verify(auditService).logNewTrade(trade); }
  • 27.
    Test Spy @Test public voidspy() { List list = new LinkedList(); List spy = spy(list); //optionally, you can stub out some methods: when(spy.size()).thenReturn(100); //using the spy calls *real* methods spy.add("one"); spy.add("two"); //prints "one" - the first element of a list System.out.println(spy.get(0)); //size() method was stubbed - 100 is printed System.out.println(spy.size()); //optionally, you can verify verify(spy).add("one"); verify(spy).add("two"); }
  • 28.
    Fake Object public voidtestReadWrite_inMemory() throws Exception{ // Setup: FlightMgmtFacadeImpl facade = new FlightMgmtFacadeImpl(); facade.setDao(new InMemoryDatabase()); // Exercise: AirPort yyc = facade.createAirport("YYC", "Calgary", "Calgary"); AirPort lax = facade.createAirport("LAX", "LAX Intl", "LA"); facade.createFlight(yyc, lax); List flights = facade.getFlightsByOriginAirport(yyc); // Verify: assertEquals( "# of flights", 1, flights.size()); Flight flight = (Flight) flights.get(0); assertEquals( "origin", "YYC", flight.getOrigin().getCode()); }
  • 29.
    Fake Object public classInMemoryDatabase implements FlightDao { private List airports = new Vector(); public Airport createAirport(String airportCode, String name, String nearbyCity) throws DataException, InvalidArgumentException {|-------10-------- 20-------- 30-------- 40-------- 50-------- 60---- assertParamtersAreValid(airportCode, name, nearbyCity); assertAirportDoesntExist(airportCode); Airport result = new Airport(getNextAirportId(),airportCode, name, createCity(nearbyCity)); airports.add(result); return result; } public Airport getAirportByPrimaryKey(BigDecimal airportId) throws DataException, InvalidArgumentException { assertAirportNotNull(airportId); Airport result = null; Iterator i = airports.iterator(); while (i.hasNext()) { Airport airport = (Airport) i.next(); if (airport.getId().equals(airportId)) { return airport; } } throw new DataException("Airport not found:" + airportId); } }
  • 30.