mockito 错误代码:检测到未完成的存根

muk1a3rh  于 2022-11-08  发布在  其他
关注(0)|答案(3)|浏览(136)

我想用mockito和powermockito为下面的Spring MVC控制器编写单元测试用例。

@Controller
@Configuration
@PropertySource("classpath:project/web/properties/RealTimeAPI.properties")
@RequestMapping("/learnon")
public class ClassManagerController {

    private final Logger logger = Logger.getLogger(ClassManagerController.class);
    @Autowired
    private ClassManagerService classManagerService;

    @Autowired
    private GroupUserService groupUserService;

    @RequestMapping(value = "/teacher", method = RequestMethod.GET)
    public ModelAndView showClassDetail(HttpServletRequest request, HttpSession httpSession,
                                        @RequestParam(value = "isbn", required = false) String isbn13,
                                        @RequestParam(value = "classId", required = false) Long classId) {

        String redirectUrl = "https://example.com/jsp/Login.jsp?reason=failedLogin&redirectUri=https://example.com/secure/Bookshelf";
        String accessDeniedUri = "https://example.com/jsp/AccessDenied.jsp";

        if (httpSession.getAttribute("USERID") == null) {
            return new ModelAndView("redirect:" + redirectUrl);
        }
        try {
            long userId = Long.parseLong(httpSession.getAttribute("USERID").toString());
            UserBean user = classManagerService.getUser(userId);
            if (httpSession.getAttribute("SCHOOLID") == null) {
                httpSession.setAttribute("SCHOOLID", user.getSchoolId());
            }
            if (httpSession.getAttribute("FULLFILLMENT_YEAR") == null) {
                httpSession.setAttribute("FULLFILLMENT_YEAR", user.getFulfillmentYear());
            }
            String isbn10 = ISBNUtil.convertIsbn13ToIsbn10(isbn13);
            String title = "";

            ModelAndView mav = null;
            ClassManagerBean classBean = null;

            if(classId == null && httpSession.getAttribute("classId") != null){
                classId = (Long)httpSession.getAttribute("classId");
            }

            if(classId != null && classId > 0) {
                List<UserBean> userBeanList = classManagerService.getUserList(user.getSchoolId(), classId, isbn10);
                classBean = classManagerService.getClassById(classId);
                classBean.setUserNumber(userBeanList.size());
                title = classBean.getTitle();
                //Set the view to ClassManager.jsp
                mav = new ModelAndView("ClassManager");
                mav.addObject("userList", userBeanList);
                boolean authorized = userBeanList.stream().anyMatch(u->u.getUserId() == userId);
                if(!authorized){
                    ModelAndView modelAndView = new ModelAndView("redirect:" + accessDeniedUri);
                    modelAndView.addObject("accessDenied", "true");
                    return modelAndView;
                }
            }else{
                title = classManagerService.getTitle(isbn10);
                //Set the view to createNewClass.jsp
                mav = new ModelAndView("CreateNewClass");
                classBean = new ClassManagerBean();
                classBean.setLo2Flag(true);
                classBean.setIsbn(isbn10);
                classBean.setTitle(title);
            }
            httpSession.setAttribute("searchTitle", title);
            httpSession.setAttribute("selectedIsbn", isbn10);
            httpSession.setAttribute("classId", classId);
            mav.addObject("user", user);
            mav.addObject("classBean", classBean);
            return mav;
        } catch (Exception ex) {
            ModelAndView mav2 = new ModelAndView("redirect:" + accessDeniedUri);
            mav2.addObject("accessDenied", "true");
            logger.error("Exception Occurred, Redirecting to Access Denied...", ex);
            return mav2;
        }
    }

}

我已经为上面的类编写了下面的单元测试用例,当我试图运行测试时,我在运行时得到了一个UnfinishedStubbingException异常。

@Test
    public void testShowClassDetail1() throws Exception {

        HttpServletRequest httpRequest = mock(HttpServletRequest.class);
        HttpSession httpSession = mock(HttpSession.class);

        Mockito.when(httpSession.getAttribute("USERID")).thenReturn(new String("anyString"));

        List<UserBean> list = new ArrayList<UserBean>();
        List<UserBean> spyList = Mockito.spy(list);

        Mockito.when(classManagerService.getUserList(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString())).thenReturn(spyList);

        doReturn(false).when(spyList.stream().anyMatch(u->u.getUserId() == Mockito.anyLong()));

        RequestBuilder request = MockMvcRequestBuilders
                .get("/learnon/teacher")
                .param("isbn", "1234567890123")
                .param("classId", "1")  
                .accept(MediaType.APPLICATION_JSON);

        String modalView = "redirect:" + "https://www.example.com/jsp/AccessDenied.jsp";

        ResultActions result = mockMvc.perform(request)
                .andExpect(status().is3xxRedirection())
                .andExpect(view().name(modalView));

       }

例外状况:

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
-> at learnonclassmanager.spring.web.controller.ClassManagerControllerTest.testShowClassDetail1(ClassManagerControllerTest.java:98)

E.g. thenReturn() may be missing.
Examples of correct stubbing:
    when(mock.isOk()).thenReturn(true);
    when(mock.isOk()).thenThrow(exception);
    doThrow(exception).when(mock).someVoidMethod();
