SlideShare a Scribd company logo
Testing Android apps based on
Dagger and RxJava
Fabio Collini
droidcon Italy April 2017
2
Ego slide
@fabioCollini
linkedin.com/in/fabiocollini
github.com/fabioCollini
medium.com/@fabioCollini
codingjam.it
3
Agenda
1. Dagger
2. Testing
3. Mockito
4. Espresso
5. DaggerMock
6. RxJava
4
github.com/fabioCollini/TestingDaggerRxJava
5
Activity
Presenter
Interactor
Retrofit
Service
Rx
Rx
@Singleton
@Provides
@Singleton
@Provides
@Provides@Inject
public interface StackOverflowService {



@GET("/users") 

Single<List<User>> getTopUsers();



@GET("/users/{userId}/badges") 

Single<List<Badge>> getBadges(@Path("userId") int userId);

}
Activity
Presenter
Interactor
Retrofit
Service
Rx
Rx
SingletonSingleton
public class UserInteractor {

private StackOverflowService service;



public UserInteractor(StackOverflowService service) {

this.service = service;

}



public Single<List<UserStats>> loadUsers() {

//...

}

}
Activity
Presenter
Interactor
Retrofit
Service
Rx
Rx
SingletonSingleton
public class UserListPresenter {b



private UserInteractor userInteractor;



private UserListActivity activity;



public UserListPresenter(UserInteractor userInteractor,
UserListActivity activity) {

this.userInteractor = userInteractor;

this.activity = activity;

}onCreate



public void reloadUserList() {

userInteractor
.loadUsers()

//...

.subscribe(

activity::updateText,

activity::showError

);

}showError

}end
Activity
Presenter
Interactor
Retrofit
Service
Rx
Rx
SingletonSingleton
public class UserListActivity extends AppCompatActivity {b



@Inject UserListPresenter presenter;



@Override

protected void onCreate(Bundle savedInstanceState) {
//...
((MyApp) getApplicationContext()).getComponent()

.userListComponent(new UserListModule(this)).inject(this);

presenter.reloadUserList();

}onCreate



public void updateText(String s) {
//...
}updateText



public void showError(Throwable t) {
//...
}showError

}end
Activity
Presenter
Interactor
Retrofit
Service
Rx
Rx
SingletonSingleton
10
@Singleton

@Component(modules = {UserInteractorModule.class, StackOverflowServiceModule.class})

public interface ApplicationComponent {

UserListComponent userListComponent(UserListModule module);

}
@Module

public class UserInteractorModule {

@Provides @Singleton
public UserInteractor provideInteractor() {

//...

}

} @Module

public class StackOverflowServiceModule {

@Provides @Singleton
public StackOverflowService provideService() {

//...

}

}
@Subcomponent(modules = UserListModule.class)

public interface UserListComponent {

void inject(UserListActivity activity);

}
@Module

public class UserListModule {

@Provides
public UserListPresenter providePresenter() {

//...

}

}
Activity
Presenter
Interactor
Retrofit
Service
UnitUnitUnit
Integration
UI
UI
UI
E2E
Espresso JVM
12
Return Of Investment
Net profit
Investment
13
14
15
16
Unit
E2E
Integration
Manual
tests
Testing pyramid
17
Integrated tests are a scam
a self-replicating virus that threatens to infect your
code base, your project, and your team with endless
pain and suffering.
J. B. Rainsberger
18
Mockito tips
public class MyTest {



Collaborator1 collaborator1;



Collaborator2 collaborator2;



ObjectUnderTest objectUnderTest;



@Before

public void setUp() {

collaborator1 = Mockito.mock(Collaborator1.class);

collaborator2 = Mockito.mock(Collaborator2.class);

objectUnderTest = new ObjectUnderTest(
collaborator1, collaborator2);

}setUp



@Test

public void myTestMethod() {

//Arrange

when(collaborator1.provideValue()).thenReturn(2);

//Act

objectUnderTest.execute();

//Assert

verify(collaborator2).printValue(10);

assertThat(objectUnderTest.getValue()).isEqualTo(10);

}_

}__
public class MyTest {



@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();



@Mock Collaborator1 collaborator1;



@Mock Collaborator2 collaborator2;



@InjectMocks ObjectUnderTest objectUnderTest;



@Test

public void myTestMethod() {

//Arrange

when(collaborator1.provideValue()).thenReturn(2);

//Act

objectUnderTest.execute();

//Assert

verify(collaborator2).printValue(10);

assertThat(objectUnderTest.getValue()).isEqualTo(10);

}_

}__
package org.mockito.configuration;

//...
public class MockitoConfiguration extends DefaultMockitoConfiguration {

public Answer<Object> getDefaultAnswer() {

return new ReturnsEmptyValues() {

@Override

public Object answer(InvocationOnMock inv) {

Class<?> type = inv.getMethod().getReturnType();

if (type.isAssignableFrom(Observable.class)) {

return Observable.error(createException(inv));

} else if (type.isAssignableFrom(Single.class)) {

return Single.error(createException(inv));

} else {

return super.answer(inv);

}

}

};

}



private RuntimeException createException(

InvocationOnMock invocation) {

String s = invocation.toString();

return new RuntimeException(

"No mock defined for invocation " + s);

}

}
Espresso
Activity
Presenter
Interactor
Retrofit
Service
public class UserListPresenter {



private UserInteractor userInteractor;



private UserListActivity activity;



public UserListPresenter(UserInteractor userInteractor,
UserListActivity activity) {

this.userInteractor = userInteractor;

this.activity = activity;

}



public void reloadUserList() {

userInteractor

.loadUsers()

.flattenAsObservable(l -> l)

.map(UserStats::toString)

.reduce((s1, s2) -> s1 + "nn" + s2)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(

activity::updateText,

activity::showError

);

}

}
Activity
Presenter
Interactor
Retrofit
Service
@Singleton

@Component(modules = {

TestUserInteractorModule.class,

StackOverflowServiceModule.class

})

public interface TestApplicationComponent

extends ApplicationComponent {

void inject(UserListActivityTest userListActivityTest);

}
Activity
Presenter
Interactor
Retrofit
Service
@Module

public class TestUserInteractorModule {

@Provides @Singleton

public UserInteractor provideUserInteractor() {

return Mockito.mock(UserInteractor.class);

}

}
public class UserListActivityTest {

@Rule public ActivityTestRule<UserListActivity> rule =

new ActivityTestRule<>(UserListActivity.class, false, false);



@Inject UserInteractor userInteractor;



@Before public void setUp() {

TestApplicationComponent component =

DaggerTestApplicationComponent.create();

getAppFromInstrumentation().setComponent(component);

component.inject(this);

}A



@Test public void shouldDisplayUsers() {

when(userInteractor.loadUsers()).thenReturn(

Observable.fromArray(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

).toList());



rule.launchActivity(null);



onView(withId(R.id.text)).check(matches(withText(

"50 user1 - badge1nn30 user2 - badge2, badge3")));

}B

}C
Activity
Presenter
Interactor
Retrofit
Service
public class UserListActivityTest {

@Rule public ActivityTestRule<UserListActivity> rule =

new ActivityTestRule<>(UserListActivity.class, false, false);



@Inject UserInteractor userInteractor;



@Before public void setUp() {

TestApplicationComponent component =

DaggerTestApplicationComponent.create();

getAppFromInstrumentation().setComponent(component);

component.inject(this);

}A



@Test public void shouldDisplayUsers() {

when(userInteractor.loadUsers()).thenReturn(

Observable.fromArray(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

).toList());



rule.launchActivity(null);



onView(withId(R.id.text)).check(matches(withText(

"50 user1 - badge1nn30 user2 - badge2, badge3")));

}B

}C
Activity
Presenter
Interactor
Retrofit
Service
ArrangeActAssert
Activity
Presenter
Interactor
Retrofit
Service
public class UserListPresenter {



private UserInteractor userInteractor;



private UserListActivity activity;



public UserListPresenter(UserInteractor userInteractor,
UserListActivity activity) {

this.userInteractor = userInteractor;

this.activity = activity;

}___



public void reloadUserList() {

userInteractor

.loadUsers()

.flattenAsObservable(l -> l)

.map(UserStats::toString)

.reduce((s1, s2) -> s1 + "nn" + s2)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(

activity::updateText,

activity::showError

);

}__

}_
Scheduler asyncTaskScheduler =

Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR);
public class AsyncTaskSchedulerRule implements TestRule {



private final Scheduler asyncTaskScheduler =

Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR);



@Override

public Statement apply(final Statement base, Description d) {

return new Statement() {

@Override

public void evaluate() throws Throwable {

RxJavaPlugins.setIoSchedulerHandler(

scheduler -> asyncTaskScheduler);

RxJavaPlugins.setComputationSchedulerHandler(

scheduler -> asyncTaskScheduler);

RxJavaPlugins.setNewThreadSchedulerHandler(

scheduler -> asyncTaskScheduler);



try {

base.evaluate();

} finally {

RxJavaPlugins.reset();

}

}

};

}

}
public class UserListActivityTest {

@Rule public ActivityTestRule<UserListActivity> rule =

new ActivityTestRule<>(UserListActivity.class, false, false);

@Rule public AsyncTaskSchedulerRule asyncTaskSchedulerRule =
new AsyncTaskSchedulerRule();


@Inject UserInteractor userInteractor;



@Before public void setUp() {

TestApplicationComponent component =

DaggerTestApplicationComponent.create();

getAppFromInstrumentation().setComponent(component);

component.inject(this);

}A



@Test public void shouldDisplayUsers() {

when(userInteractor.loadUsers()).thenReturn(

Observable.fromArray(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

).toList());



rule.launchActivity(null);



onView(withId(R.id.text)).check(matches(withText(

"50 user1 - badge1nn30 user2 - badge2, badge3")));

}B

}C
Activity
Presenter
Interactor
Retrofit
Service
ArrangeActAssert
public class UserListActivityTest {

@Rule public ActivityTestRule<UserListActivity> rule =

new ActivityTestRule<>(UserListActivity.class, false, false);

@Rule public AsyncTaskSchedulerRule asyncTaskSchedulerRule =
new AsyncTaskSchedulerRule();


@Inject UserInteractor userInteractor;



@Before public void setUp() {

TestApplicationComponent component =

DaggerTestApplicationComponent.create();

getAppFromInstrumentation().setComponent(component);

component.inject(this);

}A



@Test public void shouldDisplayUsers() {

when(userInteractor.loadUsers()).thenReturn(

Observable.fromArray(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

).toList());



rule.launchActivity(null);



onView(withId(R.id.text)).check(matches(withText(

"50 user1 - badge1nn30 user2 - badge2, badge3")));

}B

}C
Activity
Presenter
Interactor
Retrofit
Service
ArrangeActAssert
@Singleton

