Java 用Mockito测试servlet API

Java 用Mockito测试servlet API,java,servlets,mockito,Java,Servlets,Mockito,我有AddBookController: public class BookAddController extends HttpServlet { @EJB(name = "BuyerServiceEJB") private BuyerServiceInterface buyerService; @EJB(name = "AuthorServiceEJB") private AuthorServiceInterface authorService;

我有
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)
问题:

  • 我是否使用正确的测试方法
    doPost
    方法
  • 如何修复此异常
    正如user@SMA所暗示的那样,实际上您并没有测试任何东西,因为您只是在模拟代码上操作。您要做的是创建
    BookAddController
    的实例,并可能在调用
    doPost
    时传入
    HttpServletRequest
    HttpServletResponse
    的模拟实例。否则,您不会执行您所编写的任何代码,因此您无法知道您所编写的实际代码是否有效,或者是否发生了任何回归


    至于例外情况,我怀疑可能是
    junit
    没有访问javaeeapi的权限。在pom中包含javaEE,它可能会起作用。更多信息,请看这里,因为user@SMA意味着您实际上没有测试任何东西,因为您只在模拟代码上操作。您要做的是创建
    BookAddController
    的实例,并可能在调用
    doPost
    时传入
    HttpServletRequest
    HttpServletResponse
    的模拟实例。否则,您不会执行您所编写的任何代码,因此您无法知道您所编写的实际代码是否有效,或者是否发生了任何回归


    至于例外情况,我怀疑可能是
    junit
    没有访问javaeeapi的权限。在pom中包含javaEE,它可能会起作用。这是一个令人兴奋的问题,因为Oracle没有为许多类提供方法体—您得到的只是API,这使得实际上无法使用JAR文件来运行任何东西。有关更多信息,请参见此:

    这个问题可能是测试类路径上有javax:javieapi(或javax:javaeewebapi)库的结果。如果您有一个Maven项目,则POM文件中可能存在以下依赖项:

    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
      <version>6.0</version>
    </dependency>
    
    
    爪哇
    JavaEEAPI
    6
    
    或:

    <dependency>  
      <groupId>javax</groupId>  
      <artifactId>javaee-web-api</artifactId>  
      <version>6.0</version>  
    </dependency>  
    
    
    爪哇
    javaeewebapi
    6
    
    此依赖关系为您提供了JavaEEAPI,而不是实现。虽然此依赖项可用于编译,但不能用于执行代码(包括测试)

    背景 当这些工件发布到Maven存储库时,Sun/Oracle从分类为“实现”的类中剥离了代码。因此,所有接口都是代码完整的,但任何抽象类或实现类中都没有代码。任何使用该类的尝试都可能失败

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

    编译时,它们也希望运行。顺便说一句,我们一直在推广全套Java EE API,这些API只能用于编译——它们剥离了方法细节。这样,用户就不能在运行时使用这些工件

    基本上,javax:javaeeAPI和javax:javaeeWebAPI Maven工件不是真正的东西——它们是“存根”。它们非常适合轻松地提供编译所需的依赖项,但当您需要做一些事情时,它们会完全中断

    解决方法:不要使用javax.javaee-api或javax:javaee web api工件!相反,使用您需要的“真实”工件。不幸的是,没有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>
    
    
    org.apache.geronimo.specs
    geronimo-servlet_3.0_规范
    1
    测试
    

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

    这是一个令人兴奋的问题,因为Oracle没有为许多类提供方法体—您得到的只是API,这使得无法实际使用JAR文件来运行任何东西。有关更多信息,请参见此:

    这个问题可能是测试类路径上有javax:javieapi(或javax:javaeewebapi)库的结果。如果您有一个Maven项目,则POM文件中可能存在以下依赖项:

    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
      <version>6.0</version>
    </dependency>
    
    
    爪哇
    JavaEEAPI
    6
    
    或:

    <dependency>  
      <groupId>javax</groupId>  
      <artifactId>javaee-web-api</artifactId>  
      <version>6.0</version>  
    </dependency>  
    
    
    爪哇
    javaeewebapi
    6
    
    此依赖关系为您提供了JavaEEAPI,而不是实现。虽然此依赖项可用于编译,但不能用于执行代码(包括测试)

    背景 当这些工件发布到Maven存储库时,Sun/Oracle从分类为“实现”的类中剥离了代码。因此,所有接口都是代码完整的,但任何抽象类或实现类中都没有代码。任何使用该类的尝试都可能失败

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

    编译时,它们也希望运行。顺便说一句,我们一直在推广全套Java EE API,这些API只能用于编译——它们剥离了方法细节。这样,用户就不能在运行时使用这些工件

    基本上,javax:javaeeAPI和javax:javaeeWebAPI Maven工件不是真正的东西——它们是“存根”。它们非常适合轻松地提供编译所需的依赖项,但当您需要做一些事情时,它们会完全中断

    解决方法:不要使用javax.javaee-api或javax:javaee web api工件!相反,使用您需要的“真实”工件。不幸的是,没有可以排除依赖项的Maven作用域