英文:
Mocking a method that edits the input
问题
以下是您要翻译的内容:
我正在尝试模拟一个DAO方法,该方法创建记录然后在输入上设置ID。
这是一些伪代码
示例测试
RecordDto input = new RecordDto();
input.setId(null);
input.setName("test");
when(dao.createRecord(any(RecordDto.class))).thenReturn(1);
createSomething(input);
要模拟的方法
public int createRecord(RecordDto input) {
int updatedRowCount = 0;
int primaryKey = 调用数据库创建记录
rows += primary != null ? 1 : 0;
input.setId(primaryKey) <-
return updateRowCount;
}
受测试类
public void createSomething(RecordDto input)
int rowsUpdated = dao.createRecord(input);
if (input.getId() != null) {
做某事 <- 这段代码在我的测试中无法到达,
因为我的模拟仅设置返回值。它还需要修改input.id。
}
一旦在我的实际类中调用该方法,输入就会有一个ID。我的模拟只返回行数的1,但ID仍然为null。
英文:
I am trying to mock a dao method that creates a record then sets the id on the input.
here is some psuedo code
sample test
RecordDto input = new RecordDto();
input.setId(null);
input.setName("test");
when(dao.createRecord(any(RecordDto.class)).thenReturn(1);
createSomething(input);
method to mock
public int createRecord(RecordDto input) {
int updatedRowCount = 0;
int primaryKey = db call to create record
rows += primary != null ? 1 : 0;
input.setId(primaryKey) <-
return updateRowCount;
}
class under test
public void createSomething(RecordDto input)
int rowsUpdated = dao.createRecord(input);
if (input.getId() != null) {
do something <- this code can not be reached from my test
because my mock only sets the return value. It needs to also
modify the input.id.
}
Once the method is called in my real class input has an id. My mock just returns a 1 for the row count but the id is still null.
答案1
得分: 0
如果你需要除了简单返回值之外的一些额外模拟行为,可以在 when
模拟存根中使用 thenAnswer
而不是 thenReturn
。
希望这个(过于)简化的例子能帮到你:
@Mock
private Bar executor;
@Test
public void testMutation() {
Foo foo = new Foo();
when(executor.mutate(foo)).thenAnswer(a -> {
int mockedId = 10;
Foo input = a.getArgument(0);
input.setId(mockedId); // mutate the parameter from the method signature
return mockedId;
});
executor.mutate(foo);
Assertions.assertEquals(10, foo.getId());
}
//---- DTO和变异值方法的样板代码
public class Bar {
public int mutate(Foo input) {
input.setId(3);
return 3;
}
}
public class Foo {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
英文:
If you require some additional mocking behavior other than simply returning a value, you can use thenAnswer
instead of thenReturn
in your when
mocking stub.
I hope this (overly) simplified example helps:
@Mock
private Bar executor;
@Test
public void testMutation() {
Foo foo = new Foo();
when(executor.mutate(foo)).thenAnswer(a -> {
int mockedId = 10;
Foo input = a.getArgument(0);
input.setId(mockedId); // mutate the parameter from the method signature
return mockedId;
});
executor.mutate(foo);
Assertions.assertEquals(10, foo.getId());
}
//---- boiler plate code of the DTO and method that mutates the value
public class Bar {
public int mutate(Foo input) {
input.setId(3);
return 3;
}
}
public class Foo {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
答案2
得分: 0
My guess is that you are only mocking the value return but not the side effect inside the dao method. You are passing the RecordDto
as parameter, doing something and also calling methods that modify the state of the parameter received. The solution for that would be to use doAnswer
that lets you not only return a value, you can add some extra logic. So, your test should look like this:
@Test
public void test() {
dao = mock(Dao.class);
RecordDto input = new RecordDto();
Integer someRandomId = 15;
Integer randomRowsUpdated = 3;
Answer<Integer> answer = new Answer<Integer>() {
public Integer answer(InvocationOnMock invocation) {
RecordDto input = invocation.getArgument(0, RecordDto.class); // get the argument received in `createRecord`
input.setId(someRandomId); // set the new id
return randomRowsUpdated; // the value to be returned by `createdRecord`
}
};
doAnswer(answer).when(dao).createRecord(input); // using doAnswer instead of doReturn
createSomething(input);
}
That would be the quick solution.
I would say that this is not exactly the best approach. Doing side effects inside a method could cause bugs in the future, and it will be hard to know the root cause of the problem or even debug. Returning a RecordDto
or another custom object that lets you know the new id and the affected rows or something similar will make your tests easier to set up and also make the assertion.
英文:
My guess is that you are only mocking the value return but not the side effect inside the dao method. You are passing the RecordDto
as parameter, doing something and also calling methods that modify the state of the parameter received. The solution for that would be to use doAnswer
that lets you not only return a value, you can add some extra logic. So, your test should look like this:
@Test
public void test() {
dao = mock(Dao.class);
RecordDto input = new RecordDto();
Integer someRandomId = 15;
Integer randomRowsUpdated = 3;
Answer<Integer> answer = new Answer<Integer>() {
public Integer answer(InvocationOnMock invocation) {
RecordDto input = invocation.getArgument(0, RecordDto.class); // get the argument received in `createRecord`
input.setId(someRandomId); // set the new id
return randomRowsUpdated; // the value to be returned by `createdRecord`
}
};
doAnswer(answer).when(dao).createRecord(input); // using doAnswer instead of doReturn
createSomething(input);
}
That would be the quick solution.
I would say that this is not exactly the best approach. Doing side effects inside a method could cause bugs in a future and it will hard to know the root cause of the problem or even debug. Returning a RecordDto
or another custom object that lets you now the new id and the affected rows or something similar will make your tests easier to setup and also make the assertion.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论