模拟不起作用
Mocking does not work
每当我 运行 以下代码时,我都会收到错误消息:
Wanted but not invoked:
dynamoDBWriter.addItemsToDynamoTable(
<any>,
<any>
);
-> at DynamoDBWriterTest.testAllItemsAdded(DynamoDBWriterTest.java:32)
However, there were other interactions with this mock:
dynamoDBWriter.write(
[{ Item: {} }, { Item: {} }, { Item: {} }, { Item: {} }, { Item: {} }]
);
-> at DynamoDBWriterTest.testAllItemsAdded(DynamoDBWriterTest.java:31)
以下是我的代码:
public class DynamoDBWriterTest {
DynamoDBWriter dynamoDbWriter;
@Before
public void setup() {
dynamoDbWriter = mock(DynamoDBWriter.class);
}
@Test
public void testAllItemsAdded() {
List<Item> items = new ArrayList<>();
for (int index = 0; index < 5; index++) {
items.add(new Item());
}
dynamoDbWriter.write(items);
verify(dynamoDbWriter, times(5)).addItemsToDynamoTable(any(), any());
}
}
来自 DynamoDBWriter 的代码:
public void write(List<Item> items) {
// Initialize the rate limiter to allow capacity units / sec
// Since we know that the Item we are putting consumes 1 unit throughput.
RateLimiter rateLimiter = RateLimiter.create(1);
// Track how much throughput we consume on each put operation
for (Item item: items) {
// Let the rate limiter wait until our desired throughput "recharges"
rateLimiter.acquire();
addItemsToDynamoTable(table, item);
}
}
protected void addItemsToDynamoTable(Table table, Item item) {
try {
table.putItem(item);
} catch (RuntimeException e) {
logger.fatal("dynamoDB table.putItem threw exception for:" + tableName, e);
throw e;
}
}
感谢您的帮助。我还添加了我使用的实际 "corrected / working" 代码:
@Before
public void setup() {
dynamoDbWriter = spy(DynamoDBWriter....);
doNothing().when(dynamoDbWriter).addItemsToDynamoTable(any(), any());
}
// Method makes sure that irrespective of the throughput, all the items are added to dynamoDB
@Test
public void testAllItemsAdded() {
List<Item> items = new ArrayList<>();
for (int index = 0; index < 5; index++) {
items.add(new Item());
}
dynamoDbWriter.write(items);
verify(dynamoDbWriter, times(5)).addItemsToDynamoTable(any(), any());
}
当您创建一个 mock DynamoDBWriter 时,默认情况下它会使用无操作存根覆盖所有方法。当您调用 write
时,Mockito 的替换实现 允许验证 write
被调用 而不是 任何调用 write
实现使。永远不会调用该实现。
当然,真正的问题是您在嘲笑您正在测试的 class。即使有技术上的变通办法,测试模拟框架也比模拟被测的 class 更容易。通常,为被测系统的合作者保留模拟,而不是被测系统本身。 (另见 JB Nizet 的类比 。)
也就是说,如果绝对必要,您可以使用真实实例的 spy
并根据需要有选择地覆盖方法,或者更危险的是,您可以在模拟上使用 thenCallRealMethod
:
when(dynamoDbWriter.write(any())).thenCallRealMethod();
关于两者的区别,以及为什么 thenCallRealMethod
如此危险,请参阅 。
你在mock DynamoDBWriter
,这意味着对它的调用并没有真正被调用(它们被调用了,但是其中的内部代码没有被执行,它只是一个mock)。
所以你在这里得到的错误是因为只执行了 write
,而不是 addItemsToDynamoTable
你在这里模拟了错误的对象——如果你想测试DynamoDBWriter
,你不应该模拟它,你应该模拟它使用的外围对象。
在这种情况下,我会模拟 table,验证 table.put
被执行了 5 次(从代码中不确定你是如何构造 DynamoDBWriter
并传递 table 给它,但我想你可以将模拟的 table 传递给它)
每当我 运行 以下代码时,我都会收到错误消息:
Wanted but not invoked:
dynamoDBWriter.addItemsToDynamoTable(
<any>,
<any>
);
-> at DynamoDBWriterTest.testAllItemsAdded(DynamoDBWriterTest.java:32)
However, there were other interactions with this mock:
dynamoDBWriter.write(
[{ Item: {} }, { Item: {} }, { Item: {} }, { Item: {} }, { Item: {} }]
);
-> at DynamoDBWriterTest.testAllItemsAdded(DynamoDBWriterTest.java:31)
以下是我的代码:
public class DynamoDBWriterTest {
DynamoDBWriter dynamoDbWriter;
@Before
public void setup() {
dynamoDbWriter = mock(DynamoDBWriter.class);
}
@Test
public void testAllItemsAdded() {
List<Item> items = new ArrayList<>();
for (int index = 0; index < 5; index++) {
items.add(new Item());
}
dynamoDbWriter.write(items);
verify(dynamoDbWriter, times(5)).addItemsToDynamoTable(any(), any());
}
}
来自 DynamoDBWriter 的代码:
public void write(List<Item> items) {
// Initialize the rate limiter to allow capacity units / sec
// Since we know that the Item we are putting consumes 1 unit throughput.
RateLimiter rateLimiter = RateLimiter.create(1);
// Track how much throughput we consume on each put operation
for (Item item: items) {
// Let the rate limiter wait until our desired throughput "recharges"
rateLimiter.acquire();
addItemsToDynamoTable(table, item);
}
}
protected void addItemsToDynamoTable(Table table, Item item) {
try {
table.putItem(item);
} catch (RuntimeException e) {
logger.fatal("dynamoDB table.putItem threw exception for:" + tableName, e);
throw e;
}
}
感谢您的帮助。我还添加了我使用的实际 "corrected / working" 代码:
@Before
public void setup() {
dynamoDbWriter = spy(DynamoDBWriter....);
doNothing().when(dynamoDbWriter).addItemsToDynamoTable(any(), any());
}
// Method makes sure that irrespective of the throughput, all the items are added to dynamoDB
@Test
public void testAllItemsAdded() {
List<Item> items = new ArrayList<>();
for (int index = 0; index < 5; index++) {
items.add(new Item());
}
dynamoDbWriter.write(items);
verify(dynamoDbWriter, times(5)).addItemsToDynamoTable(any(), any());
}
当您创建一个 mock DynamoDBWriter 时,默认情况下它会使用无操作存根覆盖所有方法。当您调用 write
时,Mockito 的替换实现 允许验证 write
被调用 而不是 任何调用 write
实现使。永远不会调用该实现。
当然,真正的问题是您在嘲笑您正在测试的 class。即使有技术上的变通办法,测试模拟框架也比模拟被测的 class 更容易。通常,为被测系统的合作者保留模拟,而不是被测系统本身。 (另见 JB Nizet 的类比
也就是说,如果绝对必要,您可以使用真实实例的 spy
并根据需要有选择地覆盖方法,或者更危险的是,您可以在模拟上使用 thenCallRealMethod
:
when(dynamoDbWriter.write(any())).thenCallRealMethod();
关于两者的区别,以及为什么 thenCallRealMethod
如此危险,请参阅
你在mock DynamoDBWriter
,这意味着对它的调用并没有真正被调用(它们被调用了,但是其中的内部代码没有被执行,它只是一个mock)。
所以你在这里得到的错误是因为只执行了 write
,而不是 addItemsToDynamoTable
你在这里模拟了错误的对象——如果你想测试DynamoDBWriter
,你不应该模拟它,你应该模拟它使用的外围对象。
在这种情况下,我会模拟 table,验证 table.put
被执行了 5 次(从代码中不确定你是如何构造 DynamoDBWriter
并传递 table 给它,但我想你可以将模拟的 table 传递给它)