我该如何在Java中进行单元测试这个方法?

3

我正在使用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?因为JNDI池设置驻留在应用程序服务器上,所以DAO与服务器绑定。

如果有任何可以真正展示如何完成此操作的书籍/文章,我将不胜感激。

3个回答

6

你展示给我们的代码不足以完全回答你的问题。

逐行解析

setDao((MyDAO) ApplicationInitializer.getApplicationContext().getBean("MyDAO"));

这是最难的一行,因为它使用了静态方法。我们需要看一下ApplicationInitializer的工作方式。在理想的情况下,getApplicationContext()方法应该返回ApplicationContext的模拟对象。这个模拟对象应该在调用getBean("MyDAO")时返回MyDAO完全有能力处理这个问题,以及所有其他的模拟框架。
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内置了一个模拟功能。

我的单元测试是否应该调用实际的DAO并确保数据返回?

如果您的单元测试调用真实的DAO,则不再是单元测试。这是一个集成测试。

如果是这样,我该如何从单元测试中调用DAO,因为DAO与服务器绑定,因为jndi池设置驻留在应用程序服务器上。

这意味着您正在进行集成测试。在这种情况下,您应该使用内存数据库,例如,以便仍然可以在服务器上运行测试。您必须以某种方式配置您的应用程序,以从不同的位置获取 DataSource


最终说明

实质上,您应该在Struts操作类中注入所有内容的模拟。您可以告诉模拟在调用时返回任何值。然后,在调用execute()之后,您可以验证特定方法是否被调用,字段设置是否正确,并且结果值是否正确。考虑将此拆分为几个测试。


代码审查

  • Struts 2与Spring完美集成。如果您利用该功能,Spring容器将自动将MyDAO注入到您的操作类中。第一行变得过时了。

在这种情况下,您应该使用像h2这样的内存数据库,但我的应用程序后端数据库是Oracle。我无法将所有数据带到内存中的h2数据库,并编写在oracle db中编写的存储过程。有没有一种方法可以使用Spring创建一个jndi /数据源并将其注入到我的setDAO()方法中? - Anthony
@Mike:如果你正在进行单元测试,这不应该困扰你,因为整个MyDAO接口都被模拟了。如果你正在进行集成测试,并且不能简单地切换到不同的数据库引擎-你需要一个Oracle测试数据库可用。这是有问题的,因为它必须对每个愿意运行集成测试的人都可用。此外,请不要在代码中硬编码JNDI。Spring能够从JNDI获取bean,在测试目的下,只需用本地数据源替换该单个bean即可。请开另一个问题,展示你如何获取DS并发布跟进。 - Tomasz Nurkiewicz

1

这段代码很难进行单元测试,因为你没有按照 Spring 的意图(即作为依赖注入框架)使用它,而是将其用作工厂。依赖注入正是用来避免你正在进行的这种 bean 查找,这很难进行测试。DAO 应该被注入到你的对象中。这样,在单元测试对象时,你可以注入一个模拟 DAO。

此外,这个逻辑与数据库无关。DAO 包含了与数据库相关的逻辑。这个操作“使用”DAO,因此DAO是另一个单元(应该在自己的单元测试中进行测试)。因此,你应该注入一个模拟 DAO 来对这个方法进行单元测试。

最后,这个方法并没有直接使用 HttpServletRequest(至少没有直接使用),所以我不明白为什么你需要使用一个假请求。你可以模拟使用请求的 setXxx 方法。


0
  1. 不要仅仅模拟 HTTPServletRequest,而是模拟一个实际的自动化目标请求到应用程序本身?看看 Selenium,它可以让你做到这一点。

  2. 对于测试 DAO(集成测试),您可以使用 HSQLDB 在内存中创建数据库,这将允许您从测试中创建/删除对象,并确保它们被正确地持久化/检索。 HSQLDB 的优点在于,您的测试将比使用实际数据库运行得更快。当提交代码的时候,您可以针对实际数据库运行测试。通常,您会在 IDE 中设置不同的运行配置来方便这个过程。

  3. 使注入的 DAO 对象可用于测试的最简单方法是让您的单元测试类扩展 AbstractJUnit4SpringContextTests。然后,您可以使用 @ContextConfiguration 注释指向多个 XML 应用程序上下文文件,或者如果您使用基于注释的配置,则将其指向具有 <context:annotation-config /> 声明的上下文文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接