@Component(modules = {

TestUserInteractorModule.class,

StackOverflowServiceModule.class

})

public interface TestApplicationComponent

extends ApplicationComponent {

void inject(UserListActivityTest userListActivityTest);

}_
Activity
Presenter
Interactor
Retrofit
Service
@Module

public class TestUserInteractorModule {

@Provides @Singleton

public UserInteractor provideUserInteractor() {

return Mockito.mock(UserInteractor.class);

}

}
//...

@Inject UserInteractor userInteractor;



@Before public void setUp() {

TestApplicationComponent component =

DaggerTestApplicationComponent.create();

getAppFromInstrumentation().setComponent(component);

component.inject(this);

}__
//...
Activity
Presenter
Interactor
Retrofit
Service
//...
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();



@Mock UserInteractor userInteractor;



@Before public void setUp() {

ApplicationComponent component =

DaggerApplicationComponent.builder()

.userInteractorModule(new UserInteractorModule() {

@Override

public UserInteractor provideUserInteractor(

StackOverflowService service) {

return userInteractor;

}___

})

.build();

getAppFromInstrumentation().setComponent(component);

}__
//...
34
//...
@Rule
public DaggerMockRule<ApplicationComponent> daggerMockRule =

new DaggerMockRule<>(

ApplicationComponent.class,

new UserInteractorModule()

).set(component ->

getAppFromInstrumentation().setComponent(component));



@Mock UserInteractor userInteractor;
//...
Activity
Presenter
Interactor
Retrofit
Service
Activity
Presenter
Interactor
Retrofit
Service
public class UserListActivityTest {

@Rule public ActivityTestRule<UserListActivity> rule =

new ActivityTestRule<>(UserListActivity.class, false, false);



@Rule public AsyncTaskSchedulerRule asyncTaskSchedulerRule =
new AsyncTaskSchedulerRule();


@Rule
public DaggerMockRule<ApplicationComponent> daggerMockRule =

new DaggerMockRule<>(

ApplicationComponent.class,

new UserInteractorModule()

).set(component ->

getAppFromInstrumentation().setComponent(component));



@Mock UserInteractor userInteractor;



@Test public void shouldDisplayUsers() {

when(userInteractor.loadUsers()).thenReturn(

Observable.fromArray(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

).toList());



rule.launchActivity(null);



onView(withId(R.id.text)).check(matches(withText(

"50 user1 - badge1nn30 user2 - badge2, badge3")));

}

}
static <T> T createSubclass(final T module,
final Map<Class, Object> testFields) {

return (T) Mockito.mock(module.getClass(), new Answer() {

@Override public Object answer(InvocationOnMock invocation)
throws Throwable {

Method method = invocation.getMethod();

Object obj = testFields.get(method.getReturnType());

if (obj != null) {

return obj;

} else {

return method.invoke(module, invocation.getArguments());

}

}

});

}
DaggerMock internals
Activity
Presenter
Interactor
Retrofit
Service
@Rule
public DaggerMockRule<ApplicationComponent> daggerMockRule =

new DaggerMockRule<>(

ApplicationComponent.class,

new UserInteractorModule()

).set(component ->

getAppFromInstrumentation().setComponent(component));
Activity
Presenter
Interactor
Retrofit
Service
public class MyDaggerMockRule

extends DaggerMockRule<ApplicationComponent> {



public MyDaggerMockRule() {

super(

ApplicationComponent.class,

new UserInteractorModule()

);

set(component ->

getAppFromInstrumentation().setComponent(component));


providesMock(UserInteractor.class, userInteractor ->

when(userInteractor.loadUsers()).thenReturn(

Observable.fromArray(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

).toList())

);

}__

}_
Espresso
Activity
Presenter
Interactor
Retrofit
Service
41
public class MockPresenterTest {



@Rule public ActivityTestRule<UserListActivity> rule =

new ActivityTestRule<>(UserListActivity.class, false, false);



@Rule public MyDaggerMockRule daggerMockRule =

new MyDaggerMockRule();



@Mock UserListPresenter presenter;



@Test

public void testOnCreate() {

rule.launchActivity(null);



onView(withId(R.id.text)).check(matches(withText("")));



verify(presenter).reloadUserList();

}

}
Activity
Presenter
Interactor
Retrofit
Service
1.void method that uses
RxJava schedulers
2.method that returns a
synchronous RxJava object
3.method that returns an
asynchronous RxJava object
Testing RxJava code
Activity
Presenter
Interactor
Retrofit
Service
JVM
public class UserListPresenter {



private UserInteractor userInteractor;



private UserListActivity activity;



public UserListPresenter(UserInteractor userInteractor,
UserListActivity activity) {

this.userInteractor = userInteractor;

this.activity = activity;

}



public void reloadUserList() {

userInteractor

.loadUsers()

.flattenAsObservable(l -> l)

.map(UserStats::toString)

.reduce((s1, s2) -> s1 + "nn" + s2)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(

activity::updateText,

activity::showError

);

}_

}
Activity
Presenter
Interactor
Retrofit
Service
Activity
Presenter
Interactor
Retrofit
Service
public void reloadUserList() {

userInteractor

.loadUsers()

.flattenAsObservable(l -> l)

.map(UserStats::toString)

.reduce((s1, s2) -> s1 + "nn" + s2)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(

activity::updateText,

activity::showError

);

}_

