【问题标题】:Can you mock a non static method that calls a static method using Mockito?你能模拟一个使用 Mockito 调用静态方法的非静态方法吗?
【发布时间】:2013-01-08 17:47:14
【问题描述】:

我了解您不能使用 mockito 模拟静态方法。但是我试图模拟的方法不是静态的,但它调用了静态方法。那么我可以模拟这个方法吗?

运行测试时出现异常。对静态方法的调用是此异常的原因吗?

要测试的类:

public class MyAction{
        public ActionForward search(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
            MyService service = new MyService();
            **form.setList(service.getRecords(searchRequest));**
        }
    }

模拟类和方法:

public class MyService{
    public List getRecords(SearchRequest sr){
        List returnList = new ArrayList();
        MyDao dao = DAOFactory.getInstance().getDAO();---->Call to static method
        // Some codes
        return returnList;
    }
}

带有静态方法的类:

public class DAOFactory{
        private static DAOFactory instance = new DAOFactory();

         public static DAOFactory getInstance() {------> The static method
            return instance;
        }       
    }

我的测试:

@Test
public void testSearch() throws Exception{
    MyService service = mock(MyService.class);
    MyAction action = new MyAction();

    List<MyList> list = new ArrayList<MyList>();
    when(service.getRecords(searchRequest)).thenReturn(list);
    ActionForward forward = action.search(mapping, form, request, response);        
}

这是我运行测试时的堆栈跟踪。请记住,为简单起见,我更改了类的名称。

【问题讨论】:

    标签: java unit-testing mocking tdd mockito


    【解决方案1】:

    问题是您的search 方法无法使用服务模拟,因为它通过MyService service = new MyClass(); 创建了自己的实例。因此,您必须重构 MyAction 类以允许 MyService 注入并在其中注入模拟。或者使用更重的武器 - PowerMock。

    最简单、最安全的重构

    在您的 IDE 中使用“提取方法”重构来提取构造“new MyClass()”。所以会变成:

    public class MyAction{
            public ActionForward search(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
                MyService service = getMyService();
                **form.setList(service.getRecords(searchRequest));**
            }
            MyService getMyService() {
              return new MyClass();
            }
    
        }
    

    然后在您的单元测试中,您可以通过创建内部子类来注入模拟:

    public class MyActionTest {
       private MyService service = mock(MyService.class);
       private MyAction action = new MyAction(){
           @Override
           MyService getMyService() {return service;}
       };
    
        @Test
        public void testSearch() throws Exception{
    
            List<MyList> list = new ArrayList<MyList>();
            when(service.getRecords(searchRequest)).thenReturn(list);
            ActionForward forward = action.search(mapping, form, request, response);        
        }
    
    }
    

    【讨论】:

    • Doh - 我一直在寻找更复杂的情况,完全忽略了服务模拟未使用的事实......
    • @JonSkeet 嘿嘿...他是一个学生... :)
    • 虽然重构不是一个选项,因为我被要求为一些遗留代码编写测试,我将如何重构它以便我可以注入模拟?谢谢
    • @javaStudent 我在答案中添加了一种方法
    • @javaStudent 一个被模拟的方法根本不会被执行,所以不管它做什么。
    猜你喜欢
    • 1970-01-01
    • 2014-08-30
    • 2014-02-02
    • 1970-01-01
    相关资源
    最近更新 更多