Spock groovy - 如何在同一个 class 中模拟方法?
Spock groovy - how to mock methods in same class?
如何在测试class中mock私有方法和方法在不同的class?
class MyClass {
private final Retriever<ScoreData> retriever;
private DataStore<Model> dataStore;
private String gameName;
public void MyClass(Retriever<ScoreData> retriever, DataStore<Model> dataStore, String gameName) {
this.retriever = retriever;
this.dataStore = dataStore;
this.gameName = gameName;
}
public void process(GameHolder<G> games) {
// Business Logic
for (Game<G> game : games){
Integer score = game.getScore();
Integer playerId = game.getPlayerId();
Integer finalScore = getScore(game);
computeScore(score, finalScore);
}
}
private Integer computeScore(int score, int finalScore) {
// Runs some business logic and returns O3
return score + finalScore;
}
private Integer getScore(Game game) {
// Runs some business logic and returns O3
String dbName = game.getDbName();
DBRetriever ret = new DBRetriever(dbName)
if (dbName.equals("gameDB"){
return ret.getFinalScore(dbName);
}
return -1;
}
}
下面是我目前对 Spock 的实现,我不确定如何实现对象的模拟。
@Subject
def obj
def "this is my test"(){
given:
Object1 obj1 = Mock(Object1)
Object2 obj2 = Mock(Object2)
Object3 obj3 = Mock(Object3)
def myClassObject = new MyClass(obj1, obj2, obj3)
when:
myClassObject.process(new Object4())
then:
1 * getScore()
1 * computeScore()
}
如何模拟 computeScore 和 getScore 函数以及如何为对象 obj1、obj2、obj3 分配初始值?
注意:我只是想在这里测试 process()
方法。但是 process 方法是从内部调用私有方法。我希望能够 return 私有方法的模拟值而不是执行私有方法。
编辑:Retriever 和 DataStore 是接口,它们各自的实现是 ScoreData 和 Model。
Note: I am only trying to test process()
method here. But process method is calling a private method from inside. I want to be able to return a mock value for private method rather than executing the private method.
你不应该这样做,因为 MyClass
是你的 class 正在测试中。如果对它们进行存根,则无法使用测试覆盖私有方法内部的逻辑。相反,如果在这些私有方法中使用它们,您应该确保注入的模拟按照您希望的方式(通过存根方法)运行。不幸的是,您决定不显示代码的关键部分,即使确切的答案取决于它。相反,您将它们替换为注释 "some business logic",这不是很有帮助,因为您的业务逻辑正是您要测试的。你不想把它存根。
所以请不要做我在这里向你展示的事情,我只是因为你问了才回答。
为了存根方法,它不能是私有的,因为从技术上讲,间谍、模拟或存根总是子classes 或原件和子classes 不能继承甚至调用私有方法.因此,您需要使方法受保护(以便 subclasses 可以使用或覆盖它们)或包范围。我推荐前者。
但是你不能使用普通的模拟或存根作为你的 class 被测的替代品,因为你只想存根部分业务逻辑(你的两个方法有问题),而不是整个逻辑(你想保留 process()
)。因此,您需要一个部分模拟。为此,您可以使用间谍。
虚拟依赖 classes:
package de.scrum_master.Whosebug.q60103582;
public class Object1 {}
package de.scrum_master.Whosebug.q60103582;
public class Object2 {}
package de.scrum_master.Whosebug.q60103582;
public class Object3 {}
package de.scrum_master.Whosebug.q60103582;
public class Object4 {}
Class 测试中:
package de.scrum_master.Whosebug.q60103582;
public class MyClass {
private Object1 o1;
private Object2 o2;
private Object3 o3;
public MyClass(Object1 o1, Object2 o2, Object3 o3) {
this.o1 = o1;
this.o2 = o2;
this.o3 = o3;
}
public void process(Object4 o4) {
System.out.println("process - business Logic");
Object2 result = getScore("dummy ID");
Object3 obj = computeScore(result);
}
protected Object3 computeScore(Object2 result) {
System.out.println("computeScore - business logic");
return o3;
}
protected Object2 getScore(String id) {
System.out.println("getScore - business logic");
return o2;
}
}
斯波克测试:
package de.scrum_master.Whosebug.q60103582
import spock.lang.Specification
class MyClassTest extends Specification {
def "check main business logic"(){
given:
Object1 obj1 = Mock()
Object2 obj2 = Mock()
Object3 obj3 = Mock()
MyClass myClass = Spy(constructorArgs: [obj1, obj2, obj3])
when:
myClass.process(new Object4())
then:
1 * myClass.getScore(_) //>> obj2
1 * myClass.computeScore(_) //>> obj3
}
}
在这里你可以看到如何检查间谍的互动。但是注意computeScore(_)
和getScore(_)
还是会被执行,在控制台日志中可以看到:
process - business Logic
getScore - business logic
computeScore - business logic
如果取消最后两行代码末尾的注释
1 * myClass.getScore(_) >> obj2
1 * myClass.computeScore(_) >> obj3
您实际上将避免这两个(受保护的)方法被完全执行,并用存根结果替换它们。控制台日志将更改为:
process - business Logic
但我再说一遍:不要这样做。相反,请确保您注入的模拟显示正确的行为,以便您可以实际执行测试中 class 中的方法。这就是测试的意义,不是吗?
如何在测试class中mock私有方法和方法在不同的class?
class MyClass {
private final Retriever<ScoreData> retriever;
private DataStore<Model> dataStore;
private String gameName;
public void MyClass(Retriever<ScoreData> retriever, DataStore<Model> dataStore, String gameName) {
this.retriever = retriever;
this.dataStore = dataStore;
this.gameName = gameName;
}
public void process(GameHolder<G> games) {
// Business Logic
for (Game<G> game : games){
Integer score = game.getScore();
Integer playerId = game.getPlayerId();
Integer finalScore = getScore(game);
computeScore(score, finalScore);
}
}
private Integer computeScore(int score, int finalScore) {
// Runs some business logic and returns O3
return score + finalScore;
}
private Integer getScore(Game game) {
// Runs some business logic and returns O3
String dbName = game.getDbName();
DBRetriever ret = new DBRetriever(dbName)
if (dbName.equals("gameDB"){
return ret.getFinalScore(dbName);
}
return -1;
}
}
下面是我目前对 Spock 的实现,我不确定如何实现对象的模拟。
@Subject
def obj
def "this is my test"(){
given:
Object1 obj1 = Mock(Object1)
Object2 obj2 = Mock(Object2)
Object3 obj3 = Mock(Object3)
def myClassObject = new MyClass(obj1, obj2, obj3)
when:
myClassObject.process(new Object4())
then:
1 * getScore()
1 * computeScore()
}
如何模拟 computeScore 和 getScore 函数以及如何为对象 obj1、obj2、obj3 分配初始值?
注意:我只是想在这里测试 process()
方法。但是 process 方法是从内部调用私有方法。我希望能够 return 私有方法的模拟值而不是执行私有方法。
编辑:Retriever 和 DataStore 是接口,它们各自的实现是 ScoreData 和 Model。
Note: I am only trying to test
process()
method here. But process method is calling a private method from inside. I want to be able to return a mock value for private method rather than executing the private method.
你不应该这样做,因为 MyClass
是你的 class 正在测试中。如果对它们进行存根,则无法使用测试覆盖私有方法内部的逻辑。相反,如果在这些私有方法中使用它们,您应该确保注入的模拟按照您希望的方式(通过存根方法)运行。不幸的是,您决定不显示代码的关键部分,即使确切的答案取决于它。相反,您将它们替换为注释 "some business logic",这不是很有帮助,因为您的业务逻辑正是您要测试的。你不想把它存根。
所以请不要做我在这里向你展示的事情,我只是因为你问了才回答。
为了存根方法,它不能是私有的,因为从技术上讲,间谍、模拟或存根总是子classes 或原件和子classes 不能继承甚至调用私有方法.因此,您需要使方法受保护(以便 subclasses 可以使用或覆盖它们)或包范围。我推荐前者。
但是你不能使用普通的模拟或存根作为你的 class 被测的替代品,因为你只想存根部分业务逻辑(你的两个方法有问题),而不是整个逻辑(你想保留 process()
)。因此,您需要一个部分模拟。为此,您可以使用间谍。
虚拟依赖 classes:
package de.scrum_master.Whosebug.q60103582;
public class Object1 {}
package de.scrum_master.Whosebug.q60103582;
public class Object2 {}
package de.scrum_master.Whosebug.q60103582;
public class Object3 {}
package de.scrum_master.Whosebug.q60103582;
public class Object4 {}
Class 测试中:
package de.scrum_master.Whosebug.q60103582;
public class MyClass {
private Object1 o1;
private Object2 o2;
private Object3 o3;
public MyClass(Object1 o1, Object2 o2, Object3 o3) {
this.o1 = o1;
this.o2 = o2;
this.o3 = o3;
}
public void process(Object4 o4) {
System.out.println("process - business Logic");
Object2 result = getScore("dummy ID");
Object3 obj = computeScore(result);
}
protected Object3 computeScore(Object2 result) {
System.out.println("computeScore - business logic");
return o3;
}
protected Object2 getScore(String id) {
System.out.println("getScore - business logic");
return o2;
}
}
斯波克测试:
package de.scrum_master.Whosebug.q60103582
import spock.lang.Specification
class MyClassTest extends Specification {
def "check main business logic"(){
given:
Object1 obj1 = Mock()
Object2 obj2 = Mock()
Object3 obj3 = Mock()
MyClass myClass = Spy(constructorArgs: [obj1, obj2, obj3])
when:
myClass.process(new Object4())
then:
1 * myClass.getScore(_) //>> obj2
1 * myClass.computeScore(_) //>> obj3
}
}
在这里你可以看到如何检查间谍的互动。但是注意computeScore(_)
和getScore(_)
还是会被执行,在控制台日志中可以看到:
process - business Logic
getScore - business logic
computeScore - business logic
如果取消最后两行代码末尾的注释
1 * myClass.getScore(_) >> obj2
1 * myClass.computeScore(_) >> obj3
您实际上将避免这两个(受保护的)方法被完全执行,并用存根结果替换它们。控制台日志将更改为:
process - business Logic
但我再说一遍:不要这样做。相反,请确保您注入的模拟显示正确的行为,以便您可以实际执行测试中 class 中的方法。这就是测试的意义,不是吗?