public class UserListPresenterTest {



@Rule public MockitoRule mockitoRule =

MockitoJUnit.rule();



@Mock UserInteractor userInteractor;



@Mock UserListActivity activity;



@InjectMocks UserListPresenter presenter;



@Test

public void shouldLoadUsers() {

when(userInteractor.loadUsers()).thenReturn(

Observable.fromArray(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

).toList());

presenter.reloadUserList();

verify(activity).updateText(anyString());

}_

}__
Activity
Presenter
Interactor
Retrofit
Service
47
48
public class TrampolineSchedulerRule implements TestRule {



@Override

public Statement apply(final Statement base, Description d) {

return new Statement() {

@Override

public void evaluate() throws Throwable {

RxJavaPlugins.setIoSchedulerHandler(

scheduler -> Schedulers.trampoline());

RxJavaPlugins.setComputationSchedulerHandler(

scheduler -> Schedulers.trampoline());

RxJavaPlugins.setNewThreadSchedulerHandler(

scheduler -> Schedulers.trampoline());

RxAndroidPlugins.setInitMainThreadSchedulerHandler(

scheduler -> Schedulers.trampoline());



try {

base.evaluate();

} finally {

RxJavaPlugins.reset();

RxAndroidPlugins.reset();

}

}

};

}

}
Activity
Presenter
Interactor
Retrofit
Service
public class UserListPresenterTest {



@Rule public MockitoRule mockitoRule =

MockitoJUnit.rule();

@Rule public TrampolineSchedulerRule schedulerRule =

new TrampolineSchedulerRule();



@Mock UserInteractor userInteractor;



@Mock UserListActivity activity;



@InjectMocks UserListPresenter presenter;



@Test

public void shouldLoadUsers() {

when(userInteractor.loadUsers()).thenReturn(

Observable.fromArray(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

).toList());



presenter.reloadUserList();



verify(activity).updateText(

"50 user1 - badge1nn30 user2 - badge2, badge3");

}__

}_
Activity
Presenter
Interactor
Retrofit
Service
public class UserListPresenterTest {



@Rule public MockitoRule mockitoRule =

MockitoJUnit.rule();

@Rule public TrampolineSchedulerRule schedulerRule =

new TrampolineSchedulerRule();



@Mock UserInteractor userInteractor;



@Mock UserListActivity activity;



@InjectMocks UserListPresenter presenter;



@Test

public void shouldLoadUsers() {

when(userInteractor.loadUsers()).thenReturn(

Observable.fromArray(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

).toList());



presenter.reloadUserList();



verify(activity).updateText(

"50 user1 - badge1nn30 user2 - badge2, badge3");

}__

}_
Activity
Presenter
Interactor
Retrofit
Service
public class UserListPresenterTest {



@Rule public DaggerMockRule<ApplicationComponent> daggerMockRule =

new DaggerMockRule<>(ApplicationComponent.class,
new UserInteractorModule());



@Rule public TrampolineSchedulerRule schedulerRule = 

new TrampolineSchedulerRule();



@Mock UserInteractor userInteractor;



@Mock UserListActivity activity;



@InjectFromComponent(UserListActivity.class) 

UserListPresenter presenter;



@Test

public void shouldLoadUsers() {

when(userInteractor.loadUsers()).thenReturn(

Observable.fromArray(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

).toList());



presenter.reloadUserList();



verify(activity).updateText(

"50 user1 - badge1nn30 user2 - badge2, badge3");

}__

}_
1.void method that uses
RxJava schedulers
2.method that returns a
synchronous RxJava object
3.method that returns an
asynchronous RxJava object
Testing RxJava code
trampoline
scheduler
Activity
Presenter
Interactor
Retrofit
Service
JVM
Activity
Presenter
Interactor
Retrofit
Service
public class UserInteractor {

private StackOverflowService service;



public UserInteractor(StackOverflowService service) {

this.service = service;

}



public Single<List<UserStats>> loadUsers() {

return service.getTopUsers()

.flattenAsObservable(l -> l)

.take(5)

.flatMap(user ->

service.getBadges(user.id())

.map(badges -> UserStats.create(user, badges))

.toObservable()

)____

.toList();

}

}
56
public class UserInteractorTest {

@Rule public MockitoRule mockitoRule =

MockitoJUnit.rule();



@Mock StackOverflowService stackOverflowService;



@InjectMocks UserInteractor userInteractor;



@Test

public void shouldLoadUsers() {

when(stackOverflowService.getTopUsers()).thenReturn(

Observable.fromArray(

User.create(1, 50, "user1"),

User.create(2, 30, "user2")

).toList());



when(stackOverflowService.getBadges(1)).thenReturn(

Single.just(Badge.createList("badge1")));

when(stackOverflowService.getBadges(2)).thenReturn(

Single.just(Badge.createList("badge2", "badge3")));

List<UserStats> users = userInteractor.loadUsers().blockingGet();



assertThat(users).containsExactly(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

);
}__
}_
Activity
Presenter
Interactor
Retrofit
Service
1.void method that uses
RxJava schedulers
2.method that returns a
synchronous RxJava object
3.method that returns an
asynchronous RxJava object
Testing RxJava code
trampoline
scheduler
blockingGet
Activity
Presenter
Interactor
Retrofit
Service
public Single<List<UserStats>> loadUsers() {

return service.getTopUsers()

.flattenAsObservable(l -> l)

.take(5)

.flatMap(user ->

service.getBadges(user.id())

.map(badges -> UserStats.create(user, badges))

.toObservable()

)____

.toList();

}_
Activity
Presenter
Interactor
Retrofit
Service
public Single<List<UserStats>> loadUsers() {

return service.getTopUsers()

.flattenAsObservable(l -> l)

.take(5)

.flatMap(user ->

service.getBadges(user.id())
.subscribeOn(Schedulers.io())

.map(badges -> UserStats.create(user, badges))

.toObservable()

)____

.toList();

}_
Activity
Presenter
Interactor
Retrofit
Service
public class UserInteractorTest {

@Rule public MockitoRule mockitoRule =

MockitoJUnit.rule();



@Mock StackOverflowService stackOverflowService;



@InjectMocks UserInteractor userInteractor;



@Test

public void shouldLoadUsers() {

when(stackOverflowService.getTopUsers()).thenReturn(

Observable.fromArray(

User.create(1, 50, "user1"),

User.create(2, 30, "user2")

).toList());



when(stackOverflowService.getBadges(1)).thenReturn(

Single.just(Badge.createList("badge1")));

when(stackOverflowService.getBadges(2)).thenReturn(

Single.just(Badge.createList("badge2", "badge3")));

List<UserStats> users = userInteractor.loadUsers().blockingGet();



assertThat(users).containsExactly(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

);
}__
}_
Activity
Presenter
Interactor
Retrofit
Service
public class UserInteractorTest {

@Rule public MockitoRule mockitoRule =

MockitoJUnit.rule();



@Mock StackOverflowService stackOverflowService;



@InjectMocks UserInteractor userInteractor;



@Test

public void shouldLoadUsers() {

when(stackOverflowService.getTopUsers()).thenReturn(

Observable.fromArray(

User.create(1, 50, "user1"),

User.create(2, 30, "user2")

).toList());



when(stackOverflowService.getBadges(1)).thenReturn(

Single.just(Badge.createList("badge1")));

when(stackOverflowService.getBadges(2)).thenReturn(

Single.just(Badge.createList("badge2", "badge3")));

List<UserStats> users = userInteractor.loadUsers().blockingGet();



assertThat(users).containsExactly(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

);
}__
}_
Activity
Presenter
Interactor
Retrofit
Service
public class UserInteractorTest {

@Rule public MockitoRule mockitoRule =

MockitoJUnit.rule();

@Rule public TrampolineSchedulerRule schedulerRule =

new TrampolineSchedulerRule();



@Mock StackOverflowService stackOverflowService;



@InjectMocks UserInteractor userInteractor;



@Test

public void shouldLoadUsers() {

when(stackOverflowService.getTopUsers()).thenReturn(

Observable.fromArray(

User.create(1, 50, "user1"),

User.create(2, 30, "user2")

).toList());



when(stackOverflowService.getBadges(1)).thenReturn(

Single.just(Badge.createList("badge1")));

when(stackOverflowService.getBadges(2)).thenReturn(

Single.just(Badge.createList("badge2", "badge3")));

List<UserStats> users = userInteractor.loadUsers().blockingGet();



assertThat(users).containsExactly(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

);
}__
}_
Activity
Presenter
Interactor
Retrofit
Service
when(stackOverflowService.getTopUsers()).thenReturn(

Observable.fromArray(

User.create(1, 50, "user1"),

User.create(2, 30, "user2")

).toList());



when(stackOverflowService.getBadges(1)).thenReturn(

Single.just(Badge.createList("badge1"))

.delay(2, TimeUnit.SECONDS));

when(stackOverflowService.getBadges(2)).thenReturn(

Single.just(Badge.createList("badge2", "badge3"))

.delay(1, TimeUnit.SECONDS));



userInteractor.loadUsers()

.test()

.assertNoErrors()

.assertValue(check(users ->

assertThat(users).containsExactly(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

)_

));
Activity
Presenter
Interactor
Retrofit
Service
when(stackOverflowService.getTopUsers()).thenReturn(

Observable.fromArray(

User.create(1, 50, "user1"),

User.create(2, 30, "user2")

).toList());



when(stackOverflowService.getBadges(1)).thenReturn(

Single.just(Badge.createList("badge1"))

.delay(2, TimeUnit.SECONDS));

when(stackOverflowService.getBadges(2)).thenReturn(

Single.just(Badge.createList("badge2", "badge3"))

.delay(1, TimeUnit.SECONDS));



userInteractor.loadUsers()

.test()

.assertNoErrors()

.assertValue(check(users ->

assertThat(users).containsExactly(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

)_

));
Activity
Presenter
Interactor
Retrofit
Service
when(stackOverflowService.getTopUsers()).thenReturn(

Observable.fromArray(

User.create(1, 50, "user1"),

User.create(2, 30, "user2")

).toList());



when(stackOverflowService.getBadges(1)).thenReturn(

Single.just(Badge.createList("badge1"))

.delay(2, TimeUnit.SECONDS));

when(stackOverflowService.getBadges(2)).thenReturn(

Single.just(Badge.createList("badge2", "badge3"))

.delay(1, TimeUnit.SECONDS));



userInteractor.loadUsers()

.test()

.assertNoErrors()

.assertValue(check(users ->

assertThat(users).containsExactly(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

)_

));
public class TestSchedulerRule implements TestRule {

private final TestScheduler testScheduler = new TestScheduler();



public TestScheduler getTestScheduler() {

return testScheduler;

}



@Override

public Statement apply(final Statement base, Description d) {

return new Statement() {

@Override

public void evaluate() throws Throwable {

RxJavaPlugins.setIoSchedulerHandler(

scheduler -> testScheduler);

RxJavaPlugins.setComputationSchedulerHandler(

scheduler -> testScheduler);

RxJavaPlugins.setNewThreadSchedulerHandler(

scheduler -> testScheduler);

RxAndroidPlugins.setMainThreadSchedulerHandler(

scheduler -> Schedulers.trampoline());



try {

base.evaluate();

} finally {

RxJavaPlugins.reset();

RxAndroidPlugins.reset();

}

}

};

}

}
Activity
Presenter
Interactor
Retrofit
Service
when(stackOverflowService.getTopUsers()).thenReturn(

Observable.fromArray(

User.create(1, 50, "user1"),

User.create(2, 30, "user2")

).toList());



when(stackOverflowService.getBadges(1)).thenReturn(

Single.just(Badge.createList("badge1"))

.delay(2, TimeUnit.SECONDS));

when(stackOverflowService.getBadges(2)).thenReturn(

Single.just(Badge.createList("badge2", "badge3"))

.delay(1, TimeUnit.SECONDS));



TestObserver<List<UserStats>> testObserver =

userInteractor.loadUsers().test();



schedulerRule.getTestScheduler()

.advanceTimeBy(2, TimeUnit.SECONDS);



testObserver

.assertNoErrors()

.assertValue(check(users ->

assertThat(users).containsExactly(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

)_

));
when(stackOverflowService.getTopUsers()).thenReturn(

Observable.fromArray(

User.create(1, 50, "user1"),

User.create(2, 30, "user2")

).toList());



when(stackOverflowService.getBadges(1)).thenReturn(

Single.just(Badge.createList("badge1"))

.delay(2, TimeUnit.SECONDS));

when(stackOverflowService.getBadges(2)).thenReturn(

Single.just(Badge.createList("badge2", "badge3"))

.delay(1, TimeUnit.SECONDS));



TestObserver<List<UserStats>> testObserver =

userInteractor.loadUsers().test();



schedulerRule.getTestScheduler()

.advanceTimeBy(2, TimeUnit.SECONDS);



testObserver

.assertNoErrors()

