验证模拟对象中的对象被传递到 Mockito 中的参数
Verifying objects within a mocked object are passed into argument in Mockito
问题上下文
我有一个包含两个包含列表的对象的包装器 class。 (即 Class1 和 Class2 都有一个 Widget List。)
public class WrapperClass {
Class1 class1;
Class2 class2;
}
我有一个处理包装器的实用程序 class class
public class WrapperUtils {
public void processClasses() {
WrapperClass wrapperClass = getWrapperClass();
doSomething(class1.getWidgetList());
doSomething(class2.getWidgetList());
}
private WrapperClass getWrapperClass() {
return wrapperClassFromOnlineService;
}
public void doSomething(List<Widget> widgetList) {}
}
目标
我想使用 Mockito 验证 doSomething
方法是否通过 class1 的小部件列表被调用。
我的尝试
@Test
public void main(String[] args) {
WrapperClass wrapperClass = new WrapperClass();
wrapperClass.class1 = new Class1();
wrapperClass.class2 = new Class2();
WrapperUtils utils = new WrapperUtils();
Mockito.when(utils.getWrapperClass()).thenReturn(wrapperClass);
Mockito.verify(utils, times(1)).doSomething(wrapperClass.class1.getWidgetList());
Mockito.verify(utils, times(1)).doSomething(wrapperClass.class2.getWidgetList());
}
以上代码的结果:doSomething 注册为对两个验证语句调用两次。我的猜测是小部件列表被视为相同?
此代码存在设计问题,因此难以使用模拟进行测试。
- 测试没有清楚地暴露正在测试的对象和交互。
- 这个测试是在测试实现而不是行为,那应该是相反的。
- 由于问题 #1,mockito 被误用
那是什么意思
- 测试应显示正在测试的 case/scenario、夹具的清晰分隔、正在测试的调用以及 assertion/verification.
- 测试应该测试行为,即测试对象和协作者之间的交互,而不是内部的(因为它可能会在不破坏测试的情况下发生变化)。该测试还可以测试给定输入的预期输出。
- 如果 #1 和 #2 得到解决,那么很明显必须模拟哪种类型,并遵循教程。
这是我如何编写测试的想法,这段代码主要关注交互,但可以将断言集中在 Class
的状态上(不要在此嘲笑它们案例!!!) :
@RunWith(MockitoJUnitRunner.class)
public class WrapperUtilsTest {
@Mock Class class1;
@Mock Class class2;
@Test public void ensure_that____whatever() {
// given
WrapperUtils tested_utils = new WrapperUtils(new WrapperClass(class1, class2));
// when
tested_utils.processClass();
// then
verify(class1).someInteraction();
verify(class2).someInteraction();
}
}
实现可能看起来像
public class WrapperUtils {
private WrapperClass wrapperClass;
public WrapperUtils(WrapperClass wrapperClass) {
this.wrapperClass = wrapperClass;
}
public void processClasses() {
doSomething(wrapperClass.class1);
doSomething(wrapperClass.class2);
}
public void doSomething(Class clazz) {
clazz.someInteraction();
}
}
请注意,wrapperClass
通过构造函数注入 WrapperUtils
中,这有效,但您也可以传递 Supplier
(在番石榴或 JDK8 中可用),该供应商可以从任何地方获取数据,例如网络服务。或者它可以是你的类型。在测试中,供应商将是一个模拟。
@RunWith(MockitoJUnitRunner.class)
public class WrapperUtilsTest {
@Mock Class class1;
@Mock Class class2;
@Mock Supplier<WrapperClass> wrapped_class_supplier;
@Test public void ensure_that____whatever() {
// given
BDDMockito.given(wrapped_class_supplier.get()).willReturn(new WrapperClass(class1, class2));
WrapperUtils tested_utils = new WrapperUtils(wrapped_class_supplier);
// when
tested_utils.processClass();
// then
verify(class1).someInteraction();
verify(class2).someInteraction();
}
}
我强烈建议您遵循 测试 驱动 开发 方法,它确实有助于编写好的软件。还有这本书 Growing Object Oriented Software Guided by Tests 这本书很好读,这本书可能看起来很旧,但它仍然描述了最佳实践。
问题上下文
我有一个包含两个包含列表的对象的包装器 class。 (即 Class1 和 Class2 都有一个 Widget List。)
public class WrapperClass {
Class1 class1;
Class2 class2;
}
我有一个处理包装器的实用程序 class class
public class WrapperUtils {
public void processClasses() {
WrapperClass wrapperClass = getWrapperClass();
doSomething(class1.getWidgetList());
doSomething(class2.getWidgetList());
}
private WrapperClass getWrapperClass() {
return wrapperClassFromOnlineService;
}
public void doSomething(List<Widget> widgetList) {}
}
目标
我想使用 Mockito 验证 doSomething
方法是否通过 class1 的小部件列表被调用。
我的尝试
@Test
public void main(String[] args) {
WrapperClass wrapperClass = new WrapperClass();
wrapperClass.class1 = new Class1();
wrapperClass.class2 = new Class2();
WrapperUtils utils = new WrapperUtils();
Mockito.when(utils.getWrapperClass()).thenReturn(wrapperClass);
Mockito.verify(utils, times(1)).doSomething(wrapperClass.class1.getWidgetList());
Mockito.verify(utils, times(1)).doSomething(wrapperClass.class2.getWidgetList());
}
以上代码的结果:doSomething 注册为对两个验证语句调用两次。我的猜测是小部件列表被视为相同?
此代码存在设计问题,因此难以使用模拟进行测试。
- 测试没有清楚地暴露正在测试的对象和交互。
- 这个测试是在测试实现而不是行为,那应该是相反的。
- 由于问题 #1,mockito 被误用
那是什么意思
- 测试应显示正在测试的 case/scenario、夹具的清晰分隔、正在测试的调用以及 assertion/verification.
- 测试应该测试行为,即测试对象和协作者之间的交互,而不是内部的(因为它可能会在不破坏测试的情况下发生变化)。该测试还可以测试给定输入的预期输出。
- 如果 #1 和 #2 得到解决,那么很明显必须模拟哪种类型,并遵循教程。
这是我如何编写测试的想法,这段代码主要关注交互,但可以将断言集中在 Class
的状态上(不要在此嘲笑它们案例!!!) :
@RunWith(MockitoJUnitRunner.class)
public class WrapperUtilsTest {
@Mock Class class1;
@Mock Class class2;
@Test public void ensure_that____whatever() {
// given
WrapperUtils tested_utils = new WrapperUtils(new WrapperClass(class1, class2));
// when
tested_utils.processClass();
// then
verify(class1).someInteraction();
verify(class2).someInteraction();
}
}
实现可能看起来像
public class WrapperUtils {
private WrapperClass wrapperClass;
public WrapperUtils(WrapperClass wrapperClass) {
this.wrapperClass = wrapperClass;
}
public void processClasses() {
doSomething(wrapperClass.class1);
doSomething(wrapperClass.class2);
}
public void doSomething(Class clazz) {
clazz.someInteraction();
}
}
请注意,wrapperClass
通过构造函数注入 WrapperUtils
中,这有效,但您也可以传递 Supplier
(在番石榴或 JDK8 中可用),该供应商可以从任何地方获取数据,例如网络服务。或者它可以是你的类型。在测试中,供应商将是一个模拟。
@RunWith(MockitoJUnitRunner.class)
public class WrapperUtilsTest {
@Mock Class class1;
@Mock Class class2;
@Mock Supplier<WrapperClass> wrapped_class_supplier;
@Test public void ensure_that____whatever() {
// given
BDDMockito.given(wrapped_class_supplier.get()).willReturn(new WrapperClass(class1, class2));
WrapperUtils tested_utils = new WrapperUtils(wrapped_class_supplier);
// when
tested_utils.processClass();
// then
verify(class1).someInteraction();
verify(class2).someInteraction();
}
}
我强烈建议您遵循 测试 驱动 开发 方法,它确实有助于编写好的软件。还有这本书 Growing Object Oriented Software Guided by Tests 这本书很好读,这本书可能看起来很旧,但它仍然描述了最佳实践。