【问题标题】:Unit Testing in Retrofit for Callback回调改造中的单元测试
【发布时间】:2017-12-22 22:11:28
【问题描述】:

在这里,我得到了演示者中的代码示例。如何在改造调用中为 onSuccess 和 onFailure 编写测试

public void getNotifications(final List<HashMap<String,Object>> notifications){

        if (!"".equalsIgnoreCase(userDB.getValueFromSqlite("email",1))) {
            UserNotifications userNotifications =
                    new UserNotifications(userDB.getValueFromSqlite("email",1),Integer.parseInt(userDB.getValueFromSqlite("userId",1).trim()));
            Call call = apiInterface.getNotifications(userNotifications);
            call.enqueue(new Callback() {
                @Override
                public void onResponse(Call call, Response response) {
                    UserNotifications userNotifications1 = (UserNotifications) response.body();


                    if(userNotifications1.getNotifications().isEmpty()){
                        view.setListToAdapter(notifications);
                        onFailure(call,new Throwable());
                    }
                    else {
                        for (UserNotifications.Datum datum:userNotifications1.getNotifications()) {
                            HashMap<String,Object> singleNotification= new HashMap<>();
                            singleNotification.put("notification",datum.getNotification());
                            singleNotification.put("date",datum.getDate());
                            notifications.add(singleNotification);
                        }
                        view.setListToAdapter(notifications);
                    }
                }

                @Override
                public void onFailure(Call call, Throwable t) {
                    call.cancel();
                }
            });
        }
    }

}

我如何编写单元测试来涵盖这段代码的所有情况。

谢谢

【问题讨论】:

    标签: android unit-testing junit mockito retrofit2


    【解决方案1】:

    当您想测试来自服务 (API) 的不同响应时,最好模拟它并返回您需要的内容。

        @Test
        public void testApiResponse() {
          ApiInterface mockedApiInterface = Mockito.mock(ApiInterface.class);
          Call<UserNotifications> mockedCall = Mockito.mock(Call.class);
    
          Mockito.when(mockedApiInterface.getNotifications()).thenReturn(mockedCall);
    
          Mockito.doAnswer(new Answer() {
            @Override
            public Void answer(InvocationOnMock invocation) throws Throwable {
              Callback<UserNotifications> callback = invocation.getArgumentAt(0, Callback.class);
    
              callback.onResponse(mockedCall, Response.success(new UserNotifications()));
              // or callback.onResponse(mockedCall, Response.error(404. ...);
              // or callback.onFailure(mockedCall, new IOException());
    
              return null;
            }
          }).when(mockedCall).enqueue(any(Callback.class));
    
          // inject mocked ApiInterface to your presenter
          // and then mock view and verify calls (and eventually use ArgumentCaptor to access call parameters)
        }
    

    【讨论】:

    • 非常感谢老兄......它真的拯救了我的一天
    • 能否请你解释一下 Mockito.doAnswer(something..)
    • 您使用该符号来指定模拟行为。它类似于 Mockito.when(mockObject).someMethod(any(Parameter.class)).thenReturn(returnValue);但是这个必须用于没有(void)返回类型的函数。见testing.googleblog.com/2014/03/…
    • 这很好用,但是存在会导致测试失败的竞争条件。一旦调用 enqueue,单元测试将继续并尝试验证尚未调用的内容。有没有办法让回调先完成?
    • @ShrimpCrackers 您确定将模拟的 ApiInterface 注入到您的生产代码中吗?因为上面介绍的代码只指定了 mock 的行为,它实际上并没有执行任何东西。
    【解决方案2】:

    对于那些使用 Kotlin 和 MockK 寻找答案的人:

    假设你有这样的东西:

    class ApiService(private val client: OkHttpClient) {
        
        fun makeApiCall() {
            val url = "https://someendpoint.com.br/"
            val request = Request.Builder().url(url).build()
            client.newCall(request).enqueue(object : Callback {
                override fun onFailure(call: Call, exception: IOException) {
                    //Logic to handle Failure
                }
    
                override fun onResponse(call: Call, response: Response) {
                    //Logic to handle Success
                }
            })
        }
    }
    

    您可以使用 Junit 5 和 mockK 进行测试

    class ApiServiceTest {
    
        private lateinit var client: okhttp3.OkHttpClient
        private lateinit var apiService: ApiService
    
        @BeforeEach
        fun setup() {
            // Setup a new mock for each test case
            client = mockk(relaxed = true)
            apiService = ApiService(client)
        }
    
        @Test
        fun `test with mockedCallback`() {
            val mockedCall = mockk<Call>(relaxed = true) 
    
            every { mockedCall.enqueue(any()) } answers {
               //Get the callback from the arguments
                val callback = args[0] as Callback 
               // Create a fakeRequest 
               val fakeRequest =    okhttp3.Request.Builder().url("https://someendpoint.com.br/").build()
                // Create the response you need to test, it can be failure or success
                val errorResponse = Response.Builder()
                        .request(fakeRequest)
                        .protocol(Protocol.HTTP_1_1)
                        .code(400)
                        .message("Error")
                        .build()
                //Call the callback with the mocked response you created.
                callback.onResponse(mockedCall, errorResponse)
            }
    
            // Setup the client mock to return the mocked call you created
            every {
                client.newCall(any())
            } returns mockedCall
    
           apiService.makeApiCall()
           
           // Verify whatever you need to test your logic to handle each case.
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2016-09-10
      • 1970-01-01
      • 2016-10-20
      • 2020-09-05
      • 2017-05-09
      • 1970-01-01
      • 1970-01-01
      • 2014-10-22
      • 2020-09-11
      相关资源
      最近更新 更多