.assertValue(check(users ->

assertThat(users).containsExactly(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

)_

));
Activity
Presenter
Interactor
Retrofit
Service
Activity
Presenter
Interactor
Retrofit
Service
public class UserInteractor {

private StackOverflowService service;



public UserInteractor(StackOverflowService service) {

this.service = service;

}___



public Single<List<UserStats>> loadUsers() {

return service.getTopUsers()

.flattenAsObservable(l -> l)

.take(5)

.flatMap(user ->

service.getBadges(user.id())

.subscribeOn(Schedulers.io())

.map(badges -> UserStats.create(user, badges))

.toObservable()

)____

.toList();

}_

}__
Activity
Presenter
Interactor
Retrofit
Service
public class UserInteractor {

private StackOverflowService service;



public UserInteractor(StackOverflowService service) {

this.service = service;

}___



public Single<List<UserStats>> loadUsers() {

return service.getTopUsers()

.flattenAsObservable(l -> l)

.take(5)

.flatMap(user ->

service.getBadges(user.id())

.subscribeOn(Schedulers.io())

.map(badges -> UserStats.create(user, badges))

.toObservable()

)____

.toList();

}_

}__
public class UserInteractor {

private StackOverflowService service;



public UserInteractor(StackOverflowService service) {

this.service = service;

}___



public Single<List<UserStats>> loadUsers() {

return service.getTopUsers()

.flattenAsObservable(l -> l)

.take(5)

.flatMap(user ->

service.getBadges(user.id())

.subscribeOn(Schedulers.io())

.map(badges -> UserStats.create(user, badges))

.toObservable()

)____

.toList();

}_

}__
Activity
Presenter
Interactor
Retrofit
Service
Activity
Presenter
Interactor
Retrofit
Service
public class UserInteractor {

private StackOverflowService service;



public UserInteractor(StackOverflowService service) {

this.service = service;

}___



public Single<List<UserStats>> loadUsers() {

return service.getTopUsers()

.flattenAsObservable(l -> l)

.take(5)

.concatMapEager(user ->

service.getBadges(user.id())

.subscribeOn(Schedulers.io())

.map(badges -> UserStats.create(user, badges))

.toObservable()

)____

.toList();

}_

}__
Activity
Presenter
Interactor
Retrofit
Service
when(stackOverflowService.getTopUsers()).thenReturn(

Observable.fromArray(

User.create(1, 50, "user1"),

User.create(2, 30, "user2")

).toList());



when(stackOverflowService.getBadges(1)).thenReturn(

Single.just(Badge.createList("badge1"))

.delay(2, TimeUnit.SECONDS));

when(stackOverflowService.getBadges(2)).thenReturn(

Single.just(Badge.createList("badge2", "badge3"))

.delay(1, TimeUnit.SECONDS));



TestObserver<List<UserStats>> testObserver =

userInteractor.loadUsers().test();



schedulerRule.getTestScheduler()

.advanceTimeBy(2, TimeUnit.SECONDS);



testObserver

.assertNoErrors()

.assertValue(check(l ->

assertThat(l).containsExactly(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

)_

));
when(stackOverflowService.getTopUsers()).thenReturn(

Observable.fromArray(

User.create(1, 50, "user1"),

User.create(2, 30, "user2")

).toList());



when(stackOverflowService.getBadges(1)).thenReturn(

Single.just(Badge.createList("badge1"))

.delay(2, TimeUnit.SECONDS));

when(stackOverflowService.getBadges(2)).thenReturn(

Single.just(Badge.createList("badge2", "badge3"))

.delay(1, TimeUnit.SECONDS));



TestObserver<List<UserStats>> testObserver =

userInteractor.loadUsers().test();



schedulerRule.getTestScheduler()

.advanceTimeBy(2, TimeUnit.SECONDS);



testObserver

.assertNoErrors()

.assertValue(check(l ->

assertThat(l).containsExactly(

UserStats.create(1, 50, "user1", "badge1"),

UserStats.create(2, 30, "user2", "badge2", "badge3")

)_

));
Activity
Presenter
Interactor
Retrofit
Service
1.void method that uses
RxJava schedulers
2.method that returns a
synchronous RxJava object
3.method that returns an
asynchronous RxJava object
Testing RxJava code
trampoline
scheduler
blockingGet
TestScheduler
& TestObserver
77
Wrapping up
1.JVM tests isolate failures and are fast and
reliable
2.Using DaggerMock testing boilerplate code can
be reduced
3.RxJava asynchronous code can be tested using
TestObserver and TestScheduler
78
Links
github.com/fabioCollini/TestingDaggerRxJava
github.com/fabioCollini/DaggerMock
joel-costigliola.github.io/assertj
medium.com/@fabioCollini/testing-asynchronous-rxjava-code-using-
mockito-8ad831a16877
medium.com/@fabioCollini/android-testing-using-dagger-2-mockito-and-a-custom-junit-
rule-c8487ed01b56
testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html
blog.thecodewhisperer.com/permalink/clearing-up-the-integrated-tests-scam
collectiveidea.com/blog/archives/2016/10/13/retrofitting-espresso/
medium.com/@peter.tackage/an-alternative-to-rxandroidplugins-and-rxjavaplugins-
scheduler-injection-9831bbc3dfaf
artemzin.com/blog/jfyi-overriding-module-classes-with-dagger2/
Thanks for your attention!
Questions?

More Related Content

What's hot (20)

PDF
Automated%20testing%20with%20Espresso2.x
Tatsuya Maki
 
PDF
droidparts
Droidcon Berlin
 
PPTX
Dagger 2 - Injeção de Dependência
Edson Menegatti
 
PDF
Managing parallelism using coroutines
Fabio Collini
 
PDF
Kotlin Delegates in practice - Kotlin community conf
Fabio Collini
 
PDF
Modern Android app library stack
Tomáš Kypta
 
PDF
Kotlin delegates in practice - Kotlin Everywhere Stockholm
Fabio Collini
 
PDF
Using hilt in a modularized project
Fabio Collini
 
PPTX
Rx java in action
Pratama Nur Wijaya
 
PDF
Android programming -_pushing_the_limits
Droidcon Berlin
 
PDF
Java Quiz - Meetup
CodeOps Technologies LLP
 
PDF
Practical RxJava for Android
Tomáš Kypta
 
PDF
Under the Hood: Using Spring in Grails
Burt Beckwith
 
PDF
Under the Hood: Using Spring in Grails
GR8Conf
 
PDF
Why Kotlin - Apalon Kotlin Sprint Part 1
Kirill Rozov
 
PPTX
Android dev toolbox
Shem Magnezi
 
PDF
Practical RxJava for Android
Tomáš Kypta
 
PDF
My way to clean android v2 English DroidCon Spain
Christian Panadero
 
PDF
Testing Java Code Effectively
Andres Almiray
 
PDF
Solid principles in practice the clean architecture - Droidcon Italy
Fabio Collini
 
Automated%20testing%20with%20Espresso2.x
Tatsuya Maki
 
droidparts
Droidcon Berlin
 
Dagger 2 - Injeção de Dependência
Edson Menegatti
 
Managing parallelism using coroutines
Fabio Collini
 
Kotlin Delegates in practice - Kotlin community conf
Fabio Collini
 
Modern Android app library stack
Tomáš Kypta
 
Kotlin delegates in practice - Kotlin Everywhere Stockholm
Fabio Collini
 
Using hilt in a modularized project
Fabio Collini
 
Rx java in action
Pratama Nur Wijaya
 
Android programming -_pushing_the_limits
Droidcon Berlin
 
Java Quiz - Meetup
CodeOps Technologies LLP
 
Practical RxJava for Android
Tomáš Kypta
 
Under the Hood: Using Spring in Grails
Burt Beckwith
 
Under the Hood: Using Spring in Grails
GR8Conf
 
Why Kotlin - Apalon Kotlin Sprint Part 1
Kirill Rozov
 
Android dev toolbox
Shem Magnezi
 
Practical RxJava for Android
Tomáš Kypta
 
My way to clean android v2 English DroidCon Spain
Christian Panadero
 
Testing Java Code Effectively
Andres Almiray
 
Solid principles in practice the clean architecture - Droidcon Italy
Fabio Collini
 

More from Fabio Collini (16)

PDF
Using Dagger in a Clean Architecture project
Fabio Collini
 
PDF
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
Fabio Collini
 
PDF
SOLID principles in practice: the Clean Architecture
Fabio Collini
 
PDF
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
Fabio Collini
 
PDF
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Fabio Collini
 
PDF
Recap Google I/O 2018
Fabio Collini
 
PDF
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
Fabio Collini
 
PDF
From java to kotlin beyond alt+shift+cmd+k
Fabio Collini
 
PDF
Android Data Binding in action using MVVM pattern - droidconUK
Fabio Collini
 
PDF
Data Binding in Action using MVVM pattern
Fabio Collini
 
PDF
Android Wear CodeLab - GDG Firenze
Fabio Collini
 
PDF
Testable Android Apps using data binding and MVVM
Fabio Collini
 
PDF
Testable Android Apps DroidCon Italy 2015
Fabio Collini
 
PDF
Clean android code - Droidcon Italiy 2014
Fabio Collini
 
KEY
Librerie su Android: come non reinventare la ruota @ whymca 2012
Fabio Collini
 
KEY
Android Widget @ whymca 2011
Fabio Collini
 
Using Dagger in a Clean Architecture project
Fabio Collini
 
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
Fabio Collini
 
SOLID principles in practice: the Clean Architecture
Fabio Collini
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
Fabio Collini
 
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Fabio Collini
 
Recap Google I/O 2018
Fabio Collini
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
Fabio Collini
 
From java to kotlin beyond alt+shift+cmd+k
Fabio Collini
 
Android Data Binding in action using MVVM pattern - droidconUK
Fabio Collini
 
Data Binding in Action using MVVM pattern
Fabio Collini
 
Android Wear CodeLab - GDG Firenze
Fabio Collini
 
Testable Android Apps using data binding and MVVM
Fabio Collini
 
Testable Android Apps DroidCon Italy 2015
Fabio Collini
 
Clean android code - Droidcon Italiy 2014
Fabio Collini
 
Librerie su Android: come non reinventare la ruota @ whymca 2012
Fabio Collini
 
Android Widget @ whymca 2011
Fabio Collini
 
Ad

Recently uploaded (20)

PDF
The Internet - By the numbers, presented at npNOG 11
APNIC
 
