简体   繁体   English

使用Retrofit和Mockito进行Android单元测试

[英]Android Unit Test with Retrofit and Mockito

I separated retrofit api calls methods from the activity code and I want to do a unit test on these methods, one example: The interface: 我从活动代码中分离了改进的api调用方法,我想对这些方法进行单元测试,例如:接口:

public interface LoginService {
    @GET("/auth")
    public void basicLogin(Callback<AuthObject> response);
}

and this is the method that do the call, in the main activity I get the object by the event bus. 这是执行调用的方法,在主活动中,我通过事件总线获取对象。

public class AuthAPI {
    private Bus bus;
    LoginService loginService;

    public AuthAPI(String username, String password) {
        this.bus = BusProvider.getInstance().getBus();
        loginService = ServiceGenerator.createService(LoginService.class,
                CommonUtils.BASE_URL,
                username,
                password);
    }

    public void Login() {

        loginService.basicLogin(new Callback<AuthObject>() {
            @Override
            public void success(AuthObject authObject, Response response) {
                bus.post(authObject);
            }

            @Override
            public void failure(RetrofitError error) {
                AuthObject authObject = new AuthObject();
                authObject.setError(true);
                bus.post(authObject);
            }
        });
    }

}

And here the test 在这里测试

@RunWith(MockitoJUnitRunner.class)
public class AuthCallTest extends TestCase {

    AuthAPI authAPI;

    @Mock
    private LoginService mockApi;

    @Captor
    private ArgumentCaptor<Callback<AuthObject>> cb;

    @Before
    public void setUp() throws Exception {
        authAPI = new AuthAPI("username", "password");
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testLogin() throws Exception {

        Mockito.verify(mockApi).basicLogin((cb.capture()));

        AuthObject authObject = new AuthObject();
        cb.getValue().success(authObject, null);

        assertEquals(authObject.isError(), false);
    }
}

when I launch the test I have this error 当我启动测试时,我有这个错误

Wanted but not invoked:
mockApi.basicLogin(<Capturing argument>);
-> at AuthCallTest.testLogin(AuthCallTest.java:42)
Actually, there were zero interactions with this mock.

What I did wrong, this is driving me crazy I tried to follow this guide without success: http://www.mdswanson.com/blog/2013/12/16/reliable-android-http-testing-with-retrofit-and-mockito.html 我做错了什么,这让我发疯了我试图按照本指南取得成功: http//www.mdswanson.com/blog/2013/12/16/reliable-android-http-testing-with-retrofit-and -mockito.html

someone help me :( 谁来帮帮我 :(

The article isn't very clear as it misses out the setup steps. 这篇文章不太清楚,因为它错过了设置步骤。 By visiting the GitHub project linked in the article, you can see the full source code which explains those missing steps: 通过访问本文中链接的GitHub项目 ,您可以看到完整的源代码,它解释了这些缺失的步骤:

1) The code samples are extracted from a test class testing a specific activity. 1)从测试特定活动的测试类中提取代码样本。 As part of the setup (ie in @Before ), it replaces the Activity's reference to a GitHub API implementation with a mock one. 作为设置的一部分(即在@Before ),它将模拟的GitHub API实现的活动引用替换为活动。 It then calls the Activity's onCreate() . 然后它调用Activity的onCreate()

2) During onCreate() , the activity makes a call to the now-replaced GitHub API, passing in its Callback object. 2)在onCreate() )期间,活动调用现在替换的GitHub API,并传入其Callback对象。

Those first two steps explain why the Mockito.verify(mockApi).repositories(Mockito.anyString(), cb.capture()); 前两个步骤解释了为什么Mockito.verify(mockApi).repositories(Mockito.anyString(), cb.capture()); step at the beginning of each test works. 每个测试工作开始时的步骤。 As the test is run after @Before , the mockApi has indeed had a call on its repositories() method. 由于测试是在@Before之后@Before ,所以mockApi确实对其repositories()方法进行了调用。

The rest of the code is easier to understand once that's in place. 一旦到位,其余代码就更容易理解了。 As he's only created a mockApi , but not changed the actual Callback being used, the activity's content is changed. 由于他只创建了一个mockApi ,但没有更改正在使用的实际Callback ,因此活动的内容会发生变化。 The rest of the code then verifies that those changes have taken place, either by checking a ListView or the Toasts. 然后,其余代码通过检查ListView或Toasts来验证是否已发生这些更改。


So to answer your question, you need to: 所以要回答你的问题,你需要:

1) At the start of your test method, replace the AuthAPI's loginService object with your mockApi object, then call AuthAPI.Login() . 1)在测试方法开始时,用mockApi对象替换AuthAPI的loginService对象,然后调用AuthAPI.Login()

2) Use verify() as you already are to check that the function has been called. 2)使用verify()因为您已经检查该函数是否已被调用。

3) Create a sample AuthObject and pass it to the cb.getValue().success() function. 3)创建一个示例AuthObject并将其传递给cb.getValue().success()函数。

4) Obtain the AuthObject from your Bus and assert that it is the same one you sent to the callback.success() function. 4)从Bus获取AuthObject并声明它与您发送到callback.success()函数的那个​​相同。

This tests that your AuthAPI.Login() correctly sends to your Bus the AuthObject that it would retrieve from Retrofit. 这测试您的AuthAPI.Login()正确地向您的Bus发送它将从Retrofit检索的AuthObject


(I realise the SO question was written some time ago, but as I came across the same article and had the same confusion very recently, I thought this answer could be useful for others.) (我意识到SO问题是在不久前写的,但是当我遇到同一篇文章并且最近也有同样的困惑时,我认为这个答案可能对其他人有用。)

The problem is that you call verify at the wrong moment: the purpose of verify is to verify that the interactions with mockApi were what you expected. 问题是您在错误的时刻调用verifyverify的目的是verify与mockApi的交互是否符合您的预期。 So normally you would see something like: 通常你会看到类似的东西:

authApi.login();
Mockito.verify(mockApi).basicLogin((cb.capture()));

That's also what the error message is telling you: verify expected basicLogin to be called but it wasn't. 这也是错误消息告诉你的: verify预期的basicLogin被调用,但事实并非如此。

I've read that article too and felt there was something missing. 我也读过那篇文章,觉得有些东西不见了。 I don't actually undestand argument capture yet. 我实际上并没有找到参数捕获。 So can't help you with that :) 所以无法帮助你:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM