问题陈述
我希望能够对连接到数据库的方法运行 junit 测试。
当前设置
Eclipse Java EE IDE – Java 代码不使用任何框架。开发人员(包括我在内)希望在尝试将代码移至 Spring 框架之前对当前遗留代码进行更健壮的测试,以便我们可以一路证明行为仍然正确。
JBoss 4.2 – 供应商软件的版本限制 (Adobe LiveCycle ES2);我们的 Java Web 应用程序使用 JBoss 的这种设置来运行并使用 Adobe LiveCycle API。
我们无法在 Eclipse 中成功运行供应商配置的 JBoss – 我们花了数周时间尝试此操作,包括联系为我们提供 JBoss for Adobe LiveCycle 配置支持的公司。据推测,问题是 Eclipse 中设置的内存限制问题,但更改内存设置到目前为止在 Eclipse 中成功启动 JBoss 服务器失败。目前,让 JBoss 在 Eclipse 中运行的尝试被搁置了。
数据库连接在 JBoss 启动时加载的 JNDI 数据源中定义。我们的 Web 应用程序和 Adobe LiveCycle 都需要创建与该数据源的连接。
代码
我在这个代码片段中掩盖了错误检查和类结构,以专注于问题的核心。希望这不会给其他人带来麻烦。方括号中的文字不是实际文字。
我们创建连接的代码是这样的:
Properties props = new Properties();
FileInputStream in = null;
in = new FileInputStream(System.getProperty("[Properties File Alias]"));
props.load(in);
String dsName = props.getProperty(“[JNDI data source name here]”);
InitialContext jndiCntx = new InitialContext();
DataSource ds = (DataSource) jndiCntx.lookup(dsName);
Ds.getConnection();
我希望能够在不对其进行任何更改的情况下测试依赖于此代码的方法。
在 properties-service.xml 文件中引用属性文件别名:
<!-- ==================================================================== -->
<!-- System Properties Service -->
<!-- ==================================================================== -->
<!-- Allows rich access to system properties.-->
<mbean code="org.jboss.varia.property.SystemPropertiesService"
name="jboss:type=Service,name=SystemProperties">
<attribute name="Properties">
[Folder Alias]=[filepath1]
[Properties File Alias]=[filepath2]
</attribute>
</mbean>
来自位于 filepath2 的属性文件的片段
[JNDI data source name]=java:/[JNDI data source name]
这个数据源的 JNDI xml 文件是这样设置的:
<datasources>
<local-tx-datasource>
<jndi-name>[JNDI data source name here]</jndi-name>
<connection-url>jdbc:teradata://[url]/database=[database name]</connection-url>
<driver-class>com.teradata.jdbc.TeraDriver</driver-class>
<user-name>[user name]</user-name>
<password>[password]</password>
<!-- sql to call on an existing pooled connection when it is obtained from pool -->
<check-valid-connection-sql>SELECT 1+1</check-valid-connection-sql>
</local-tx-datasource>
</datasources>
我对解决方案的看法
我可以在 @BeforeClass 方法中做些什么来使上述代码正在寻找的属性在没有 JBoss 的情况下可用吗?也许以某种方式使用 java.util.Properties 类的 setProperty 方法?如果可能的话,我还想使用 JBoss 从中读取的相同 JNDI xml 文件,以减少重复的配置设置。
到目前为止,我所有的研究都以“使用 Spring”的建议结束,但我认为我们还没有准备好打开蠕虫 jar 头。我不是 JBoss 方面的专家,但如果需要我们的 JBoss 设置的更多详细信息以获得有用的答案,我会尽力获取它们,尽管我可能需要一些关于查找位置的指示。
Stackoverflow 研究引用资料:
Jndi lookup in junit using spring
Out of container JNDI data source
其他研究引用:
http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Properties.html
http://docs.oracle.com/javase/jndi/tutorial/basics/prepare/initial.html
最佳答案
您的问题有一个非常简单的答案,但您不会喜欢它:不要。
根据定义,单元测试应该验证单个单元的功能(单元的大小可能会有所不同,但应该是自给自足的)。创建一个测试依赖于 web 服务、数据库等的设置会适得其反:它会减慢你的测试速度,它包括无数可能出错的事情(网络连接失败、数据集更改、...... ) 在测试期间,这与您正在处理的实际代码无关,最重要的是:它使测试变得更加困难和复杂。
相反,您应该寻找将遗留代码与任何数据源分离的方法,以便您可以轻松地替换 mock objects或类似test doubles在你测试的时候。
您应该创建测试来验证整个堆栈的完整性,但这些被称为集成测试,它们在更高的抽象级别上运行。我个人喜欢推迟编写这些内容,直到单元本身就位、经过测试和工作 - 至少直到您达到不再期望每天更改服务调用和协议(protocol)的地步。
在您的情况下,最明显的策略是将对 Web 服务的所有调用封装在一个或多个单独的类中,提取业务对象可以依赖的接口(interface),并使用模拟实现相同的接口(interface)进行单元测试。
例如,如果您有一个调用地址数据库的业务对象,您应该将 JNDI 查找代码复制到一个名为 AddressServiceImpl
的新服务类中。它的公共(public)方法应该模仿 JNDI 数据源的所有方法签名。然后,将这些提取到 AddressService
接口(interface)。
然后您可以编写一个简单的集成测试来验证新类是否有效:调用所有方法一次并查看是否获得正确的结果。这样做的美妙之处在于,您可以提供指向测试数据库(而不是原始数据库)的 JNDI 配置,您可以使用测试数据集填充该数据库以确保始终获得预期的结果。为此,您不一定需要 JBoss 实例(尽管我从未遇到过 eclipse 集成的任何问题)——任何其他 JNDI 提供程序都应该可以工作,只要数据源本身的行为方式相同。需要明确的是:你测试一次,然后忘记它。至少在实际服务方法发生变化之前。
一旦您验证该服务可以正常运行,下一个任务就是遍历所有依赖类,并将对数据源的直接调用替换为对 AddressService 接口(interface)的调用。从那时起,您就有了一个正确的设置来对实际的业务方法实现单元测试,而不必担心应该在别处测试的事情;)
编辑
我支持 Mockito 的建议.真的很好!
关于java - 单元测试困境 : Using a JNDI data source without running JBoss or Spring,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13052944/