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

时间:2012-12-20 20:52:01

标签: 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));
   }
}

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

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()));
}

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

2 个答案:

答案 0 :(得分:5)

这就是mockito的工作原理,但问题是:listViewButton是否正在使用mockedApp?似乎没有,因为您在测试方法中创建mockedApp并且从未在任何地方设置它。 Mockito不会模拟Application的所有实例的方法调用,只会从你声明为模拟的内容开始。

我个人不知道android如何与Application类一起使用,但你必须将它设置在某个地方,以便listView使用你的mockedApp而不是正常接收的内容。

修改 更新后的问题后,您可以使用受保护的方法转换getApplicationspy改变listViewButton mockedApp并使其返回listViewButton。这有点不好,但如果你不能将你的应用程序模拟对象设置为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); ... } } ,那就是一种方法。

<强> EDIT2

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

spy

之后,您的测试应该按预期工作。但我强调:只有在无法将mockedApp注入HomeActivity时才使用{{1}}。

答案 1 :(得分:2)

您的模拟版本未被调用。

看到那个电话,getApplication()? (下面)。那将返回MyApplication类的真实副本,而不是您的模拟版本。您需要拦截getApplication()调用并传入模拟的Application对象。

HomeActivity.java:

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

我不确定Mockito可以做到这一点。您是否尝试过自定义ShadowActivity#getApplication()方法?