KEMBAR78
Testing Android apps based on Dagger and RxJava | PDF
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?

Testing Android apps based on Dagger and RxJava

  • 1.
    Testing Android appsbased on Dagger and RxJava Fabio Collini droidcon Italy April 2017
  • 2.
  • 3.
    3 Agenda 1. Dagger 2. Testing 3.Mockito 4. Espresso 5. DaggerMock 6. RxJava
  • 4.
  • 5.
  • 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 UserListActivityextends 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() {
 //...
 }
 }
  • 11.
  • 12.
    12 Return Of Investment Netprofit Investment
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
    17 Integrated tests area 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.
  • 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 classMockitoConfiguration 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);
 }
 }
  • 22.
  • 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
 })
 publicinterface 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
 );
 }__
 }_
  • 28.
  • 29.
    public class AsyncTaskSchedulerRuleimplements 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
 })
 publicinterface 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 MockitoRulemockitoRule = 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.
  • 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> TcreateSubclass(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
 extendsDaggerMockRule<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())
 );
 }__
 }_
  • 40.
  • 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 thatuses RxJava schedulers 2.method that returns a synchronous RxJava object 3.method that returns an asynchronous RxJava object Testing RxJava code
  • 43.
  • 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.
  • 48.
  • 49.
    public class TrampolineSchedulerRuleimplements 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 thatuses RxJava schedulers 2.method that returns a synchronous RxJava object 3.method that returns an asynchronous RxJava object Testing RxJava code trampoline scheduler
  • 54.
  • 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.
  • 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 thatuses 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 TestSchedulerRuleimplements 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 thatuses 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 testsisolate 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.
  • 79.
    Thanks for yourattention! Questions?