Spock对存根和模拟做了很强的区分。当要更改的内容从被测试类使用的类返回时,请使用存根,这样您就可以测试if语句的另一个分支。使用mock,当您不关心测试中的类返回什么时,只需调用另一个类的另一个方法,并且您希望确保调用了该方法。很整洁。然而,假设您有一个具有流利API的构建器,它使人们。您希望测试调用此生成器的方法。
Person myMethod(int age) {
...
// do stuff
...
Person tony =
builder.withAge(age).withHair("brown").withName("tony").build();
return tony;
}
所以最初,我想只是模拟构建器,然后myMethod()的单元测试应该检查具有正确参数的withAge()、withHair()。
都很酷。
但是mock方法返回NULL。这意味着您不能使用fluent API。
你可以做的。
Person myMethod(int age) {
...
// do stuff
...
builder.withAge(age);
builder.withHair("brown");
builder.withName("tony");
builder.build();
return tony;
}
您需要确保生成器模拟的stubbedwith*
方法返回模拟本身,并且build()
方法返回所需的任何对象(真实的或也是模拟的)。
这样怎么样?第一个特征方法只是为了说明,你对第二个和第三个感兴趣。请注意,对于返回模拟对象的with*
方法,您不能内联定义存根(即mock(){myMethod(_)>>myResult}
),而对于build()
,您可以定义存根,因为它没有引用模拟对象本身。
package de.scrum_master.stackoverflow.q57298557
import spock.lang.Specification
class PersonBuilderTest extends Specification {
def "create person with real builder"() {
given:
def personBuilder = new PersonBuilder()
when:
def person = personBuilder
.withHair("blonde")
.withAge(22)
.withName("Alice")
.build()
then:
person.age == 22
person.hair == "blonde"
person.name == "Alice"
}
def "create person with mock builder, no interactions"() {
given:
def personBuilder = Mock(PersonBuilder)
personBuilder./with.*/(_) >> personBuilder
personBuilder.build() >> new Person(name: "John Doe", age: 99, hair: "black")
when:
def person = personBuilder
.withHair("blonde")
.withAge(22)
.withName("Alice")
.build()
then:
person.age == 99
person.hair == "black"
person.name == "John Doe"
}
def "create person with mock builder, use interactions"() {
given:
def personBuilder = Mock(PersonBuilder)
when:
def person = personBuilder
.withHair("blonde")
.withAge(22)
.withName("Alice")
.build()
then:
3 * personBuilder./with.*/(_) >> personBuilder
1 * personBuilder.build() >> new Person(name: "John Doe", age: 99, hair: "black")
person.age == 99
person.hair == "black"
person.name == "John Doe"
}
}
测试中的类(快速和肮脏的实现,仅供说明):
package de.scrum_master.stackoverflow.q57298557
import groovy.transform.ToString
@ToString(includePackage = false)
class Person {
String name
int age
String hair
}
package de.scrum_master.stackoverflow.q57298557
class PersonBuilder {
Person person = new Person()
PersonBuilder withAge(int age) {
person.age = age
this
}
PersonBuilder withName(String name) {
person.name = name
this
}
PersonBuilder withHair(String hair) {
person.hair = hair
this
}
Person build() {
person
}
}
更新:如果您想要构建器类的通用解决方案,您可以使用Spock手册中描述的点菜模拟。需要注意的一点是:在创建模拟时,手册指定了一个自定义的idefaultresponse
类型参数,但您需要指定该类型的实例。
这里有自定义的idefaultresponse
,它使模拟调用的默认响应不是null、0或空对象,而是模拟实例本身。这对于模拟具有流畅接口的构建器类非常理想。您只需要确保存根build()
方法,以实际返回要构建的对象,而不是模拟。例如,PersonBuilder.build()
不应返回默认的PersonBuilder
模拟,而应返回Person
。
package de.scrum_master.stackoverflow.q57298557
import org.spockframework.mock.IDefaultResponse
import org.spockframework.mock.IMockInvocation
class ThisResponse implements IDefaultResponse {
public static final ThisResponse INSTANCE = new ThisResponse()
private ThisResponse() {}
@Override
Object respond(IMockInvocation invocation) {
invocation.mockObject.instance
}
}
def "create person with a la carte mock builder, no interactions"() {
given:
PersonBuilder personBuilder = Mock(defaultResponse: ThisResponse.INSTANCE) {
build() >> new Person(name: "John Doe", age: 99, hair: "black")
}
when:
def person = personBuilder
.withHair("blonde")
.withAge(22)
.withName("Alice")
.build()
then:
person.age == 99
person.hair == "black"
person.name == "John Doe"
}
def "create person with a la carte mock builder, use interactions"() {
given:
PersonBuilder personBuilder = Mock(defaultResponse: ThisResponse.INSTANCE) {
3 * /with.*/(_)
1 * build() >> new Person(name: "John Doe", age: 99, hair: "black")
}
when:
def person = personBuilder
.withHair("blonde")
.withAge(22)
.withName("Alice")
.build()
then:
person.age == 99
person.hair == "black"
person.name == "John Doe"
}
更新2:这个示例测试没有太大意义,因为它只是测试模拟,而不是任何应用程序代码。但是如果您将类似这样的模拟作为依赖项注入到对象中,我的方法就会派上用场。而且我越想越喜欢定制idefaultresponse
的点菜模拟,因为它可以通用于流畅的API类。
我有一个示例方法(我需要编写测试用例)如下所示, 我想模拟getConfig方法并返回一个特定的字符串值。getConfig是Kotlin对象中方法,如下所示, 下面是我尝试的测试 我没有得到任何错误,但是getConfig方法没有被嘲笑。执行实际的实现。我也试过使用Powermockito。请帮帮我
问题内容: 我正在尝试为一些依赖WifiManager和返回的ScanResults的类实现一些单元测试。我想做的是能够控制我收到的ScanResults,以测试各种不同的条件。 不幸的是,对我来说,成功模拟WifiManager非常困难(尽管我想我可以在MockWifiManager中传递其构造函数null引用)。这只是我的第一个问题,因为一旦我有一个MockWifiManager可以玩(如果它
在如何模拟Grails单元测试中使用的自动有线依赖方面,我可以提供一些建议。我省略了大部分不必要的代码,只给出了测试类和被测试文件类中的相关方法 如果不对此依赖性进行攻击或嘲弄,我就会得到错误 我尝试存根密码编码器并让它返回true 但这会给出一条错误消息: 有什么方法可以用Spock来嘲笑这种依赖吗?
我在尝试包装我的代码以用于单元测试时遇到了一些问题。问题是。我有接口IHttpHandler: 现在很明显,我将在Connection类中有一些方法,这些方法将从my后端检索数据(JSON)。但是,我想为这个类编写单元测试,显然我不想编写针对真实后端的测试,而是一个被嘲弄的测试。我曾尝试谷歌一个很好的答案,但没有很大的成功。我以前可以并且曾经使用过Moq来模拟,但是从来没有在像HttpClient
遇到了另一个常见的问题,同时为Spring Batch编写单元测试和集成测试组件是如何模拟域对象。一个很好的例子是StepExecutionListener,如下所示: public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport { public ExitStatus afterSte
我正在寻找一种方法来模拟Controller中使用的服务bean,这样我就可以使用MockMVC只测试Controller。但是我找不到一个简单的方法来用Spock Mock代替real bean。一切都使用spring-boot 1.3.2版本。更多细节如下: 我有一个以下控制器类 和集成Spock测试: 我需要一种方法来替换这个autowired bean,用一个mock/stub这样我就可以