创建JUnit,并且设定TestComponent
  Creating a JUnit rule that sets the TestComponent
  为了确保在每次测试前TestComponent被设置到Application类中,我们可以创建JUnit 4 的 TestRule
  public class TestComponentRule implements TestRule {
  private final TestComponent mTestComponent;
  private final Context mContext;
  public TestComponentRule(Context context) {
  mContext = context;
  MyApplication application = (MyApplication) context.getApplicationContext();
  mTestComponent = DaggerTestComponent.builder()
  .applicationTestModule(new ApplicationTestModule(application))
  .build();
  }
  public DataManager getMockDataManager() {
  return mTestComponent.dataManager();
  }
  @Override
  public Statement apply(final Statement base, Description description) {
  return new Statement() {
  @Override
  public void evaluate() throws Throwable {
  MyApplication application = (MyApplication) context.getApplicationContext();
  // Set the TestComponent before the test runs
  application.setComponent(mTestComponent);
  base.evaluate();
  // Clears the component once the tets finishes so it would use the default one.
  application.setComponent(null);
  }
  };
  }
  }
  TestComponentRule将会创建TestComponent的实例对象,这也会覆写apply方法并返回一个新的 Statement,新的Statement会:
  1 设定TestComponent给Application类的component对象。
  2调用基类的Statement 的evaluate()方法(这是在test的时候执行)
  3 设置Application的component字段为空,也让其恢复到初始状态。我们能够通过这种方式预防测试用例之间的相互影响
  通过上面的代码我们可以通过getMockDataManager()方法获取模拟的DataManager对象。这也允许我们能够给得到DataManager对象并且stub它的方法。需要注意的是,这只有TestApplicationComponent的provideDataManger方法使用@Singleton注解的时候有效。如果它没有被指定为单例的,那么我们通过getMockDataManager方法得到的实例对象将会不同于应用使用的实例对象。因此,我们也不可能stub它。
  编写测试用例
  Writing the tests
  现在我们有Dagger正确的配置,并且TestComponentRule也可以使用了,我们还有一件事要做,那是编写测试用例。我们使用 Espresso编写UI测试。它并不是完美的但是它是一个快速可靠的Android测试框架。在编写测试用例之前我们需要一个app去测试。假如我们有一个非常简单的app,从REST API 中加载用户名,并且展示到RecyclerView上面。那么DataManger将会是下面这个样子:
  public DataManager {
  // Loads usernames from a REST API using a Retrofit
  public Single<List<String>> loadUsernames() {
  return mUsersService.getUsernames();
  }
  }
  loadUsername()方法使用Retrofit和Rxjava 去加载REST API 的数据。它返回的是Single 对象,并且发送一串字符串。 我们也需要一个Activity展示用户名usernames到RecyclerView上面,我们假设这个Activity叫做UsernamesActivity。如果你遵循MVP模式你也会有相应的presenter但为了直观理解,这里不做presenter操作。
  现在我们想要测试这个简单的 Activity有至少三个情况需要测试:
  1如果API返回一个有效的用户名列表数据,那么它们会被展示到列表上面。
  2 如果API返回空的数据,那么界面会显示“空的列表”
  3 如果API 请求失败,那么界面会显示“加载用户名失败”
  下面依次展示三个测试:
  @Test
  public void usernamesDisplay() {
  // Stub the DataManager with a list of three usernames
  List<String> expectedUsernames = Arrays.asList("Joe", "Jemma", "Matt");
  when(component.getMockDataManager().loadUsernames())
  .thenReturn(Single.just(expectedUsernames));
  // Start the Activity
  main.launchActivity(null);
  // Check that the three usernames are displayed
  for (Sting username:expectedUsernames) {
  onView(withText(username))
  .check(matches(isDisplayed()));
  }
  }
  @Test
  public void emptyMessageDisplays() {
  // Stub an empty list
  when(component.getMockDataManager().loadUsernames())
  .thenReturn(Single.just(Collections.emptyList()));
  // Start the Activity
  main.launchActivity(null);
  // Check the empty list message displays
  onView(withText("Empty list"))
  .check(matches(isDisplayed()));
  }
  @Test
  public void errorMessageDisplays() {
  // Stub with a Single that emits and error
  when(component.getMockDataManager().loadUsernames())
  .thenReturn(Single.error(new RuntimeException()));
  // Start the Activity
  main.launchActivity(null);
  // Check the error message displays
  onView(withText("Error loading usernames"))
  .check(matches(isDisplayed()));
  }
  }
  通过上面的代码,我们使用TestComponentRule 和android 官方测试框架提供的ActivityTestRule。ActivityTestRule会让我们从测试中启动UsernamesActivity 。注意我们使用 RuleChain 来确保 TestComponentRule总是在ActivityTestRule前运行。这也是确保TestComponent在任何Activity运行之前在Application类中设定好。
  你可能注意到了三个测试用例遵循同样的构建方式:
  1 通过when (xxx).thenReturn(yyy)设置前置条件。这是通过stub loadUsernames()方法实现的。例如,第一个测试的前置条件是有一个有效的用户名列表。
  2 通过main.launchActivity(null)运行activity。
  3 通过check(matches(isDisplayed()));检查视图的展示,并且展示相应前置条件期望的值。
  这是一个非常有效的解决方案,它允许你测试不同的场景,因为你对整个application的初始状态拥有的控制权。如果你不使用mock来编写上面的三个用例,几乎不可能达到这样的效果因为真实的API接口总会返回同样的数据。
  如果你想要查看使用这个测试方法的完整实例,你可以在github查看项目ribot Android boilerplate 或者 ribot app.
  当然这个解决方案也有一些瑕疵。首先在每个test之前都会stub显得非常繁琐。复杂的界面可能需要在每个测试之前有5-10个stub。将一些stub移到初始化setup()方法中是有用的但经常不同的测试需要不同的stub。第二个问题是UI测试和潜在的实现存在着耦合,也意味着如果你重构DataManager,那么你也需要修改stub。
  虽然这样,我们也在ribot 的几个应用中应用了这个UI测试方法,事实证明这中方法也是有好处的。例如,我们近的一个Android应用中有250个UI测试能够在三分钟之内运行成功。其中也有380个Model层和Presenter层的单元测试。