Java 嘲笑是行不通的

Java 嘲笑是行不通的,java,junit,mocking,mockito,amazon-dynamodb,Java,Junit,Mocking,Mockito,Amazon Dynamodb,无论何时运行以下代码,都会出现错误: Wanted but not invoked: dynamoDBWriter.addItemsToDynamoTable( <any>, <any> ); -> at DynamoDBWriterTest.testAllItemsAdded(DynamoDBWriterTest.java:32) However, there were other interactions with this mock: dyn

无论何时运行以下代码,都会出现错误:

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)
需要但未调用:
dynamoDBWriter.ADDINATEMSTODYNAMOTABLE(
,
);
->在DynamoDBWriterTest.testAllItemsAdded(DynamoDBWriterTest.java:32)
但是,与此模拟还有其他交互:
发电机写入器(
[{Item:{},{Item:{},{Item:{},{Item:{},{Item:{},{Item:{}]
);
->在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());
    }
}
公共类dynamodbwriterst{
发电机记录器;
@以前
公共作废设置(){
dynamoDbWriter=mock(dynamoDbWriter.class);
}
@试验
公共无效测试项附加(){
列表项=新建ArrayList();
对于(int-index=0;index<5;index++){
添加(新项());
}
dynamoDbWriter.write(项目);
验证(dynamoDbWriter,次数(5))。可添加dynamoDbWriter(任意(),任意());
}
}
来自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;
        }
    }
公共作废写入(列表项){
//初始化速率限制器以允许容量单位/秒
//因为我们知道我们放置的项目消耗1个单位的吞吐量。
RateLimiter RateLimiter=RateLimiter.create(1);
//跟踪每个put操作消耗的吞吐量
用于(项目:项目){
//让速率限制器等待我们所需的吞吐量“充电”
rateLimiter.acquire();
附加动力(表,项目);
}
}
受保护的void addItemsToDynamoTable(表、项){
试一试{
表2.项目(项目);
}捕获(运行时异常e){
logger.fatal(“dynamoDB table.putItem为:“+tableName,e”引发异常);
投掷e;
}
}
谢谢你的帮助。我还添加了我使用的实际“更正/工作”代码:

@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());
    }
@之前
公共作废设置(){
dynamoDbWriter=spy(dynamoDbWriter….);
doNothing().when(dynamoDbWriter).addItemsToDynamoTable(any(),any());
}
//方法确保无论吞吐量如何,所有项都添加到dynamoDB
@试验
公共无效测试项附加(){
列表项=新建ArrayList();
对于(int-index=0;index<5;index++){
添加(新项());
}
dynamoDbWriter.write(项目);
验证(dynamoDbWriter,次数(5))。可添加dynamoDbWriter(任意(),任意());
}
当您创建模拟DynamoDBWriter时,默认情况下,它会使用不执行任何操作的存根覆盖所有方法。调用
write
时,Mockito的替换实现允许验证是否调用了
write
,而不是
write
实现进行的任何调用。该实现从未被调用

当然,真正的问题是你在嘲笑你正在测试的类。即使有了技术上的变通方法,测试模拟框架也非常容易,而不是在测试中模拟您的类。一般来说,为您的被测系统的合作者保留模拟,而不是为您的被测系统本身保留模拟。(另请参见JB Nizet的类比。)


也就是说,如果绝对必要,您可以使用真实实例的
spy
,并根据需要选择性地重写方法,或者更危险的是,您可以在模拟上使用
thenCallRealMethod

when(dynamoDbWriter.write(any())).thenCallRealMethod();

关于两者之间的区别,以及为什么调用RealMethod如此危险,请参见。

您正在模拟
DynamoDBWriter
,这意味着对它的调用不是真正调用的(它们被调用了,但其中的内部代码没有执行,这只是一个模拟)。 因此,这里出现的错误是因为只执行了
write
,而不是
addItemsToDynamoTable

你在这里模拟的对象是错误的-如果你想测试
DynamoDBWriter
,你不应该模拟它,你应该模拟它使用的外围对象

在本例中,我将模拟table,验证
table.put
是否执行了5次(从代码中不确定如何构造
DynamoDBWriter
并将表传递给它,但我猜您可以将模拟的表传递给它)