Hints:
 1. missing thenReturn()
 2. you are trying to stub a final method, you naughty developer!
 3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed

    at learnonclassmanager.spring.web.controller.ClassManagerControllerTest.testShowClassDetail1(ClassManagerControllerTest.java:98)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:316)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:300)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:131)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.access$100(PowerMockJUnit47RunnerDelegateImpl.java:59)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner$TestExecutorStatement.evaluate(PowerMockJUnit47RunnerDelegateImpl.java:147)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.evaluateStatement(PowerMockJUnit47RunnerDelegateImpl.java:107)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:288)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:208)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:147)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:121)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:123)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:121)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
    at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)

我已尝试解决例外状况,但仍然失败。请协助我解决此例外状况。
编辑:
我删除了下面的代码行doReturn(false).when(spyList. stream().anyMatch(u-〉u.getUserId()== Mockito.anyLong()));
并按如下方式更新测试。

@Test
    public void testShowClassDetail1() throws Exception {

        HttpServletRequest httpRequest = mock(HttpServletRequest.class);
        HttpSession httpSession = mock(HttpSession.class);

        Mockito.when(httpSession.getAttribute("USERID")).thenReturn(1l);

        UserBean beanMock = mock(UserBean.class);

        Mockito.when(classManagerService.getUser(1l)).thenReturn(beanMock);

        List<UserBean> beanList = new ArrayList<>(); 
        beanList.add(beanMock); 

        Mockito.when(classManagerService.getUserList(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString())).thenReturn(beanList);

        Mockito.when(beanMock.getUserId()).thenReturn(2l);

        RequestBuilder request = MockMvcRequestBuilders
                .get("/learnon/teacher")
                .param("isbn", "1234567890123")
                .param("classId", "1")  
                .accept(MediaType.APPLICATION_JSON);

        String modalView = "redirect:" + "https://example.com/jsp/AccessDenied.jsp";

        ResultActions result = mockMvc.perform(request)
                .andExpect(status().is3xxRedirection())
                .andExpect(view().name(modalView));

       }

现在我得到以下AssetionError。
java.lang.AssertionError:视图名称应为:https://example.com/jsp/AccessDenied.jsp〉但实际为:https://example.com/jsp/Login.jsp?reason=failedLogin&redirectUri=https:/

gtlvzcf8

gtlvzcf81#

让我们看一下下面这行代码(为了便于阅读而进行了格式化):

doReturn(false).when(
    spyList.stream()
           .anyMatch(u->u.getUserId() == Mockito.anyLong())
);

它包含两个错误,一个是由异常报告的,但我还将解释另一个

错误1:对实对象的方法调用传递给when

为了记录方法调用,你必须将模拟的方法调用结果传递给when函数。
请参阅How does mockito when() invocation work?

错误2:参数匹配器的使用无效

不能将Mockito.anyLong()用作任意值。这会进行编译,因为ArgumentMatchers返回伪值(零),但不会按预期工作(与anyLong()的比较并不总是为真)。
请参阅How do Mockito matchers work?

溶液

模拟的目的是强制在测试方法中满足某些条件。您的目的是在通过id查找用户时返回false。要实现这一点,只需使用空列表。这意味着可以删除整个有问题的行。

kdfy810k

kdfy810k2#

@RunWith(PowerMockRunner.class)
public class ClassManagerControllerTest {

    @Mock
    public ClassManagerService classManagerService;

    @InjectMocks
    public ClassManagerServiceImpl classManagerServiceImpl;

    @Mock
    public GroupUserService groupUserService;

    private MockMvc mockMvc;

    @InjectMocks
    private ClassManagerController classManagerController;

    @Before
    public void setUp()  {

        mockMvc = MockMvcBuilders.standaloneSetup(classManagerController).build();
        MockitoAnnotations.initMocks(this);
    }

@Test
    public void testShowClassDetail1() throws Exception {

        UserBean beanMock = mock(UserBean.class);

        Mockito.when(classManagerService.getUser(1l)).thenReturn(beanMock);

        List<UserBean> beanList = new ArrayList<>(); 
        beanList.add(beanMock); 

        Mockito.when(classManagerService.getUserList(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString())).thenReturn(beanList);

        Mockito.when(beanMock.getUserId()).thenReturn(2l);

        RequestBuilder request = MockMvcRequestBuilders
                .get("/learnon/teacher")
                .param("isbn", "1234567890123")
                .param("classId", "1")  
                .sessionAttr("USERID", 1l)
                .accept(MediaType.APPLICATION_JSON);

        String modalView = "redirect:" + "https://www.example.com/jsp/AccessDenied.jsp";

        ResultActions result = mockMvc.perform(request)
                .andExpect(status().is3xxRedirection())
                .andExpect(view().name(modalView));

       }

}
5vf7fwbs

5vf7fwbs3#

对我来说,这个问题是由于stubbing the behaviour of another mock inside before 'thenReturn' instruction is completed造成的
旧代码为:

Mockito.doReturn(Unit).whenever(mockObj)
            .login(userObject, spyMainActivityViewModel.onLoginListener)

我把它改成了

val listener = spyMainActivityViewModel.onLoginListener
Mockito.doReturn(Unit).whenever(mockObj)
            .login(userObject, listener)

https://github.com/mdhpuri355/Mockito/blob/master/src/main/java/org/mockito/internal/exceptions/Reporter.java .请参阅unfinishedStubbing() * 提示3*)

相关问题