DOCX
Custom vs. Off-the-Shelf Banking Software
KristenCarter35
 
PDF
BRKSP-2551 - Introduction to Segment Routing.pdf
fcesargonca
 
PDF
BRKACI-1001 - Your First 7 Days of ACI.pdf
fcesargonca
 
PPTX
法国巴黎第二大学本科毕业证{Paris 2学费发票Paris 2成绩单}办理方法
Taqyea
 
PDF
BRKAPP-1102 - Proactive Network and Application Monitoring.pdf
fcesargonca
 
PPTX
Networking_Essentials_version_3.0_-_Module_3.pptx
ryan622010
 
PPTX
Presentation3gsgsgsgsdfgadgsfgfgsfgagsfgsfgzfdgsdgs.pptx
SUB03
 
PDF
Top 10 Testing Procedures to Ensure Your Magento to Shopify Migration Success...
CartCoders
 
PPTX
L1A Season 1 ENGLISH made by A hegy fixed
toszolder91
 
PDF
Enhancing Parental Roles in Protecting Children from Online Sexual Exploitati...
ICT Frame Magazine Pvt. Ltd.
 
PPTX
04 Output 1 Instruments & Tools (3).pptx
GEDYIONGebre
 
PPTX
Networking_Essentials_version_3.0_-_Module_5.pptx
ryan622010
 
PDF
Digital burnout toolkit for youth workers and teachers
asociatiastart123
 
PDF
Boardroom AI: The Next 10 Moves | Cerebraix Talent Tech
ssuser73bdb11
 
PDF
FutureCon Seattle 2025 Presentation Slides - You Had One Job
Suzanne Aldrich
 
PPTX
PHIPA-Compliant Web Hosting in Toronto: What Healthcare Providers Must Know
steve198109
 
PPTX
Metaphysics_Presentation_With_Visuals.pptx
erikjohnsales1
 
PPTX
Lec15_Mutability Immutability-converted.pptx
khanjahanzaib1
 
PDF
BRKACI-1003 ACI Brownfield Migration - Real World Experiences and Best Practi...
fcesargonca
 
The Internet - By the numbers, presented at npNOG 11
APNIC
 
Custom vs. Off-the-Shelf Banking Software
KristenCarter35
 
BRKSP-2551 - Introduction to Segment Routing.pdf
fcesargonca
 
BRKACI-1001 - Your First 7 Days of ACI.pdf
fcesargonca
 
法国巴黎第二大学本科毕业证{Paris 2学费发票Paris 2成绩单}办理方法
Taqyea
 
BRKAPP-1102 - Proactive Network and Application Monitoring.pdf
fcesargonca
 
Networking_Essentials_version_3.0_-_Module_3.pptx
ryan622010
 
Presentation3gsgsgsgsdfgadgsfgfgsfgagsfgsfgzfdgsdgs.pptx
SUB03
 
Top 10 Testing Procedures to Ensure Your Magento to Shopify Migration Success...
CartCoders
 
L1A Season 1 ENGLISH made by A hegy fixed
toszolder91
 
Enhancing Parental Roles in Protecting Children from Online Sexual Exploitati...
ICT Frame Magazine Pvt. Ltd.
 
04 Output 1 Instruments & Tools (3).pptx
GEDYIONGebre
 
Networking_Essentials_version_3.0_-_Module_5.pptx
ryan622010
 
Digital burnout toolkit for youth workers and teachers
asociatiastart123
 
Boardroom AI: The Next 10 Moves | Cerebraix Talent Tech
ssuser73bdb11
 
FutureCon Seattle 2025 Presentation Slides - You Had One Job
Suzanne Aldrich
 
PHIPA-Compliant Web Hosting in Toronto: What Healthcare Providers Must Know
steve198109
 
Metaphysics_Presentation_With_Visuals.pptx
erikjohnsales1
 
Lec15_Mutability Immutability-converted.pptx
khanjahanzaib1
 
BRKACI-1003 ACI Brownfield Migration - Real World Experiences and Best Practi...
fcesargonca
 
Ad

