7种方法检测安卓模拟器
无论您选择采用TDD还是采用更传统的方法,都将单独测试类。 这通常意味着您使用接口进行编码,并将依赖项注入类中。
去年,我不得不测试一个servlet。 Servlet是展示容器外测试的展示柜,因为它们依赖于doXxx()
方法中的所述容器。 这些方法中的每一个都依赖于HttpServletRequest
和HttpServletResponse
。 两者都是在Servlet API中没有具体实现的接口。 因此,基本上,在您的测试中,您会遇到三个选择:
- 使用您的容器实现(糟糕!),
- 创建自己的实现(相当耗时),
- 使用没有依赖关系的已有实现。
选择第三个选项,我发现了这个小小的珠宝MockRunner 。 MockRunner为您提供以下API和框架的模拟实现:
- 日本国家发展研究院
- EJB2,
- JDBC,
- JMS,
- Servlet,
- TagLib,
- Struts
所有这些模拟实现均应按需运行。 例如,如果在测试开始时将对象放入模拟会话中,并且此后仍然处于同一请求中,则可以检查对象是否仍在此处。 让我们考虑以下servlet方法进行测试:
@Override
protectedvoiddoGet(HttpServletRequestaRequest,HttpServletResponseaResponse)throwsServletException,IOException{
Stringaction=aRequest.getParameter("action");
HttpSessionsession=aRequest.getSession();
Objectobject=session.getAttribute("number");
intnumber=object==null?0:(Integer)object;
if("add".equals(action)){
number++;
session.setAttribute("number",number);
}elseif("remove".equals(action)){
number--;
session.setAttribute("number",number);
}elseif("reset".equals(action)){
session.setAttribute("number",0);
}
}
您如何使用MockRunner测试此代码? 第一件事是从com.mockrunner.servlet.BasicServletTestCaseAdapter
继承。 代码如下:
publicclassMockRunnerTestextendsBasicServletTestCaseAdapter{
/**
* Setup the servlet to test.
*/
@Override
publicvoidsetUp()throwsException{
super.setUp();
createServlet(SessionServlet.class);
}
/**
* Test method for
* {@link SessionServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}.
*
* @throws IOException
* @throws ServletException
*/
publicvoidtestDoGetAdd()throwsServletException,IOException{
addRequestParameter("action","add");
doGet();
Objectobject=getSessionAttribute("number");
assertTrue(Integer.class.isAssignableFrom(object.getClass()));
intnumber=(Integer)object;
assertEquals(1,number);
}
/**
* Test method for
* {@link SessionServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}.
*
* @throws IOException
* @throws ServletException
*/
publicvoidtestDoGetRemove()throwsServletException,IOException{
addRequestParameter("action","remove");
doGet();
Objectobject=getSessionAttribute("number");
assertTrue(Integer.class.isAssignableFrom(object.getClass()));
intnumber=(Integer)object;
assertEquals(-1,number);
}
/**
* Test method for
* {@link SessionServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}.
*
* @throws IOException
* @throws ServletException
*/
publicvoidtestDoGetReset()throwsServletException,IOException{
addRequestParameter("action","reset");
doGet();
Objectobject=getSessionAttribute("number");
assertTrue(Integer.class.isAssignableFrom(object.getClass()));
intnumber=(Integer)object;
assertEquals(0,number);
}
}
像这样测试我们的servlet很好,尽管使用MockRunner有一些限制:
- 您必须继承MockRunner的基类,
- 因此,您将不得不使用JUnit v3。 您也不能使用TestNG或JUnit v4,
- 开箱即用的测试功能有限。 如果出于某种原因,您决定创建一个具有其他功能的超级servlet,则必须扩展MockRunner框架。 同样,如果出现新的API,则必须创建自己的适配器,
- 您必须记住在
setUp()
方法中调用super.setUp()
(确实不是一个好主意), - 最后但并非最不重要的一点是,自08年夏季以来,MockRunner项目一直没有任何活动。
然后我对Mockito产生了兴趣。 Mockito是一个“真正的” Mock框架,可增强您的接口,并使用CGLIB进行分类以提供方法的存根:即,您提供了链接到方法调用的代码。 如今,Mockito似乎非常炒作,但趋势仍在流行。 以下代码向您展示了使用Mockito制作的测试类:
publicclassMockitoTest{
/** Servlet under test. */
privateSessionServletservlet;
/** Mock request. */
privateHttpServletRequestrequest;
/** Mock response. */
privateHttpServletResponseresponse;
/** Mock session. */
privateHttpSessionsession;
/** Session's attribute map. */
privateMapattributes;
/** Request's parameter map. */
privateMapparameters;
/**
* Launches Mockito configuration from annotations.
*/
@Before
publicvoidsetUp(){
attributes=newHashMap();
parameters=newHashMap();
servlet=newSessionServlet();
request=mock(HttpServletRequest.class);
response=mock(HttpServletResponse.class);
session=mock(HttpSession.class);
when(request.getSession()).thenReturn(session);
when(request.getParameterMap()).thenReturn(parameters);
when(request.getParameter(anyString())).thenAnswer(newAnswer(){
/**
* @see org.mockito.stubbing.Answer#answer(org.mockito.invocation.InvocationOnMock)
*/
@Override
publicObjectanswer(InvocationOnMockaInvocation)throwsThrowable{
Stringkey=(String)aInvocation.getArguments()[0];
returnparameters.get(key);
}
});
when(session.getAttribute(anyString())).thenAnswer(newAnswer(){
/**
* @see org.mockito.stubbing.Answer#answer(org.mockito.invocation.InvocationOnMock)
*/
@Override
publicObjectanswer(InvocationOnMockaInvocation)throwsThrowable{
Stringkey=(String)aInvocation.getArguments()[0];
returnattributes.get(key);
}
});
Mockito.doAnswer(newAnswer(){
/**
* @see org.mockito.stubbing.Answer#answer(org.mockito.invocation.InvocationOnMock)
*/
@Override
publicObjectanswer(InvocationOnMockaInvocation)throwsThrowable{
Stringkey=(String)aInvocation.getArguments()[0];
Objectvalue=aInvocation.getArguments()[1];
attributes.put(key,value);
returnnull;
}
}).when(session).setAttribute(anyString(),anyObject());
}
/**
* Test method for
* {@link SessionServlet#doGet(HttpServletRequest, HttpServletResponse)} .
*
* @throws IOException
* @throws ServletException
*/
@Test
publicvoidtestDoGetAdd()throwsServletException,IOException{
parameters.put("action","add");
servlet.doGet(request,response);
Objectobject=attributes.get("number");
assertNotNull(object);
assertTrue(Integer.class.isAssignableFrom(object.getClass()));
intnumber=(Integer)object;
assertEquals(1,number);
}
/**
* Test method for
* {@link SessionServlet#doGet(HttpServletRequest, HttpServletResponse)} .
*
* @throws IOException
* @throws ServletException
*/
@Test
publicvoidtestDoGetRemove()throwsServletException,IOException{
parameters.put("action","remove");
servlet.doGet(request,response);
Objectobject=attributes.get("number");
assertNotNull(object);
assertTrue(Integer.class.isAssignableFrom(object.getClass()));
intnumber=(Integer)object;
assertEquals(-1,number);
}
/**
* Test method for
* {@link SessionServlet#doGet(HttpServletRequest, HttpServletResponse)} .
*
* @throws IOException
* @throws ServletException
*/
@Test
publicvoidtestDoGetReset()throwsServletException,IOException{
parameters.put("action","reset");
servlet.doGet(request,response);
Objectobject=attributes.get("number");
assertNotNull(object);
assertTrue(Integer.class.isAssignableFrom(object.getClass()));
intnumber=(Integer)object;
assertEquals(0,number);
}
}
Mockito严重缺乏针对新手的文档。 尽管Mockito类有充分的文献资料,但我认为应该对哲学和体系结构进行外部介绍。 不过,这是我可以解决的许多Google Code项目的限制。
恕我直言,MockRunner与API集成程度更高,隐藏了实现细节,而对于Mockito,您需要了解实现以提供足够的存根。 Mockito的测试类的代码大小约为MockRunner的两倍。 因此,尽管会迫使我使用旧版本的JUnit,并在没有此类约束的情况下使用Mockito,但我将继续使用MockRunner来使用其集成API的类。
总之,我承认这两种测试工具都具有截然不同的方法和范围。 我仍然觉得Mockito的使用有点复杂。
使自己的想法:
翻译自: https://blog.frankel.ch/two-different-mocking-approaches/
7种方法检测安卓模拟器