我正在尝试使用spock框架对某些java类进行单元测试。结构如下所示:
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
我正在尝试为DatabaseQuery
类编写Spock测试,该测试依赖于DatabaseQuery
。我正在考虑模仿RequestProcessor
类,以便单独测试RequestProcessor
类。
正在调用processRequest()
的{{1}}方法,该方法依赖于另一种私有方法。该方法将使用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语句捕获。
我在这里做错了什么?
答案 0 :(得分:2)
首先,您的示例代码不起作用,即使我简化它并创建我自己的虚拟DatabaseQuery
类,因为此处至少有三个错误:
this.request = request
(自我分配),但应该是this.request = aRequest;
。requestHandler.processRequest()
,但应该是requestProcessor.processRequest()
。executeRequest()
未按规定返回String
。所以我只能推测它实际上会在DatabaseQuery
上调用另一种方法,以便将查询结果转换为String
。完全避免这种情况后,让我们看看你的测试真正的根本错误。
我在这里做错了什么?
假设局部变量中的mock在应用程序代码中的其他局部变量中以某种方式有效。为了使用mock,您需要将它注入到被测试的类中。但是和许多开发人员一样,你没有通过依赖注入来设计解耦和可测试性,但是你在内部创建了你的依赖 - 在这种情况下是DatabaseQuery
对象。
我认为您的测试只会受到过度规范的影响。为什么要检查另一个类中的特定方法是否从私有方法调用(间接地,在那个)?您不直接测试私有方法,而是通过调用公共方法来覆盖它们的代码,并且您的测试已经这样做了。
如果您想覆盖catch
块,只需确保您的请求导致正确的例外情况。也许你需要为此模拟数据库连接并确保它将预期结果返回给DatabaseQuery
。我没有看到足够的代码才能准确说出来。
现在让我们假设您绝对想要检查这种互动,无论我之前说过什么。您需要做什么取决于具体情况(您未在代码中显示):
在任何情况下,您都需要DatabaseQuery
注射。您只需在您的班级中添加成员和其他设置者即可。
现在你的路上有一个分叉,取决于交互dbquery.executeQuery(_ as String)
来自哪里(被叫):
DatabaseQuery
调用该方法,则可以注入正常的Mock
。DatabaseQuery
内部调用该方法,则需要注入Spy
,因为模拟不会像原始对象一样调用其他内部方法,因为 - 好吧,它只是一个模拟。 / LI>
醇>
案例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)
从其自己的班级内部调用
您是否在RequestProcessor
和RequestProcessorTest
中看到了两条已注释掉的行?只需使用它们并注释掉其他两个,而不是这样:
private String executeRequest() {
//return databaseQuery.executeQuery(request);
return databaseQuery.getResult();
}
and: "mock the DBQuery class"
//dbquery = Mock(DatabaseQuery)
dbquery = Spy(DatabaseQuery, constructorArgs: [queryRequest])
测试仍然有效,包括互动检查。
当然,我必须假装一些东西并填写你没有提供的缺失拼图瓷砖,但这基本上就是它的工作方式。