Testing Android apps based on Dagger and RxJava

  • 1. Testing Android apps based on Dagger and RxJava Fabio Collini droidcon Italy April 2017
  • 3. 3 Agenda 1. Dagger 2. Testing 3. Mockito 4. Espresso 5. DaggerMock 6. RxJava
  • 6. public interface StackOverflowService {
 
 @GET("/users") 
 Single<List<User>> getTopUsers();
 
 @GET("/users/{userId}/badges") 
 Single<List<Badge>> getBadges(@Path("userId") int userId);
 } Activity Presenter Interactor Retrofit Service Rx Rx SingletonSingleton
  • 7. public class UserInteractor {
 private StackOverflowService service;
 
 public UserInteractor(StackOverflowService service) {
 this.service = service;
 }
 
 public Single<List<UserStats>> loadUsers() {
 //...
 }
 } Activity Presenter Interactor Retrofit Service Rx Rx SingletonSingleton
  • 8. public class UserListPresenter {b
 
 private UserInteractor userInteractor;
 
 private UserListActivity activity;
 
 public UserListPresenter(UserInteractor userInteractor, UserListActivity activity) {
 this.userInteractor = userInteractor;
 this.activity = activity;
 }onCreate
 
 public void reloadUserList() {
 userInteractor .loadUsers()
 //...
 .subscribe(
 activity::updateText,
 activity::showError
 );
 }showError
 }end Activity Presenter Interactor Retrofit Service Rx Rx SingletonSingleton
  • 9. public class UserListActivity extends AppCompatActivity {b
 
 @Inject UserListPresenter presenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) { //... ((MyApp) getApplicationContext()).getComponent()
 .userListComponent(new UserListModule(this)).inject(this);
 presenter.reloadUserList();
 }onCreate
 
 public void updateText(String s) { //... }updateText
 
 public void showError(Throwable t) { //... }showError
 }end Activity Presenter Interactor Retrofit Service Rx Rx SingletonSingleton
  • 10. 10 @Singleton
 @Component(modules = {UserInteractorModule.class, StackOverflowServiceModule.class})
 public interface ApplicationComponent {
 UserListComponent userListComponent(UserListModule module);
 } @Module
 public class UserInteractorModule {
 @Provides @Singleton public UserInteractor provideInteractor() {
 //...
 }
 } @Module
 public class StackOverflowServiceModule {
 @Provides @Singleton public StackOverflowService provideService() {
 //...
 }
 } @Subcomponent(modules = UserListModule.class)
 public interface UserListComponent {
 void inject(UserListActivity activity);
 } @Module
 public class UserListModule {
 @Provides public UserListPresenter providePresenter() {
 //...
 }
 }
  • 12. 12 Return Of Investment Net profit Investment
  • 13. 13
  • 14. 14
  • 15. 15
  • 17. 17 Integrated tests are a scam a self-replicating virus that threatens to infect your code base, your project, and your team with endless pain and suffering. J. B. Rainsberger
  • 19. public class MyTest {
 
 Collaborator1 collaborator1;
 
 Collaborator2 collaborator2;
 
 ObjectUnderTest objectUnderTest;
 
 @Before
 public void setUp() {
 collaborator1 = Mockito.mock(Collaborator1.class);
 collaborator2 = Mockito.mock(Collaborator2.class);
 objectUnderTest = new ObjectUnderTest( collaborator1, collaborator2);
 }setUp
 
 @Test
 public void myTestMethod() {
 //Arrange
 when(collaborator1.provideValue()).thenReturn(2);
 //Act
 objectUnderTest.execute();
 //Assert
 verify(collaborator2).printValue(10);
 assertThat(objectUnderTest.getValue()).isEqualTo(10);
 }_
 }__
  • 20. public class MyTest {
 
 @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
 
 @Mock Collaborator1 collaborator1;
 
 @Mock Collaborator2 collaborator2;
 
 @InjectMocks ObjectUnderTest objectUnderTest;
 
 @Test
 public void myTestMethod() {
 //Arrange
 when(collaborator1.provideValue()).thenReturn(2);
 //Act
 objectUnderTest.execute();
 //Assert
 verify(collaborator2).printValue(10);
 assertThat(objectUnderTest.getValue()).isEqualTo(10);
 }_
 }__
  • 21. package org.mockito.configuration;
 //... public class MockitoConfiguration extends DefaultMockitoConfiguration {
 public Answer<Object> getDefaultAnswer() {
 return new ReturnsEmptyValues() {
 @Override
 public Object answer(InvocationOnMock inv) {
 Class<?> type = inv.getMethod().getReturnType();
 if (type.isAssignableFrom(Observable.class)) {
 return Observable.error(createException(inv));
 } else if (type.isAssignableFrom(Single.class)) {
 return Single.error(createException(inv));
 } else {
 return super.answer(inv);
 }
 }
 };
 }
 
 private RuntimeException createException(
 InvocationOnMock invocation) {
 String s = invocation.toString();
 return new RuntimeException(
 "No mock defined for invocation " + s);
 }
 }
  • 23. public class UserListPresenter {
 
 private UserInteractor userInteractor;
 
 private UserListActivity activity;
 
 public UserListPresenter(UserInteractor userInteractor, UserListActivity activity) {
 this.userInteractor = userInteractor;
 this.activity = activity;
 }
 
 public void reloadUserList() {
 userInteractor
 .loadUsers()
 .flattenAsObservable(l -> l)
 .map(UserStats::toString)
 .reduce((s1, s2) -> s1 + "nn" + s2)
 .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe(
 activity::updateText,
 activity::showError
 );
 }
 } Activity Presenter Interactor Retrofit Service
  • 24. @Singleton
 @Component(modules = {
 TestUserInteractorModule.class,
 StackOverflowServiceModule.class
 })
 public interface TestApplicationComponent
 extends ApplicationComponent {
 void inject(UserListActivityTest userListActivityTest);
 } Activity Presenter Interactor Retrofit Service @Module
 public class TestUserInteractorModule {
 @Provides @Singleton
 public UserInteractor provideUserInteractor() {
 return Mockito.mock(UserInteractor.class);
 }
 }
  • 25. public class UserListActivityTest {
 @Rule public ActivityTestRule<UserListActivity> rule =
 new ActivityTestRule<>(UserListActivity.class, false, false);
 
 @Inject UserInteractor userInteractor;
 
 @Before public void setUp() {
 TestApplicationComponent component =
 DaggerTestApplicationComponent.create();
 getAppFromInstrumentation().setComponent(component);
 component.inject(this);
 }A
 
 @Test public void shouldDisplayUsers() {
 when(userInteractor.loadUsers()).thenReturn(
 Observable.fromArray(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ).toList());
 
 rule.launchActivity(null);
 
 onView(withId(R.id.text)).check(matches(withText(
 "50 user1 - badge1nn30 user2 - badge2, badge3")));
 }B
 }C Activity Presenter Interactor Retrofit Service
  • 26. public class UserListActivityTest {
 @Rule public ActivityTestRule<UserListActivity> rule =
 new ActivityTestRule<>(UserListActivity.class, false, false);
 
 @Inject UserInteractor userInteractor;
 
 @Before public void setUp() {
 TestApplicationComponent component =
 DaggerTestApplicationComponent.create();
 getAppFromInstrumentation().setComponent(component);
 component.inject(this);
 }A
 
 @Test public void shouldDisplayUsers() {
 when(userInteractor.loadUsers()).thenReturn(
 Observable.fromArray(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ).toList());
 
 rule.launchActivity(null);
 
 onView(withId(R.id.text)).check(matches(withText(
 "50 user1 - badge1nn30 user2 - badge2, badge3")));
 }B
 }C Activity Presenter Interactor Retrofit Service ArrangeActAssert
  • 27. Activity Presenter Interactor Retrofit Service public class UserListPresenter {
 
 private UserInteractor userInteractor;
 
 private UserListActivity activity;
 
 public UserListPresenter(UserInteractor userInteractor, UserListActivity activity) {
 this.userInteractor = userInteractor;
 this.activity = activity;
 }___
 
 public void reloadUserList() {
 userInteractor
 .loadUsers()
 .flattenAsObservable(l -> l)
 .map(UserStats::toString)
 .reduce((s1, s2) -> s1 + "nn" + s2)
 .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe(
 activity::updateText,
 activity::showError
 );
 }__
 }_
  • 29. public class AsyncTaskSchedulerRule implements TestRule {
 
 private final Scheduler asyncTaskScheduler =
 Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR);
 
 @Override
 public Statement apply(final Statement base, Description d) {
 return new Statement() {
 @Override
 public void evaluate() throws Throwable {
 RxJavaPlugins.setIoSchedulerHandler(
 scheduler -> asyncTaskScheduler);
 RxJavaPlugins.setComputationSchedulerHandler(
 scheduler -> asyncTaskScheduler);
 RxJavaPlugins.setNewThreadSchedulerHandler(
 scheduler -> asyncTaskScheduler);
 
 try {
 base.evaluate();
 } finally {
 RxJavaPlugins.reset();
 }
 }
 };
 }
 }
  • 30. public class UserListActivityTest {
 @Rule public ActivityTestRule<UserListActivity> rule =
 new ActivityTestRule<>(UserListActivity.class, false, false);
 @Rule public AsyncTaskSchedulerRule asyncTaskSchedulerRule = new AsyncTaskSchedulerRule(); 
 @Inject UserInteractor userInteractor;
 
 @Before public void setUp() {
 TestApplicationComponent component =
 DaggerTestApplicationComponent.create();
 getAppFromInstrumentation().setComponent(component);
 component.inject(this);
 }A
 
 @Test public void shouldDisplayUsers() {
 when(userInteractor.loadUsers()).thenReturn(
 Observable.fromArray(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ).toList());
 
 rule.launchActivity(null);
 
 onView(withId(R.id.text)).check(matches(withText(
 "50 user1 - badge1nn30 user2 - badge2, badge3")));
 }B
 }C Activity Presenter Interactor Retrofit Service ArrangeActAssert
  • 31. public class UserListActivityTest {
 @Rule public ActivityTestRule<UserListActivity> rule =
 new ActivityTestRule<>(UserListActivity.class, false, false);
 @Rule public AsyncTaskSchedulerRule asyncTaskSchedulerRule = new AsyncTaskSchedulerRule(); 
 @Inject UserInteractor userInteractor;
 
 @Before public void setUp() {
 TestApplicationComponent component =
 DaggerTestApplicationComponent.create();
 getAppFromInstrumentation().setComponent(component);
 component.inject(this);
 }A
 
 @Test public void shouldDisplayUsers() {
 when(userInteractor.loadUsers()).thenReturn(
 Observable.fromArray(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ).toList());
 
 rule.launchActivity(null);
 
 onView(withId(R.id.text)).check(matches(withText(
 "50 user1 - badge1nn30 user2 - badge2, badge3")));
 }B
 }C Activity Presenter Interactor Retrofit Service ArrangeActAssert
  • 32. @Singleton
 @Component(modules = {
 TestUserInteractorModule.class,
 StackOverflowServiceModule.class
 })
 public interface TestApplicationComponent
 extends ApplicationComponent {
 void inject(UserListActivityTest userListActivityTest);
 }_ Activity Presenter Interactor Retrofit Service @Module
 public class TestUserInteractorModule {
 @Provides @Singleton
 public UserInteractor provideUserInteractor() {
 return Mockito.mock(UserInteractor.class);
 }
 } //...
 @Inject UserInteractor userInteractor;
 
 @Before public void setUp() {
 TestApplicationComponent component =
 DaggerTestApplicationComponent.create();
 getAppFromInstrumentation().setComponent(component);
 component.inject(this);
 }__ //...
  • 33. Activity Presenter Interactor Retrofit Service //... @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
 
 @Mock UserInteractor userInteractor;
 
 @Before public void setUp() {
 ApplicationComponent component =
 DaggerApplicationComponent.builder()
 .userInteractorModule(new UserInteractorModule() {
 @Override
 public UserInteractor provideUserInteractor(
 StackOverflowService service) {
 return userInteractor;
 }___
 })
 .build();
 getAppFromInstrumentation().setComponent(component);
 }__ //...
  • 34. 34
  • 35. //... @Rule public DaggerMockRule<ApplicationComponent> daggerMockRule =
 new DaggerMockRule<>(
 ApplicationComponent.class,
 new UserInteractorModule()
 ).set(component ->
 getAppFromInstrumentation().setComponent(component));
 
 @Mock UserInteractor userInteractor; //... Activity Presenter Interactor Retrofit Service
  • 36. Activity Presenter Interactor Retrofit Service public class UserListActivityTest {
 @Rule public ActivityTestRule<UserListActivity> rule =
 new ActivityTestRule<>(UserListActivity.class, false, false);
 
 @Rule public AsyncTaskSchedulerRule asyncTaskSchedulerRule = new AsyncTaskSchedulerRule(); 
 @Rule public DaggerMockRule<ApplicationComponent> daggerMockRule =
 new DaggerMockRule<>(
 ApplicationComponent.class,
 new UserInteractorModule()
 ).set(component ->
 getAppFromInstrumentation().setComponent(component));
 
 @Mock UserInteractor userInteractor;
 
 @Test public void shouldDisplayUsers() {
 when(userInteractor.loadUsers()).thenReturn(
 Observable.fromArray(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ).toList());
 
 rule.launchActivity(null);
 
 onView(withId(R.id.text)).check(matches(withText(
 "50 user1 - badge1nn30 user2 - badge2, badge3")));
 }
 }
  • 37. static <T> T createSubclass(final T module, final Map<Class, Object> testFields) {
 return (T) Mockito.mock(module.getClass(), new Answer() {
 @Override public Object answer(InvocationOnMock invocation) throws Throwable {
 Method method = invocation.getMethod();
 Object obj = testFields.get(method.getReturnType());
 if (obj != null) {
 return obj;
 } else {
 return method.invoke(module, invocation.getArguments());
 }
 }
 });
 } DaggerMock internals
  • 38. Activity Presenter Interactor Retrofit Service @Rule public DaggerMockRule<ApplicationComponent> daggerMockRule =
 new DaggerMockRule<>(
 ApplicationComponent.class,
 new UserInteractorModule()
 ).set(component ->
 getAppFromInstrumentation().setComponent(component));
  • 39. Activity Presenter Interactor Retrofit Service public class MyDaggerMockRule
 extends DaggerMockRule<ApplicationComponent> {
 
 public MyDaggerMockRule() {
 super(
 ApplicationComponent.class,
 new UserInteractorModule()
 );
 set(component ->
 getAppFromInstrumentation().setComponent(component)); 
 providesMock(UserInteractor.class, userInteractor ->
 when(userInteractor.loadUsers()).thenReturn(
 Observable.fromArray(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ).toList())
 );
 }__
 }_
  • 41. 41 public class MockPresenterTest {
 
 @Rule public ActivityTestRule<UserListActivity> rule =
 new ActivityTestRule<>(UserListActivity.class, false, false);
 
 @Rule public MyDaggerMockRule daggerMockRule =
 new MyDaggerMockRule();
 
 @Mock UserListPresenter presenter;
 
 @Test
 public void testOnCreate() {
 rule.launchActivity(null);
 
 onView(withId(R.id.text)).check(matches(withText("")));
 
 verify(presenter).reloadUserList();
 }
 } Activity Presenter Interactor Retrofit Service
  • 42. 1.void method that uses RxJava schedulers 2.method that returns a synchronous RxJava object 3.method that returns an asynchronous RxJava object Testing RxJava code
  • 44. public class UserListPresenter {
 
 private UserInteractor userInteractor;
 
 private UserListActivity activity;
 
 public UserListPresenter(UserInteractor userInteractor, UserListActivity activity) {
 this.userInteractor = userInteractor;
 this.activity = activity;
 }
 
 public void reloadUserList() {
 userInteractor
 .loadUsers()
 .flattenAsObservable(l -> l)
 .map(UserStats::toString)
 .reduce((s1, s2) -> s1 + "nn" + s2)
 .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe(
 activity::updateText,
 activity::showError
 );
 }_
 } Activity Presenter Interactor Retrofit Service
  • 45. Activity Presenter Interactor Retrofit Service public void reloadUserList() {
 userInteractor
 .loadUsers()
 .flattenAsObservable(l -> l)
 .map(UserStats::toString)
 .reduce((s1, s2) -> s1 + "nn" + s2)
 .subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe(
 activity::updateText,
 activity::showError
 );
 }_

  • 46. public class UserListPresenterTest {
 
 @Rule public MockitoRule mockitoRule =
 MockitoJUnit.rule();
 
 @Mock UserInteractor userInteractor;
 
 @Mock UserListActivity activity;
 
 @InjectMocks UserListPresenter presenter;
 
 @Test
 public void shouldLoadUsers() {
 when(userInteractor.loadUsers()).thenReturn(
 Observable.fromArray(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ).toList());
 presenter.reloadUserList();
 verify(activity).updateText(anyString());
 }_
 }__ Activity Presenter Interactor Retrofit Service
  • 47. 47
  • 48. 48
  • 49. public class TrampolineSchedulerRule implements TestRule {
 
 @Override
 public Statement apply(final Statement base, Description d) {
 return new Statement() {
 @Override
 public void evaluate() throws Throwable {
 RxJavaPlugins.setIoSchedulerHandler(
 scheduler -> Schedulers.trampoline());
 RxJavaPlugins.setComputationSchedulerHandler(
 scheduler -> Schedulers.trampoline());
 RxJavaPlugins.setNewThreadSchedulerHandler(
 scheduler -> Schedulers.trampoline());
 RxAndroidPlugins.setInitMainThreadSchedulerHandler(
 scheduler -> Schedulers.trampoline());
 
 try {
 base.evaluate();
 } finally {
 RxJavaPlugins.reset();
 RxAndroidPlugins.reset();
 }
 }
 };
 }
 }
  • 50. Activity Presenter Interactor Retrofit Service public class UserListPresenterTest {
 
 @Rule public MockitoRule mockitoRule =
 MockitoJUnit.rule();
 @Rule public TrampolineSchedulerRule schedulerRule =
 new TrampolineSchedulerRule();
 
 @Mock UserInteractor userInteractor;
 
 @Mock UserListActivity activity;
 
 @InjectMocks UserListPresenter presenter;
 
 @Test
 public void shouldLoadUsers() {
 when(userInteractor.loadUsers()).thenReturn(
 Observable.fromArray(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ).toList());
 
 presenter.reloadUserList();
 
 verify(activity).updateText(
 "50 user1 - badge1nn30 user2 - badge2, badge3");
 }__
 }_
  • 51. Activity Presenter Interactor Retrofit Service public class UserListPresenterTest {
 
 @Rule public MockitoRule mockitoRule =
 MockitoJUnit.rule();
 @Rule public TrampolineSchedulerRule schedulerRule =
 new TrampolineSchedulerRule();
 
 @Mock UserInteractor userInteractor;
 
 @Mock UserListActivity activity;
 
 @InjectMocks UserListPresenter presenter;
 
 @Test
 public void shouldLoadUsers() {
 when(userInteractor.loadUsers()).thenReturn(
 Observable.fromArray(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ).toList());
 
 presenter.reloadUserList();
 
 verify(activity).updateText(
 "50 user1 - badge1nn30 user2 - badge2, badge3");
 }__
 }_
  • 52. Activity Presenter Interactor Retrofit Service public class UserListPresenterTest {
 
 @Rule public DaggerMockRule<ApplicationComponent> daggerMockRule =
 new DaggerMockRule<>(ApplicationComponent.class, new UserInteractorModule());
 
 @Rule public TrampolineSchedulerRule schedulerRule = 
 new TrampolineSchedulerRule();
 
 @Mock UserInteractor userInteractor;
 
 @Mock UserListActivity activity;
 
 @InjectFromComponent(UserListActivity.class) 
 UserListPresenter presenter;
 
 @Test
 public void shouldLoadUsers() {
 when(userInteractor.loadUsers()).thenReturn(
 Observable.fromArray(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ).toList());
 
 presenter.reloadUserList();
 
 verify(activity).updateText(
 "50 user1 - badge1nn30 user2 - badge2, badge3");
 }__
 }_
  • 53. 1.void method that uses RxJava schedulers 2.method that returns a synchronous RxJava object 3.method that returns an asynchronous RxJava object Testing RxJava code trampoline scheduler
  • 55. Activity Presenter Interactor Retrofit Service public class UserInteractor {
 private StackOverflowService service;
 
 public UserInteractor(StackOverflowService service) {
 this.service = service;
 }
 
 public Single<List<UserStats>> loadUsers() {
 return service.getTopUsers()
 .flattenAsObservable(l -> l)
 .take(5)
 .flatMap(user ->
 service.getBadges(user.id())
 .map(badges -> UserStats.create(user, badges))
 .toObservable()
 )____
 .toList();
 }
 }
  • 56. 56
  • 57. public class UserInteractorTest {
 @Rule public MockitoRule mockitoRule =
 MockitoJUnit.rule();
 
 @Mock StackOverflowService stackOverflowService;
 
 @InjectMocks UserInteractor userInteractor;
 
 @Test
 public void shouldLoadUsers() {
 when(stackOverflowService.getTopUsers()).thenReturn(
 Observable.fromArray(
 User.create(1, 50, "user1"),
 User.create(2, 30, "user2")
 ).toList());
 
 when(stackOverflowService.getBadges(1)).thenReturn(
 Single.just(Badge.createList("badge1")));
 when(stackOverflowService.getBadges(2)).thenReturn(
 Single.just(Badge.createList("badge2", "badge3")));
 List<UserStats> users = userInteractor.loadUsers().blockingGet();
 
 assertThat(users).containsExactly(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ); }__ }_ Activity Presenter Interactor Retrofit Service
  • 58. 1.void method that uses RxJava schedulers 2.method that returns a synchronous RxJava object 3.method that returns an asynchronous RxJava object Testing RxJava code trampoline scheduler blockingGet
  • 59. Activity Presenter Interactor Retrofit Service public Single<List<UserStats>> loadUsers() {
 return service.getTopUsers()
 .flattenAsObservable(l -> l)
 .take(5)
 .flatMap(user ->
 service.getBadges(user.id())
 .map(badges -> UserStats.create(user, badges))
 .toObservable()
 )____
 .toList();
 }_
  • 60. Activity Presenter Interactor Retrofit Service public Single<List<UserStats>> loadUsers() {
 return service.getTopUsers()
 .flattenAsObservable(l -> l)
 .take(5)
 .flatMap(user ->
 service.getBadges(user.id()) .subscribeOn(Schedulers.io())
 .map(badges -> UserStats.create(user, badges))
 .toObservable()
 )____
 .toList();
 }_
  • 61. Activity Presenter Interactor Retrofit Service public class UserInteractorTest {
 @Rule public MockitoRule mockitoRule =
 MockitoJUnit.rule();
 
 @Mock StackOverflowService stackOverflowService;
 
 @InjectMocks UserInteractor userInteractor;
 
 @Test
 public void shouldLoadUsers() {
 when(stackOverflowService.getTopUsers()).thenReturn(
 Observable.fromArray(
 User.create(1, 50, "user1"),
 User.create(2, 30, "user2")
 ).toList());
 
 when(stackOverflowService.getBadges(1)).thenReturn(
 Single.just(Badge.createList("badge1")));
 when(stackOverflowService.getBadges(2)).thenReturn(
 Single.just(Badge.createList("badge2", "badge3")));
 List<UserStats> users = userInteractor.loadUsers().blockingGet();
 
 assertThat(users).containsExactly(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ); }__ }_
  • 62. Activity Presenter Interactor Retrofit Service public class UserInteractorTest {
 @Rule public MockitoRule mockitoRule =
 MockitoJUnit.rule();
 
 @Mock StackOverflowService stackOverflowService;
 
 @InjectMocks UserInteractor userInteractor;
 
 @Test
 public void shouldLoadUsers() {
 when(stackOverflowService.getTopUsers()).thenReturn(
 Observable.fromArray(
 User.create(1, 50, "user1"),
 User.create(2, 30, "user2")
 ).toList());
 
 when(stackOverflowService.getBadges(1)).thenReturn(
 Single.just(Badge.createList("badge1")));
 when(stackOverflowService.getBadges(2)).thenReturn(
 Single.just(Badge.createList("badge2", "badge3")));
 List<UserStats> users = userInteractor.loadUsers().blockingGet();
 
 assertThat(users).containsExactly(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ); }__ }_
  • 63. Activity Presenter Interactor Retrofit Service public class UserInteractorTest {
 @Rule public MockitoRule mockitoRule =
 MockitoJUnit.rule();
 @Rule public TrampolineSchedulerRule schedulerRule =
 new TrampolineSchedulerRule();
 
 @Mock StackOverflowService stackOverflowService;
 
 @InjectMocks UserInteractor userInteractor;
 
 @Test
 public void shouldLoadUsers() {
 when(stackOverflowService.getTopUsers()).thenReturn(
 Observable.fromArray(
 User.create(1, 50, "user1"),
 User.create(2, 30, "user2")
 ).toList());
 
 when(stackOverflowService.getBadges(1)).thenReturn(
 Single.just(Badge.createList("badge1")));
 when(stackOverflowService.getBadges(2)).thenReturn(
 Single.just(Badge.createList("badge2", "badge3")));
 List<UserStats> users = userInteractor.loadUsers().blockingGet();
 
 assertThat(users).containsExactly(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 ); }__ }_
  • 64. Activity Presenter Interactor Retrofit Service when(stackOverflowService.getTopUsers()).thenReturn(
 Observable.fromArray(
 User.create(1, 50, "user1"),
 User.create(2, 30, "user2")
 ).toList());
 
 when(stackOverflowService.getBadges(1)).thenReturn(
 Single.just(Badge.createList("badge1"))
 .delay(2, TimeUnit.SECONDS));
 when(stackOverflowService.getBadges(2)).thenReturn(
 Single.just(Badge.createList("badge2", "badge3"))
 .delay(1, TimeUnit.SECONDS));
 
 userInteractor.loadUsers()
 .test()
 .assertNoErrors()
 .assertValue(check(users ->
 assertThat(users).containsExactly(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 )_
 ));
  • 65. Activity Presenter Interactor Retrofit Service when(stackOverflowService.getTopUsers()).thenReturn(
 Observable.fromArray(
 User.create(1, 50, "user1"),
 User.create(2, 30, "user2")
 ).toList());
 
 when(stackOverflowService.getBadges(1)).thenReturn(
 Single.just(Badge.createList("badge1"))
 .delay(2, TimeUnit.SECONDS));
 when(stackOverflowService.getBadges(2)).thenReturn(
 Single.just(Badge.createList("badge2", "badge3"))
 .delay(1, TimeUnit.SECONDS));
 
 userInteractor.loadUsers()
 .test()
 .assertNoErrors()
 .assertValue(check(users ->
 assertThat(users).containsExactly(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 )_
 ));
  • 66. Activity Presenter Interactor Retrofit Service when(stackOverflowService.getTopUsers()).thenReturn(
 Observable.fromArray(
 User.create(1, 50, "user1"),
 User.create(2, 30, "user2")
 ).toList());
 
 when(stackOverflowService.getBadges(1)).thenReturn(
 Single.just(Badge.createList("badge1"))
 .delay(2, TimeUnit.SECONDS));
 when(stackOverflowService.getBadges(2)).thenReturn(
 Single.just(Badge.createList("badge2", "badge3"))
 .delay(1, TimeUnit.SECONDS));
 
 userInteractor.loadUsers()
 .test()
 .assertNoErrors()
 .assertValue(check(users ->
 assertThat(users).containsExactly(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 )_
 ));
  • 67. public class TestSchedulerRule implements TestRule {
 private final TestScheduler testScheduler = new TestScheduler();
 
 public TestScheduler getTestScheduler() {
 return testScheduler;
 }
 
 @Override
 public Statement apply(final Statement base, Description d) {
 return new Statement() {
 @Override
 public void evaluate() throws Throwable {
 RxJavaPlugins.setIoSchedulerHandler(
 scheduler -> testScheduler);
 RxJavaPlugins.setComputationSchedulerHandler(
 scheduler -> testScheduler);
 RxJavaPlugins.setNewThreadSchedulerHandler(
 scheduler -> testScheduler);
 RxAndroidPlugins.setMainThreadSchedulerHandler(
 scheduler -> Schedulers.trampoline());
 
 try {
 base.evaluate();
 } finally {
 RxJavaPlugins.reset();
 RxAndroidPlugins.reset();
 }
 }
 };
 }
 }
  • 68. Activity Presenter Interactor Retrofit Service when(stackOverflowService.getTopUsers()).thenReturn(
 Observable.fromArray(
 User.create(1, 50, "user1"),
 User.create(2, 30, "user2")
 ).toList());
 
 when(stackOverflowService.getBadges(1)).thenReturn(
 Single.just(Badge.createList("badge1"))
 .delay(2, TimeUnit.SECONDS));
 when(stackOverflowService.getBadges(2)).thenReturn(
 Single.just(Badge.createList("badge2", "badge3"))
 .delay(1, TimeUnit.SECONDS));
 
 TestObserver<List<UserStats>> testObserver =
 userInteractor.loadUsers().test();
 
 schedulerRule.getTestScheduler()
 .advanceTimeBy(2, TimeUnit.SECONDS);
 
 testObserver
 .assertNoErrors()
 .assertValue(check(users ->
 assertThat(users).containsExactly(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 )_
 ));
  • 69. when(stackOverflowService.getTopUsers()).thenReturn(
 Observable.fromArray(
 User.create(1, 50, "user1"),
 User.create(2, 30, "user2")
 ).toList());
 
 when(stackOverflowService.getBadges(1)).thenReturn(
 Single.just(Badge.createList("badge1"))
 .delay(2, TimeUnit.SECONDS));
 when(stackOverflowService.getBadges(2)).thenReturn(
 Single.just(Badge.createList("badge2", "badge3"))
 .delay(1, TimeUnit.SECONDS));
 
 TestObserver<List<UserStats>> testObserver =
 userInteractor.loadUsers().test();
 
 schedulerRule.getTestScheduler()
 .advanceTimeBy(2, TimeUnit.SECONDS);
 
 testObserver
 .assertNoErrors()
 .assertValue(check(users ->
 assertThat(users).containsExactly(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 )_
 )); Activity Presenter Interactor Retrofit Service
  • 70. Activity Presenter Interactor Retrofit Service public class UserInteractor {
 private StackOverflowService service;
 
 public UserInteractor(StackOverflowService service) {
 this.service = service;
 }___
 
 public Single<List<UserStats>> loadUsers() {
 return service.getTopUsers()
 .flattenAsObservable(l -> l)
 .take(5)
 .flatMap(user ->
 service.getBadges(user.id())
 .subscribeOn(Schedulers.io())
 .map(badges -> UserStats.create(user, badges))
 .toObservable()
 )____
 .toList();
 }_
 }__
  • 71. Activity Presenter Interactor Retrofit Service public class UserInteractor {
 private StackOverflowService service;
 
 public UserInteractor(StackOverflowService service) {
 this.service = service;
 }___
 
 public Single<List<UserStats>> loadUsers() {
 return service.getTopUsers()
 .flattenAsObservable(l -> l)
 .take(5)
 .flatMap(user ->
 service.getBadges(user.id())
 .subscribeOn(Schedulers.io())
 .map(badges -> UserStats.create(user, badges))
 .toObservable()
 )____
 .toList();
 }_
 }__
  • 72. public class UserInteractor {
 private StackOverflowService service;
 
 public UserInteractor(StackOverflowService service) {
 this.service = service;
 }___
 
 public Single<List<UserStats>> loadUsers() {
 return service.getTopUsers()
 .flattenAsObservable(l -> l)
 .take(5)
 .flatMap(user ->
 service.getBadges(user.id())
 .subscribeOn(Schedulers.io())
 .map(badges -> UserStats.create(user, badges))
 .toObservable()
 )____
 .toList();
 }_
 }__ Activity Presenter Interactor Retrofit Service
  • 73. Activity Presenter Interactor Retrofit Service public class UserInteractor {
 private StackOverflowService service;
 
 public UserInteractor(StackOverflowService service) {
 this.service = service;
 }___
 
 public Single<List<UserStats>> loadUsers() {
 return service.getTopUsers()
 .flattenAsObservable(l -> l)
 .take(5)
 .concatMapEager(user ->
 service.getBadges(user.id())
 .subscribeOn(Schedulers.io())
 .map(badges -> UserStats.create(user, badges))
 .toObservable()
 )____
 .toList();
 }_
 }__
  • 74. Activity Presenter Interactor Retrofit Service when(stackOverflowService.getTopUsers()).thenReturn(
 Observable.fromArray(
 User.create(1, 50, "user1"),
 User.create(2, 30, "user2")
 ).toList());
 
 when(stackOverflowService.getBadges(1)).thenReturn(
 Single.just(Badge.createList("badge1"))
 .delay(2, TimeUnit.SECONDS));
 when(stackOverflowService.getBadges(2)).thenReturn(
 Single.just(Badge.createList("badge2", "badge3"))
 .delay(1, TimeUnit.SECONDS));
 
 TestObserver<List<UserStats>> testObserver =
 userInteractor.loadUsers().test();
 
 schedulerRule.getTestScheduler()
 .advanceTimeBy(2, TimeUnit.SECONDS);
 
 testObserver
 .assertNoErrors()
 .assertValue(check(l ->
 assertThat(l).containsExactly(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 )_
 ));
  • 75. when(stackOverflowService.getTopUsers()).thenReturn(
 Observable.fromArray(
 User.create(1, 50, "user1"),
 User.create(2, 30, "user2")
 ).toList());
 
 when(stackOverflowService.getBadges(1)).thenReturn(
 Single.just(Badge.createList("badge1"))
 .delay(2, TimeUnit.SECONDS));
 when(stackOverflowService.getBadges(2)).thenReturn(
 Single.just(Badge.createList("badge2", "badge3"))
 .delay(1, TimeUnit.SECONDS));
 
 TestObserver<List<UserStats>> testObserver =
 userInteractor.loadUsers().test();
 
 schedulerRule.getTestScheduler()
 .advanceTimeBy(2, TimeUnit.SECONDS);
 
 testObserver
 .assertNoErrors()
 .assertValue(check(l ->
 assertThat(l).containsExactly(
 UserStats.create(1, 50, "user1", "badge1"),
 UserStats.create(2, 30, "user2", "badge2", "badge3")
 )_
 )); Activity Presenter Interactor Retrofit Service
  • 76. 1.void method that uses RxJava schedulers 2.method that returns a synchronous RxJava object 3.method that returns an asynchronous RxJava object Testing RxJava code trampoline scheduler blockingGet TestScheduler & TestObserver
  • 77. 77 Wrapping up 1.JVM tests isolate failures and are fast and reliable 2.Using DaggerMock testing boilerplate code can be reduced 3.RxJava asynchronous code can be tested using TestObserver and TestScheduler
  • 79. Thanks for your attention! Questions?