如何在 java 中对这个方法进行单元测试?
Posted
技术标签:
【中文标题】如何在 java 中对这个方法进行单元测试?【英文标题】:How can I unit test this method in java? 【发布时间】:2012-05-30 08:34:10 【问题描述】:我正在使用 Struts2 框架并希望对下面的 execute
方法进行单元测试:
public String execute()
setDao((MyDAO) ApplicationInitializer.getApplicationContext().getBean("MyDAO"));
setUserPrincipal(); //fetches attribute from request and stores it in a var
setGroupValue(); //
setResults(getMyDao().getReportResults(getActionValue(), getTabName());
setFirstResultSet((List) getResults()[0]);
setSecondResultSet((List) getResults()[1]);
return SUCCESS;
如您所见,大部分逻辑与数据库相关。那么我将如何对这个功能进行单元测试呢?我想通过模拟其中包含少量请求变量的HTTPServletRequest
来进行单元测试。
我的问题是:
如何伪造/模拟请求变量,就好像它来自浏览器一样 我的单元测试是否应该调用实际的 DAO 并确保数据返回? 如果是这样,我如何从单元测试中调用 DAO,因为 DAO 与服务器绑定,因为 jndi 池设置驻留在应用程序服务器上。如果有任何书籍/文章说明如何真正做到这一点,我将不胜感激。
【问题讨论】:
【参考方案1】:您向我们展示的代码不足以完全回答您的问题。
一行一行
setDao((MyDAO) ApplicationInitializer.getApplicationContext().getBean("MyDAO"));
这是最难的,因为它使用静态方法。我们需要看看ApplicationInitializer
是如何工作的。在理想世界中,getApplicationContext()
方法应该返回 ApplicationContext
的模拟。当getBean("MyDAO")
时,这个模拟应该依次返回MyDAO
。 mockito 完全有能力处理这个问题,以及所有其他模拟框架。
setUserPrincipal(); //fetches attribute from request and stores it in a var
请求来自哪里?它是否注入到动作类中?如果是这样,只需注入模拟的请求对象,例如MockHttpServletRequest
.
setGroupValue(); //
和上面一样吗?请提供更多细节,这个方法实际上是做什么的?
setResults(getMyDao().getReportResults(getActionValue(), getTabName());
当使用给定参数调用 getReportResults()
时,您之前创建的模拟应该返回一些内容。
setFirstResultSet((List) getResults()[0]);
setSecondResultSet((List) getResults()[1]);
我猜下面的方法在动作类上设置了一些字段。因为您可以完全控制从模拟 getReportResults()
返回的内容,所以这不是问题。
return SUCCESS;
您可以断言SUCCESS
是否是执行结果。
现在一般
如何伪造/模拟请求变量,就好像它来自浏览器一样
见上文,Spring 中内置了一个 mock。
我的单元测试是否应该调用实际的 DAO 并确保数据返回?
如果您的单元测试调用真正的 DAO,则它不再是单元测试。这是一个集成测试。
如果是这样,我如何从单元测试中调用 DAO,因为 DAO 与服务器绑定,因为 jndi 池设置驻留在应用程序服务器上。
这意味着您正在进行集成测试。在这种情况下,您应该使用像h2 这样的内存数据库,这样您仍然可以在ci 服务器上运行测试。您必须以某种方式配置您的应用程序以从不同的位置获取 DataSource
。
最后说明
本质上,您应该将所有内容的模拟注入您的 Struts 动作类。您可以告诉模拟在调用时返回任何值。然后,在调用execute()
之后,您可以验证给定的方法被调用、字段设置和结果值是否正确。考虑将其拆分为多个测试。
代码审查
Struts 2 与 Spring 完美集成。如果您利用该功能,Spring 容器会自动将MyDAO
注入您的操作类。第一行已过时。
【讨论】:
“在这种情况下,您应该使用像 h2 这样的内存数据库”,但我的应用程序后端数据库是 Oracle。我负担不起将所有数据带到内存中的 h2 数据库并编写在 oracle db 中编写的存储过程。 spring 有没有办法创建一个 jndi/datasource 并将其注入我的setDAO()
方法?
@Mike:再说一次,如果你在做单元测试,这不应该打扰你,因为整个MyDAO
接口都是模拟的。如果您正在进行集成测试并且不能简单地切换到不同的数据库引擎 - 您需要一个可用的 Oracle 测试数据库。这是有问题的,因为它必须可供所有愿意运行集成测试的人使用。此外,不要在代码中硬编码 JNDI。 Spring 能够从 JNDI 获取 bean,出于测试目的,只需将单个 bean 替换为本地数据源。请打开另一个问题,说明您如何获得 DS 并发布跟进。 (没有空格)【参考方案2】:
这段代码很难进行单元测试,因为你没有按预期使用 Spring(即作为依赖注入框架),而是将其用作工厂。依赖注入正是用于避免您正在执行的这种难以测试的 bean 查找。 DAO 应该被注入到您的对象中。这样,您可以在对对象进行单元测试时注入一个模拟 DAO。
此外,此逻辑根本与数据库无关。 DAO 包含与数据库相关的逻辑。此操作使用 DAO,因此 DAO 是另一个单元(应在其自己的单元测试中进行测试)。因此,您应该注入一个模拟 DAO 来对该方法进行单元测试。
最后,这个方法不使用 HttpServletRequest(至少不直接使用),所以我不明白你为什么需要使用假请求。您可以模拟使用请求的 setXxx 方法。
【讨论】:
【参考方案3】:与其简单地模拟HTTPServletRequest
,不如
模拟对应用程序的实际自动定向请求
本身?查看Selenium,它可以让您做到这一点。
对于测试 DAO(集成测试),您可以使用 HSQLDB 在内存中创建数据库,这将允许您从测试中创建/删除对象,并确保它们被正确地持久化/检索。 HSQLDB 的优势在于您的测试将比使用实际数据库运行得快得多。当需要提交代码时,您可以针对您的实际数据库运行测试。通常,您会在 IDE 中设置不同的运行配置来促进这一点。
让注入的 daos 可用于测试的最简单方法是让单元测试类扩展 AbstractJUnit4SpringContextTests
。然后,您可以使用 @ContextConfiguration
注释指向多个 xml 应用程序上下文文件,或者如果您使用基于注释的配置,请将其指向其中包含 <context:annotation-config />
声明的上下文文件。
【讨论】:
以上是关于如何在 java 中对这个方法进行单元测试?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 java 中对 jdbc 代码进行单元测试? [关闭]
如何在 Laravel 中对调用另一个模型的方法的模型方法进行单元测试