gpt4 book ai didi

java - spock 测试中的模拟 java 类没有被执行

转载 作者:行者123 更新时间:2023-12-02 02:15:59 25 4
gpt4 key购买 nike

我正在尝试使用 spock 框架对一些 java 类进行单元测试。结构如下所示:

  • com.myorg.requests(类名称:RequestProcessor)
  • com.myorg.query(类名称:DatabaseQuery)

第一类看起来像这样:

public class RequestProcessor {

private String request;

public RequestProcessor(String aRequest) {
this.request = request;
}

public String processRequest() {
String response ;
//do something here

try {
if(condition meets) {
response = executeRequest();
}
} catch ( various exceptions... ) {
System.out.println("something went wrong...");
}
}

private String executeRequest() throws <<exceptions thrown by DatabaseQuery>> {
//do something here
DatabaseQuery queryResult = new DatabaseQuery(request)
}
}

我正在尝试为这个 RequestProcessor 类编写一个 Spock 测试,该类依赖于 DatabaseQuery。我正在考虑模拟 DatabaseQuery 类,以便简单地单独测试 RequestProcessor 类。

正在调用 RequestProcessorprocessRequest() 方法,该方法依赖于另一个私有(private)方法。该方法将使用DatabaseQuery 来获取实际的查询结果。这就是我的 Spock 测试的样子:

class RequestProcessorSpec extends Specification {

//Class to be tested
RequestProcessor requestProcessor

//Dependencies
DatabaseQuery dbquery

def "Given a valid request, dbquery's executeQuery method is called" () {
given: "a valid request"
def queryRequest = '{"info1":"value1","info2":"value2","query":"select * from users"}'

and: "mock the DBQuery class"
dbquery = Mock(DatabaseQuery)

and: "create a new request"
requestProcessor = new RequestProcessor(queryRequest)

when: "the request is processed"
requestHandler.processRequest()

then: "dbquery executeQuery method is called"
1 * dbquery.executeQuery(_ as String)
}
}

这对我来说并不完全有效。我收到错误:

  • 调用次数太少
  • 不匹配的调用

当我使用 gradlew test --info 运行测试以获得更多结果时,我看到控制台上打印了一条日志,该日志是由 processRequest 中的 try-catch 语句捕获的方法。

我在这里做错了什么?

最佳答案

示例代码中的问题

首先,即使我简化它并创建我自己的虚拟 DatabaseQuery 类,您的示例代码也不起作用,因为这里至少有三个错误:

  • 在构造函数中,您有 this.request = request (自赋值),但它应该是 this.request = aRequest;
  • 在测试中,您有 requestHandler.processRequest(),但应该是 requestProcessor.processRequest()
  • 方法 executeRequest() 不会返回指定的 String。所以我只能推测,实际上它会调用DatabaseQuery上的另一个方法,以便将查询结果转换为String

为什么模拟不起作用?

解决了这个问题后,让我们看看您的测试到底存在什么根本性错误。

What am I doing wrong here?

假设局部变量中的模拟在应用程序代码中的其他局部变量中以某种方式有效。为了使用模拟,您需要将其注入(inject)到被测试的类中。但像许多开发人员一样,您没有通过依赖项注入(inject)来设计解耦和可测试性,而是在内部创建依赖项 - 在本例中是 DatabaseQuery 对象。

测试还有什么问题吗?

我认为您的测试只是过度规范。为什么要检查另一个类中的特定方法是否是从私有(private)方法调用的(间接地)?您不直接测试私有(private)方法,而是通过调用公共(public)方法来覆盖其代码,并且您的测试已经做到了这一点。

如果您想覆盖您的 catch block ,只需确保您的请求导致正确的异常即可。也许您需要为此模拟数据库连接,并确保它将预期结果返回到DatabaseQuery。我没有看到足够的代码来准确地说。

原问题的技术解决方案

现在让我们假设您绝对想要检查此交互,无论我之前说过什么。您需要做什么取决于情况(您没有在代码中显示):

无论如何,您都需要使DatabaseQuery可注入(inject)。您只需向您的类(class)添加一个成员和另一个 setter 即可。

现在您遇到了一个岔路口,具体取决于交互 dbquery.executeQuery(_ as String) 的进行(调用)位置:

  1. 如果从外部 DatabaseQuery 调用该方法,您可以注入(inject)普通的 Mock
  2. 如果从 DatabaseQuery 内部调用该方法,则需要注入(inject) Spy,因为模拟不会调用原始对象等其他内部方法,因为 - 嗯,它只是一个模拟。

情况 1:从外部调用 executeQuery(String)

package de.scrum_master.query;

public class DatabaseQuery {
private String request;

public DatabaseQuery(String request) {
this.request = request;
}

public String executeQuery(String request) {
return request.toUpperCase();
}

public String getResult() {
return executeQuery(request);
}
}
package de.scrum_master.requests;

import de.scrum_master.query.DatabaseQuery;

public class RequestProcessor {
private String request;
private DatabaseQuery databaseQuery;

public RequestProcessor(String aRequest) {
this.request = aRequest;
databaseQuery = new DatabaseQuery(request);
}

public String processRequest() {
return executeRequest();
}

private String executeRequest() {
return databaseQuery.executeQuery(request);
//return databaseQuery.getResult();
}

public void setDatabaseQuery(DatabaseQuery databaseQuery) {
this.databaseQuery = databaseQuery;
}
}
package de.scrum_master.requests

import de.scrum_master.query.DatabaseQuery
import spock.lang.Specification

class RequestProcessorTest extends Specification {
//Class to be tested
RequestProcessor requestProcessor

//Dependencies
DatabaseQuery dbquery

def "Given a valid request, dbquery's executeQuery method is called" () {
given: "a valid request"
def queryRequest = '{"info1":"value1","info2":"value2","query":"select * from users"}'

and: "mock the DBQuery class"
dbquery = Mock(DatabaseQuery)
//dbquery = Spy(DatabaseQuery, constructorArgs: [queryRequest])

and: "create a new request"
requestProcessor = new RequestProcessor(queryRequest)
requestProcessor.databaseQuery = dbquery

when: "the request is processed"
requestProcessor.processRequest()

then: "dbquery executeQuery method is called"
1 * dbquery.executeQuery(_ as String)
}
}

现在测试工作正常,包括交互检查。

情况 2:executeQuery(String) 从其自己的类内部调用

您看到 RequestProcessorRequestProcessorTest 中的两行注释了吗?只需使用它们并注释掉其他两个,如下所示:

  private String executeRequest() {
//return databaseQuery.executeQuery(request);
return databaseQuery.getResult();
}
    and: "mock the DBQuery class"
//dbquery = Mock(DatabaseQuery)
dbquery = Spy(DatabaseQuery, constructorArgs: [queryRequest])

测试仍然有效,包括交互检查。

当然,我必须伪造一些东西并填写您未提供的缺失拼图 block ,但这基本上就是它的工作原理。

关于java - spock 测试中的模拟 java 类没有被执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49302445/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com