如何在 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 中对调用另一个模型的方法的模型方法进行单元测试

如何在 Typescript 中对私有方法进行单元测试

如何在Typescript中对私有方法进行单元测试

如何在 Swift 中对这个自定义 UITextField 进行单元测试?

如何在验证方法 nuxtjs vuejs jest 中对代码进行单元测试