困惑如何使用Mockito进行Android测试

Mat*_*lfe 9 android mockito robolectric

我正在尝试为我的Android应用程序编写一个单元测试,但无法用mockito做我想做的事情.这与Robolectric一起使用,我工作得很好并证明单元测试工作正常.

我想测试一个按钮是否会打开一个新活动,具体取决于是否连接了一些蓝牙设备.显然,在我的测试中没有与蓝牙连接的设备,但我想假装好像有.蓝牙连接的状态存储在我的Application类中.没有可公开访问的方法来更改此值.

所以基本上应用程序中的逻辑是这样的:

HomeActivity.java:

//this gets called when the button to open the list is clicked.
public void openListActivity(View button) { 
  MyApplication myApplication = (MyApplication) getApplication();
  if (myApplication.isDeviceConnected() {
      startActivity(new intent(this, ListActivity.class));
   }
}
Run Code Online (Sandbox Code Playgroud)

所以为了测试这个,我做了以下事情:

TestHomeActivity.java:

@Test
public void buttonShouldOpenListIfConnected() {
    FlexApplication mockedApp = Mockito.mock(MyApplication.class);
    Mockito.when(mockedApp.isDeviceConnected()).thenReturn(true);
    //listViewButton was setup in @Before
    listViewButton.performClick();
    ShadowActivity shadowActivity = Robolectric.shadowOf(activity);

    Intent intent = shadowActivity.getNextStartedActivity();
    assertNotNull(intent); //this fails because no new activity was opened. I debugged this and found that isDeviceConnected returned false.
    ShadowIntent shadowIntent = Robolectric.shadowOf(intent);
    assertThat(shadowIntent.getComponent().getClassName(), equalTo(ListActivity.class.getName()));
}
Run Code Online (Sandbox Code Playgroud)

因此,我的单元测试失败,因为isDeviceConnected的调用(在活动中)返回false,即使我认为我告诉它使用模拟框架返回true.我希望我的测试让这个方法返回true.这不是mockito所做的,还是我完全错误的如何使用mockito?

Cae*_*alf 5

这就是mockito的工作方式,但问题是:你listViewButton使用的是mockedApp?似乎没有,因为你mockedApp在测试方法创建并且从未在任何地方设置它.Mockito不会模拟所有实例的方法调用Application,只能从你声明为mock的方法调用.

我个人不知道android如何使用Application该类,但你必须将它设置在某处,所以listView使用你的mockedApp而不是它正常接收的东西.

编辑 更新的问题后,你可以改变你getApplication在一个受保护的方法,spylistViewButton并使其返回你的mockedApp.这闻起来有点不好,但如果你不能将应用程序模拟对象设置为,那就是一种方法listViewButton.

EDIT2

在您的测试中使用间谍的示例,BDDMockito以提高可读性:)

public HomeActivity {
    ...
    protected MyApplication getApplication() {
       // real code
    }
    ...
}

public void TestHomeActivity {
   private HomeActivity homeActivity;

   @Before
   public void setUp() {
       this.homeActivity = spy(new HomeActivity());
   }

   @Test
   public void buttonShouldOpenListIfConnected() {
       // given
       FlexApplication mockedApp = Mockito.mock(MyApplication.class);
       Mockito.when(mockedApp.isDeviceConnected()).thenReturn(true);
       // IMPORTANT PART
       given(homeActivity.getApplication()).willReturn(mockedApp);
       ...
   }
}
Run Code Online (Sandbox Code Playgroud)

之后,您的测试应该按预期工作.但我强调:spy只有在你无法注入mockedApp内部HomeActivity时使用.

  • 如果间谍对象是代码气味,那么什么是更好的解决方案?我应该尝试注射应用程序吗?为应用程序添加setter?将处理连接状态的逻辑移动到另一个类?我目前没有使用依赖注入框架,但也许我可以.我也不太喜欢用于测试的书写方法,但也许这是一个必要的邪恶. (2认同)