Mockito:在整个控制流中注入模拟
我仍在学习mockito,现在我正在学习如何注入mock。Mockito:在整个控制流中注入模拟
我有一个测试对象与依赖于其他对象的特定方法。这些对象又取决于其他对象。我想嘲笑某些事情,并在执行过程中随处使用这些模拟 - 贯穿方法的控制流程。
例如,假设有像类:
public class GroceryStore {
public double inventoryValue = 0.0;
private shelf = new Shelf(5);
public void takeInventory() {
for(Item item : shelf) {
inventoryValue += item.price();
}
}
}
public class Shelf extends ArrayList<Item> {
private ProductManager manager = new ProductManager();
public Shelf(int aisleNumber){
super(manager.getShelfContents(aisleNumber);
}
}
public class ProductManager {
private Apple apple;
public void setApple(Apple newApple) {
apple = newApple;
}
public Collection<Item> getShelfContents(int aisleNumber) {
return Arrays.asList(apple, apple, apple, apple, apple);
}
}
我需要编写测试代码与沿着线的部分:
....
@Mock
private Apple apple;
...
when(apple.price()).thenReturn(10.0);
...
...
@InjectMocks
private GroceryStore store = new GroceryStore();
...
@Test
public void testTakeInventory() {
store.takeInventory();
assertEquals(50.0, store.inventoryValue);
}
每当apple.price()被调用时,我想我的嘲笑苹果是使用的。这可能吗?
编辑:
重要提示...
包含我想嘲笑确实有该对象的二传手对象的类。但是,我在测试的级别上并没有真正掌握该类。因此,遵循这个例子,尽管ProductManager有一个Apple的setter,但我没有办法从GroceryStore对象获取ProductManager。
问题是你通过调用new
而不是注入来创建你所依赖的对象。将ProductManager
注入Shelf
(例如在构造函数中),并将Shelf
注入GroceryStore
。然后在测试中使用mocks。如果你想使用@InjectMocks
,你必须通过setter方法注入。
通过构造它可能看起来像这样:
public class GroceryStore {
public double inventoryValue = 0.0;
private shelf;
public GroceryStore(Shelf shelf) {
this.shelf = shelf;
}
public void takeInventory() {
for(Item item : shelf) {
inventoryValue += item.price();
}
}
}
public class Shelf extends ArrayList<Item> {
private ProductManager manager;
public Shelf(int aisleNumber, ProductManager manager) {
super(manager.getShelfContents(aisleNumber);
this.manager = manager;
}
}
public class ProductManager {
private Apple apple;
public void setApple(Apple newApple) {
apple = newApple;
}
public Collection<Item> getShelfContents(int aisleNumber) {
return Arrays.asList(apple, apple, apple, apple, apple);
}
}
然后你就可以测试它嘲笑你依赖的对象:
@Mock
private Apple apple;
...
when(apple.price()).thenReturn(10.0);
@InjectMocks
private ProductManager manager = new ProductManager();
private Shelf shelf = new Shelf(5, manager);
private GroceryStore store = new GroceryStore(shelf);
//Then you can test your store.
我忘了我有这个问题打开! :)最基本的答案是“不,你不能在方法调用的控制流中注入模拟。”意思是,你不能创建一个模拟,并自动适用于任何地方。你必须手动“安装”它在你想要的地方,可以这么说。要求“在任何你看到苹果的地方,用我的模拟代替它”会更加方便!但这是不能做到的。你是对的,唯一的解决办法是改变代码并注入模拟,“手动”。感谢您花时间回复。 – gMale 2010-11-03 13:21:47
不客气。注入依赖关系通常是好的做法,而不是通过“新建”来创建它们。它使你的代码更可测试。米斯科Hevery有很好的指导:http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/ – amorfis 2010-11-03 14:40:44
@gmale:其实,你可以做到这一点(即,模拟所有实例),但它需要更强大的嘲弄工具,如JMockit(我自己的)或PowerMockito。 – 2010-12-08 16:07:56
我认为你必须创建苹果,然后模拟工厂工厂 – 2010-10-15 12:29:19
@Alois:沿着这些线可能是正确的,但。 。 。如何让ProductManager使用工厂(从GroceryStore的单元测试中)? – gMale 2010-10-15 12:31:51
用ProductManager中的setter来定义工厂。你使用任何DI(依赖注入)框架?例如春天或者guice – 2010-10-15 12:38:10