10

我正在尝试为我的 android 应用程序编写一个单元测试,但是在使用 mockito 做我想做的事情时遇到了麻烦。这与 Robolectric 结合使用,我工作得很好,并且已经证明单元测试有效。

我想根据是否连接了一些蓝牙设备来测试按钮是否会打开新活动。显然,在我的测试中没有连接蓝牙的设备,但是我想假装好像有。蓝牙连接的状态存储在我的应用程序类中。没有可公开访问的方法来更改此值。

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

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?

4

2 回答 2

6

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

我个人不知道 android 如何与Application该类一起工作,但是您必须将其设置在某个位置,以便 listView 使用您的mockedApp而不是它通常接收的内容。

编辑 更新问题后,您可以将您的方法转换getApplication为受保护的方法,并使其返回您spy的. 这闻起来有点难闻,但如果您不能将应用程序模拟对象设置为.listViewButtonmockedApplistViewButton

编辑2

在测试中使用 spy 以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);
       ...
   }
}

之后,您的测试应该按预期工作。但我强调:spy仅当您无法注入mockedApp内部 HomeActivity 时才使用。

于 2012-12-20T21:06:32.177 回答
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() 方法?

于 2012-12-20T21:08:52.077 回答