【问题标题】:How to use Mockito.verify() on static methods?如何在静态方法上使用 Mockito.verify()?
【发布时间】:2021-01-29 21:59:15
【问题描述】:

我正在研究 Junit 和 Mockito。在我的项目中,我有一个 SocialDataAccess 控制器,其代码如下所示:

public class SocialDataAccessController implements Controller{

private SocialAuthServiceProvider socialAuthServiceProvider;

@Override
    public ModelAndView handleRequest(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        String provider = request.getParameter("pId");
        String appCode =  request.getParameter("apc");

         * check if data in session is of the same provider orof different
         * provider, if different then remove auth and request token
         **/

        SocialUtility.removeOtherProviderAuthTokenFromSession(request,provider);

        try {
            /** creating the OAuthService object based on provider type **/
            OAuthService service = getSocialAuthServiceProvider().getOAuthServiceProvider(appCode, provider);
            .....
            ........
            ............            
return new ModelAndView("redirect:callback.html?pId=" + provider);
    }

public SocialAuthServiceProvider getSocialAuthServiceProvider() {
        return socialAuthServiceProvider;
    }

}

这就是我所做的。我提出了一个请求,我的请求成功调用了我的控制器。当我尝试使用Mockito.verify() 来测试我的静态方法是否被调用时,出现如下所示的错误。

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(
        locations={
            "file:/opt/div/BatchWorkspace/harvest_branch/WebContent/WEB-INF/test-servlet.xml"
        }
)

public class TestSocialDataAccessController {   
    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();      
    }


    @SuppressWarnings("static-access")
    @Test
    public void testBasicSetUp() throws Exception{
        RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/social-connect.html")
                .param("apc","tj")
                .param("src","google")
                .param("pId","ggl")
                .param("cl","xxxxxxxxxxxxxx");

        mockMvc.perform(requestBuilder)
       .andDo(MockMvcResultHandlers.print())
       .andExpect(MockMvcResultMatchers.status().isMovedTemporarily())
       .andExpect(MockMvcResultMatchers.redirectedUrl("xxxxxxxx"));

           SocialUtility sutil = new SocialUtility();
           SocialUtility spy = Mockito.spy(sutil);
           MockHttpServletRequest request = requestBuilder.buildRequest(wac.getServletContext());
           Mockito.verify(spy).removeOtherProviderAuthTokenFromSession(request,Matchers.anyString());          

    }
}

我得到的错误:

org.mockito.exceptions.misusing.UnfinishedVerificationException: 
Missing method call for verify(mock) here:
-> at com.tj.harvest.testcase.TestSocialDataAccessController.testBasicSetUp(TestSocialDataAccessController.java:88)

Example of correct verification:
    verify(mock).doSomething()

Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.

    at com.tj.harvest.testcase.TestSocialDataAccessController.testBasicSetUp(TestSocialDataAccessController.java:89)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597).

我的问题是:

  1. 我可以在我的方法removeOtherProviderAuthTokenFromSession(request,provider) 上使用Mockito.verify()。如果“是”怎么办? & 如果“否”,为什么? SocialUtility 是类名,方法是静态的。请求与到达控制器的请求相同。提供者是一个字符串。我不想使用 PowerMockito。

  2. 我还想在getOAuthServiceProvider(appCode, provider) 上使用验证。 我该怎么做?

任何帮助都将不胜感激。

【问题讨论】:

  • Mockito 无法处理静态方法。为此,您需要使用 PowerMock 或类似的框架。
  • 重构你的代码。需要模拟或验证静态方法是错误代码的标志。 PowerMock 不是我认为的典型工具,它是测试不良代码的一种解决方法。为了同时使用它们,我倾向于手动创建 Spring 上下文,而不是通过 Spring 运行器,因为 PowerMock 运行器需要做更脏的工作,而这些工作不能轻易放在其他地方。
  • 您要验证的对象来自您的socialAuthServiceProvider。因此,您必须对其进行模拟或将 OAuthServiceProvider 的间谍版本放在那里,以便方法调用将返回它。嘲笑它似乎更容易,但可能有副作用。
  • 我试过这个,但它说假设被调用但没有被调用 SocialAuthServiceProvider sap=new SocialAuthServiceProvider(); SocialAuthServiceProvider 间谍=Mockito.spy(sap); Mockito.verify(spy).getOAuthServiceProvider(Mockito.isA(String.class), Mockito.isA(String.class));
  • 我试过这个仍然不起作用 SocialAuthServiceProvider sasp=Mockito.mock(SocialAuthServiceProvider.class); Mockito.verify(sasp).getOAuthServiceProvider(Mockito.isA(String.class), Mockito.isA(String.class));错误:需要但未调用:socialAuthServiceProvider.getOAuthServiceProvider(isA(java.lang.String), isA(java.lang.String)); @FlorianSchaetz

标签: java mockito


【解决方案1】:

使用 Mockito 验证静态方法 -> MockedStatic。

如果方法有参数并且你想验证它,那么它将通过这种方式进行验证:

@Test
void testMethod() {
  try (MockedStatic<StaticProperties> theMock = Mockito.mockStatic(StaticProperties.class)) {
    theMock.when(StaticProperties.getProperty("abc", "xyz", "lmn"))).thenReturn("OK");

    //code .....

    theMock.verify(() -> StaticProperties.getProperty("abc", "xyz", "lmn"));
  }
  
}

【讨论】:

  • 我看到这个被赞成并且最近得到了回答,但是,我看到这种在模拟对象上调用验证的方式现在已被弃用。
【解决方案2】:
  1. 您必须使用 PowerMockito,因为仅此 Mockito 无法验证这一点

    PowerMockito.doNothing().when(SocialUtility.class,
                                  "removeOtherProviderAuthTokenFromSession", 
                                  any(MockHttpServletRequest.class),
                                  anyString());
    
  2. 您可以模拟您的getSocialAuthServiceProvider() 或在您调用SocialDataAccessController 时窥探它

【讨论】:

  • SocialDataAccessController mocksdac=Mockito.mock(SocialDataAccessController.class); SocialAuthServiceProvider sasp=Mockito.mock(SocialAuthServiceProvider.class); Mockito.when(mocksdac.getSocialAuthServiceProvider()).thenReturn(sasp); Mockito.verify(sasp).getOAuthServiceProvider(Mockito.isA(String.class), Mockito.isA(String.class));还是没有成功
【解决方案3】:

关于你的第二个问题:

我还想在 getOAuthServiceProvider(appCode, provider) 上使用验证。我该怎么做?

答案可能是这样的:

Mockito.verify(this.getSocialAuthServiceProvider())
       .getOAuthServiceProvider(Mockito.isA(String.class), Mockito.isA(String.class));

如果我遗漏了什么,请告诉我。

【讨论】:

  • Myeclipse 无法识别此行在 this.getSocialAuthServiceProvider() 下显示红色标记,我怀疑这是否是正确的做法。
  • 其实 this.getSocialAuthServiceProvider() 不是一个模拟对象,并验证以模拟对象作为参数
  • 所以我认为你必须模拟 this.getSocialAuthServiceProvider()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-28
相关资源
最近更新 更多