我正在尝试通过访问以下网址的在线教程后,对访问数据源的Servlet创建并运行单元测试。
http://fandry.blogspot.com/2011/03/junit-based-integration-testing-with.html

注意:我过去有类似的帖子,从中得到了使用jndi-simple api的想法,这是答案之一中建议的。

但是我在测试执行期间遇到以下异常。

例外

Running com.study.mockito.controllers.ProductControllerTest
Failed to bind JNDI context....
java.lang.NullPointerException
    at java.util.Hashtable.put(Hashtable.java:514)
    at org.osjava.sj.jndi.StaticHashtable.put(StaticHashtable.java:84)
    at org.osjava.sj.jndi.AbstractContext.bind(AbstractContext.java:337)
    at org.osjava.sj.jndi.AbstractContext.rebind(AbstractContext.java:361)
    at org.osjava.sj.jndi.AbstractContext.rebind(AbstractContext.java:368)
    at javax.naming.InitialContext.rebind(InitialContext.java:427)
    at org.osjava.sj.jndi.DelegatingContext.rebind(DelegatingContext.java:76)
    at javax.naming.InitialContext.rebind(InitialContext.java:427)
    at com.study.mockito.controllers.ProductControllerTest.setUpClass(ProductControllerTest.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)


我已经使用META-INF / context.xml在Tomcat容器中配置了数据源,并且它工作正常,没有任何问题。我能够在我的JSP Web应用程序中检索和显示数据,而没有任何问题。

context.xml

<Context antiJARLocking="true" path="/mockito-web">
    <!-- PostgreSQL Datasource -->
    <Resource auth="Container" driverClassName="org.postgresql.Driver" factory="org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory" maxActive="50" maxIdle="10" maxWait="-1" name="jdbc/istore-db" password="postgres" type="javax.sql.DataSource" url="jdbc:postgresql://mydb-server:5432/istore-db" username="postgres"/>
</Context>


pom.xml

<dependency>
    <groupId>simple-jndi</groupId>
    <artifactId>simple-jndi</artifactId>
    <version>0.11.4.1</version>
    <scope>test</scope>
</dependency>


src / test / resources / jndi.properties

java.naming.factory.initial=org.osjava.sj.SimpleContextFactory
org.osjava.sj.root=target/test-classes
org.osjava.jndi.delimiter=/
org.osjava.sj.jndi.shared=true


src / test / resources / jdbc.properties

istore-db.type=javax.sql.DataSource
istore-db.driver=org.postgresql.Driver
istore-db.url=jdbc:postgresql://mydb-server:5432/istore-db
istore-db.user=postgres
istore-db.password=postgres


ProductContollerTest.java

public class ProductControllerTest {

    private HttpServletRequest request;
    private HttpServletResponse response;
    private ProductController controller;
    private RequestDispatcher rd;
    private ServletContext appContext;
    private ProductService productService;
    private Product product;

    @BeforeClass
    public static void setUpClass() {
        try {
            InitialContext ctxt = new InitialContext();
            DataSource dataSource = (DataSource) ctxt.lookup("jdbc/istore-db");
            // rebind for alias if needed
            ctxt.rebind("jdbc/istore-db", dataSource);
        } catch (Exception ex) {
            System.out.println("Failed to bind JNDI context....");
            ex.printStackTrace();
        }
    }

    @Before
    public void setUp() {
        controller = new ProductController();
        request = mock(HttpServletRequest.class);
        response = mock(HttpServletResponse.class);
        rd = mock(RequestDispatcher.class);
        appContext = mock(ServletContext.class);
        productService = mock(ProductService.class);
        product = mock(Product.class);
    }

    @Test
    public void testProcessRequest() throws ServletException, IOException {
        when(productService.getProduct(anyInt())).thenReturn(product);

        when(request.getServletContext()).thenReturn(appContext);
        when(appContext.getRequestDispatcher(anyString())).thenReturn(rd);

        //make an actual call
        controller.doGet(request, response);

        //verify that the method is getting called
        verify(rd).forward(request, response);
    }
}


ProductController.java

@WebServlet(name = "ProductController", urlPatterns = {"/product.htm"})
public class ProductController extends HttpServlet {

    private static final int PRODUCT_ID = 301;

    @Resource(name = "jdbc/istore-db")
    protected DataSource dataSource;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        processRequest(request, response);
    }


    public void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("Into the ProductController...");

        //fetch the product from the db based on productId parameter
        Product product = new ProductService(dataSource).getProduct(PRODUCT_ID);
        request.setAttribute("product", product);

        RequestDispatcher rd = request.getServletContext().getRequestDispatcher("/jsp/product.jsp");
        rd.forward(request, response);
    }

}

最佳答案

我们遇到此问题的原因有两个:JNDI为多个数据源定义了相同的名称;密码包含破坏JNDI的'#'字母

09-11 18:34
查看更多