使用Mockito测试servlet API

时间:2016-07-19 09:38:08

标签: java servlets mockito

我有AddBookController

public class BookAddController extends HttpServlet {

    @EJB(name = "BuyerServiceEJB")
    private BuyerServiceInterface buyerService;

    @EJB(name = "AuthorServiceEJB")
    private AuthorServiceInterface authorService;

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Book newBook = new Book();
        newBook.setName(request.getParameter("name"));
        newBook.setAuthor(authorService.findAuthorById(Long.parseLong(request.getParameter("author_id"))));
        newBook.setBought(Boolean.parseBoolean(request.getParameter("sold")));
        newBook.setGenre(request.getParameter("genre"));
        newBook.setPrice(Integer.parseInt(request.getParameter("price")));
        buyerService.addNewBook(newBook);
        doGet(request, response);
    }

我有测试课:

public class BookAddControllerTest {

final static Logger logger = Logger.getLogger(UserServiceBean.class);
private Book testBook = new Book();
private Author testAuthor = new Author();

@Before
public void precondition(){
    logger.info("Add Book Controller Test");
    testBook.setName("Возера Радасці");
    testAuthor.setAuthor_id(1L);
    testBook.setAuthor(testAuthor);
    testBook.setBought(false);
    testBook.setGenre("postmodernism");
    testBook.setPrice(15);
}

@Test
public void testDoPost() {
    logger.info("Test post request processing");
    BookAddController bookAddController = Mockito.mock(BookAddController.class);
    HttpServletRequest testRequest = Mockito.mock(HttpServletRequest.class);
    HttpServletResponse testResponse = Mockito.mock(HttpServletResponse.class);
    BuyerServiceInterface buyerServiceInterface = Mockito.mock(BuyerServiceInterface.class);
    AuthorServiceInterface authorServiceInterface = Mockito.mock(AuthorServiceInterface.class);

    when(testRequest.getParameter("name")).thenReturn("Возера Радасці");
    when(testRequest.getParameter("author_id")).thenReturn("1");
    when(testRequest.getParameter("sold")).thenReturn("false");
    when(testRequest.getParameter("genre")).thenReturn("postmodernism");
    when(testRequest.getParameter("price")).thenReturn("15");

    bookAddController.doPost(testRequest, testResponse);

    verify(buyerServiceInterface, times(1)).addNewBook(testBook);
    verify(authorServiceInterface, times(1)).findAuthorById(Long.parseLong("1"));
    verify(bookAddController, times(1)).doGet(testRequest, testResponse);

}

我收到例外:

java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/servlet/ServletException

    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
    at java.lang.Class.getMethod0(Class.java:3018)
    at java.lang.Class.getMethod(Class.java:1784)
    at org.junit.internal.builders.SuiteMethodBuilder.hasSuiteMethod(SuiteMethodBuilder.java:18)
    at org.junit.internal.builders.SuiteMethodBuilder.runnerForClass(SuiteMethodBuilder.java:10)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:98)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
    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 com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

问题:

  1. 我是否使用正确的方法测试doPost方法?
  2. 如何修复此异常?

2 个答案:

答案 0 :(得分:3)

由于用户@SMA意味着你实际上没有测试任何东西,因为你只是在模拟代码上运行。您要做的是创建BookAddController的实例,并在您致电HttpServletRequest时传递HttpServletResponsedoPost的模拟实例。否则,您不会使用您编写的任何代码,因此您无法知道您编写的实际代码是否有效或是否发生任何回归。

至于异常,我怀疑junit可能无法访问java ee api。在您的pom中包含javaEE,它可能会起作用。请查看此question了解详情

答案 1 :(得分:2)

这是一个令人兴奋的问题,因为Oracle没有为许多类提供方法体 - 所有你得到的就是API,这使得无法实际使用JAR文件来运行任何东西。有关详情,请参阅此JBoss Wiki article

  

这个问题可能是你的测试类路径上有javax:javeee-api(或javax:javaee-web-api)库的结果。如果您有Maven项目,则可能在POM文件中具有以下依赖项:

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-api</artifactId>
  <version>6.0</version>
</dependency>
     

或者:

<dependency>  
  <groupId>javax</groupId>  
  <artifactId>javaee-web-api</artifactId>  
  <version>6.0</version>  
</dependency>  
     

此依赖项为您提供Java EE API,而不是实现。虽然此依赖项适用于编译,但它不能用于执行代码(包括测试)。

     

背景

     

当这些工件发布到Maven存储库时,Sun / Oracle从类别&#34;实现&#34;中删除了代码。所以所有接口都是代码完整的,但是任何抽象类或实现类都没有代码。任何使用该类的尝试都可能会爆炸。

     

以下是关于Sun / Oracle为何这样做的论点的摘录:

     
    

当编译时,他们也希望运行。顺便说一句,我们一直在推广只能用于编译的全套Java EE API - 它们被剥离了方法细节。这样,用户就无法获取这些工件并尝试在运行时使用它。

  
     

基本上,javax:javaee-api和javax:javaee-web-api Maven工件并不是真正的交易 - 它们是“存根”。它们非常适合轻松提供编译所需的依赖项,但是当您需要执行某些操作时它们会完全断开。

     

解决方法:不要使用javax.javaee-api或javax:javaee-web-api工件!相反,使用&#34; real&#34;你需要的文物。遗憾的是,没有Maven作用域可以从测试类路径中排除依赖项(尽管在surefire插件中有一个classpathDependencyExcludes配置选项,用于在测试期间将它们列入黑名单)。

解决方法是使用以下内容:

<dependency> 
    <groupId>org.apache.geronimo.specs</groupId>
    <artifactId>geronimo-servlet_3.0_spec</artifactId>
    <version>1.0</version>
    <scope>test</scope>
</dependency>

或JBoss版本,或包含您正在使用的类的实际实现的其他版本。