NetBeans 8.2 RC运行Web项目JUnit测试报java.naming.factory.initial错误
报错根因
你遇到的javax.naming.NoInitialContextException报错核心原因是:JUnit测试是脱离Tomcat容器独立运行的。你项目中的DBHelpers.makeConnection()方法是通过JNDI查找Tomcat容器中配置的数据源来获取数据库连接,本地运行单测时没有容器提供的JNDI上下文环境,因此无法初始化上下文、找不到绑定的数据源。
Web项目在Tomcat中运行正常是因为Tomcat启动时已经自动完成了JNDI上下文初始化,并且绑定了你在context.xml中配置的数据源,因此可以正常获取连接。
解决方案
下面提供三种常用解决方案,可根据你的测试需求选择:
方案1:测试类中手动模拟JNDI上下文(最常用,代码侵入性低)
在测试类中增加@Before注解的初始化方法,单测运行前手动创建JNDI上下文并绑定对应数据源,和Tomcat的运行环境对齐。
步骤1:导入测试依赖
需要在项目测试依赖中引入以下包(如果用Maven/Gradle可直接添加对应坐标):
- tomcat-naming(提供JNDI上下文实现)
- commons-dbcp(提供数据源实现)
- 对应数据库的JDBC驱动
步骤2:改造测试类
package web.models.tblSpecialty; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; import javax.naming.InitialContext; import org.apache.commons.dbcp.BasicDataSource; public class SpecialtyDAOTest { SpecialtyDAO dao = new SpecialtyDAO(); @Before public void initJNDI() throws Exception { // 设置JNDI初始化工厂类 System.setProperty("java.naming.factory.initial", "org.apache.naming.java.javaURLContextFactory"); InitialContext ic = new InitialContext(); // 按照Tomcat的JNDI路径层级创建上下文 ic.createSubcontext("java:"); ic.createSubcontext("java:/comp"); ic.createSubcontext("java:/comp/env"); ic.createSubcontext("java:/comp/env/jdbc"); // 构造数据源,参数和你Tomcat中配置的数据源保持一致 BasicDataSource ds = new BasicDataSource(); ds.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); // 替换为你的数据库驱动类 ds.setUrl("jdbc:sqlserver://localhost:1433;databaseName=你的数据库名"); // 替换为你的数据库连接地址 ds.setUsername("你的数据库账号"); ds.setPassword("你的数据库密码"); // 绑定数据源,路径和DBHelpers中lookup的路径完全一致 ic.bind("java:/comp/env/jdbc/你的数据源名称", ds); } @Test public void testAddSpecialty() throws Exception { String name = "dentistry"; assertTrue(dao.addSpecialty(name)); } }
方案2:改造DBHelpers适配测试场景(实现最简单,不需要额外依赖)
修改DBHelpers.makeConnection()方法,增加测试场景分支,单测时直接走JDBC直连,不需要依赖JNDI:
import java.sql.Connection; import java.sql.DriverManager; import javax.naming.Context; import javax.naming.InitialContext; import javax.sql.DataSource; public class DBHelpers { // 测试时改为true,部署到Tomcat时改为false即可 private static final boolean IS_TEST_ENV = true; public static Connection makeConnection() throws Exception { if (IS_TEST_ENV) { // 测试场景直接JDBC直连 Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); // 替换为你的驱动类 String url = "jdbc:sqlserver://localhost:1433;databaseName=你的库名"; String username = "账号"; String password = "密码"; return DriverManager.getConnection(url, username, password); } else { // 生产场景保持原有JNDI逻辑不变 Context context = new InitialContext(); DataSource ds = (DataSource) context.lookup("java:/comp/env/jdbc/你的数据源名"); return ds.getConnection(); } } }
方案3:使用嵌入式容器运行单测(适合复杂集成测试)
如果你的测试需要依赖完整的Tomcat容器能力,可以使用Arquillian、Embedded Tomcat等框架,将单测跑在嵌入式启动的Tomcat中,自动获得完整的容器环境,无需手动模拟JNDI。
该方案适合项目中集成测试场景较多的情况,缺点是配置复杂度相对更高。
内容的提问来源于stack exchange,提问作者Grace Nguyen
相关产品推荐
相关产品推荐

