Java 如何简化测试?
我想测试我的方法Java 如何简化测试?,java,unit-testing,junit,mockito,Java,Unit Testing,Junit,Mockito,我想测试我的方法IOConsoleWriterImpl.displayAllClientsInfo(列出客户端)是否打印数据 displayAllClientsInfo的结构大致如下: for (ClientEntity client : clients) { System.out.println(client.getName()); List<AccountEntity> accounts = client.getAccountEntities(); for
IOConsoleWriterImpl.displayAllClientsInfo(列出客户端)
是否打印数据
displayAllClientsInfo
的结构大致如下:
for (ClientEntity client : clients) {
System.out.println(client.getName());
List<AccountEntity> accounts = client.getAccountEntities();
for (AccountEntity account : accounts){
System.out.println(account.getLogin());
}
}
for(客户实体客户:客户){
System.out.println(client.getName());
List accounts=client.getAccountEntities();
for(会计实体账户:账户){
System.out.println(account.getLogin());
}
}
因此,据我所知,为了模拟两个foreach循环(使用Mockito),我需要模拟两个迭代器
有一个测试:
@Test
void isDisplayAllClientsInfoPrintData() {
//Given
List<ClientEntity> clients = mock(List.class);
List<AccountEntity> accounts = mock(List.class);
Iterator<ClientEntity> clientIterator = mock(Iterator.class);
Iterator<AccountEntity> accountIterator = mock(Iterator.class);
ClientEntity client = mock(ClientEntity.class);
AccountEntity account = mock(AccountEntity.class);
when(clientIterator.hasNext()).thenReturn(true, false);
when(clientIterator.next()).thenReturn(client);
when(clients.iterator()).thenReturn(clientIterator);
when(accountIterator.hasNext()).thenReturn(true, false);
when(accountIterator.next()).thenReturn(account);
when(accounts.iterator()).thenReturn(accountIterator);
when(clients.size()).thenReturn(1);
when(client.getAccountEntities()).thenReturn(accounts);
when(client.getId()).thenReturn(1L);
when(client.getEmail()).thenReturn("client@example.com");
when(client.getName()).thenReturn("John Smith");
when(account.getId()).thenReturn(2L);
when(account.getCreated()).thenReturn(LocalDateTime.of(2017,5,25,12,59));
when(account.getLogin()).thenReturn("JSmith");
when(account.getPassword()).thenReturn("zzwvp0d9");
//When
IOConsoleWriter io = new IOConsoleWriterImpl();
io.displayAllClientsInfo(clients);
//Then
String output = outputStream.toString();
assertAll(
() -> assertTrue(output.contains(Long.toString(1))),
() -> assertTrue(output.contains("client@example.com")),
() -> assertTrue(output.contains("John Smith")),
() -> assertTrue(output.contains(Long.toString(2))),
() -> assertTrue(output.contains(LocalDateTime.of(2017,5,25,12,59).toString())),
() -> assertTrue(output.contains("JSmith")),
() -> assertTrue(output.contains("zzwvp0d9"))
);
}
@测试
void isDisplayAllClientsInfoPrintData(){
//给定
List clients=mock(List.class);
List accounts=mock(List.class);
迭代器clientIterator=mock(Iterator.class);
迭代器accountIterator=mock(Iterator.class);
ClientEntity client=mock(ClientEntity.class);
AccountEntity account=mock(AccountEntity.class);
when(clientIterator.hasNext())。然后返回(true,false);
when(clientIterator.next())。然后返回(client);
当(clients.iterator())。然后返回(clientIterator);
when(accountIterator.hasNext())。然后返回(true,false);
when(accountIterator.next())。然后返回(account);
when(accounts.iterator()).thenReturn(accountIterator);
当(clients.size())。然后返回(1);
当(client.getAccountEntities())。然后返回(accounts);
when(client.getId())。然后返回(1L);
当(client.getEmail())。然后返回(“client@example.com");
when(client.getName())。然后返回(“John Smith”);
when(account.getId())。然后返回(2L);
when(account.getCreated()).thenReturn(LocalDateTime.of(2017,5,25,12,59));
当(account.getLogin()).thenReturn(“JSmith”);
当(account.getPassword())。然后返回(“zzwvp0d9”);
//什么时候
IOConsoleWriter io=新IOConsoleWriterImpl();
io.displayAllClientsInfo(客户端);
//然后
字符串输出=outputStream.toString();
资产(
()->assertTrue(output.contains(Long.toString(1)),
()->assertTrue(output.contains()client@example.com")),
()->assertTrue(output.contains(“John Smith”),
()->assertTrue(output.contains(Long.toString(2)),
()->assertTrue(output.contains(LocalDateTime.of(2017,5,25,12,59).toString()),
()->assertTrue(output.contains(“JSmith”),
()->assertTrue(output.contains(“zzwvp0d9”))
);
}
我相信有一种很好的方法可以避免代码重复(我指的是测试的第二段和第三段)。或者我不必担心,一切都很好?这一切看起来有点尴尬,所以我可以理解你关于如何简化它的想法 您可以只传入一个实际的列表,其中包含
ClientInfo
实例,而不是模拟的实例。例如:
List<ClientInfo> clientInfos = new ArrayList<>();
clients.add(new ClientInfo(1L, "client@example.com", "John Smith",
Arrays.asList(
new Account(2L, LocalDateTime.of(2017,5,25,12,59), "JSmith", "zzwvp0d9"))
)
);
io.displayAllClientsInfo(clients);
然后,在您的测试用例中,您可以将模拟的编写器
注入IOConsoleWriter
中,并验证是否使用预期状态调用了它
Writer writer = Mockito.mock(Writer.class);
IOConsoleWriter io = new IOConsoleWriterImpl(writer);
io.displayAllClientsInfo(clients);
Mockito.verify(writer).write(...);
类似地,您可以提供Writer
的存根实现,它记录给定的内容,然后在此存根的内容上断言。例如:
public class RecordingWriter implements Writer {
private List<String> recordings = new ArrayList<>();
@Override
public void write(String output) {
recordings.add(output);
}
public boolean contains(String incoming) {
return recordings.contains(incoming);
}
}
RecordingWriter writer = new RecordingWriter();
IOConsoleWriter io = new IOConsoleWriterImpl(writer);
io.displayAllClientsInfo(clients);
assertAll(
() -> assertTrue(writer.contains(Long.toString(1))),
() -> assertTrue(writer.contains("client@example.com")),
() -> assertTrue(writer.contains("John Smith")),
() -> assertTrue(writer.contains(Long.toString(2))),
() -> assertTrue(writer.contains(LocalDateTime.of(2017,5,25,12,59).toString())),
() -> assertTrue(writer.contains("JSmith")),
() -> assertTrue(writer.contains("zzwvp0d9"))
);
公共类RecordingWriter实现Writer{
私有列表录制=新建ArrayList();
@凌驾
公共无效写入(字符串输出){
添加(输出);
}
公共布尔包含(传入字符串){
返回录制。包含(传入);
}
}
RecordingWriter=新的RecordingWriter();
IOConsoleWriter io=新IOConsoleWriterImpl(写入程序);
io.displayAllClientsInfo(客户端);
资产(
()->assertTrue(writer.contains(Long.toString(1)),
()->assertTrue(writer.contains()client@example.com")),
()->assertTrue(writer.contains(“John Smith”),
()->assertTrue(writer.contains(Long.toString(2)),
()->assertTrue(writer.contains(LocalDateTime.of(2017,5,25,12,59).toString()),
()->assertTrue(writer.contains(“JSmith”),
()->assertTrue(writer.contains(“zzwvp0d9”))
);
更新1:根据您的评论和您提供的与实际课程的链接
它看起来像是IOConsoleWriterImpl.displayAllClientsInfo()
有两个职责:
- 询问
集合并确定要打印的内容ClientInfo
- 打印输出(包括标题和格式)
编写器接口会带来一些好处:
- 升级
IOConsoleWriterImpl的SRP
- 简化
IOConsoleWriterImpl
- 有助于简化对
IOConsoleWriterImpl
的测试,因为IOConsoleWriterTest
可以只关注客户端的询问职责
- 有助于简化“写作”行为的测试;您可以编写一个
SystemOutWriterTest
,它只关注编写者的职责
然而,你已经做的是好的;它很好地覆盖了IOConsoleWriterImpl.displayAllClientsInfo()
,尽管它非常详细,但仍然可读
总之,我建议传入一个实际的列表是最简单的更改,它(a)在功能上等同于您当前拥有的,并且(b)涉及较少的设置/更易于阅读。除此之外,我关于提取新接口背后的“编写”行为的建议将简化IOConsoleWriterImpl
,并使您的测试更细粒度(每个测试用例可能更小,更容易推理),我认为这一更改将非常简单。当然,你对改变的渴望可能会有所不同;)还有好处呢
public class RecordingWriter implements Writer {
private List<String> recordings = new ArrayList<>();
@Override
public void write(String output) {
recordings.add(output);
}
public boolean contains(String incoming) {
return recordings.contains(incoming);
}
}
RecordingWriter writer = new RecordingWriter();
IOConsoleWriter io = new IOConsoleWriterImpl(writer);
io.displayAllClientsInfo(clients);
assertAll(
() -> assertTrue(writer.contains(Long.toString(1))),
() -> assertTrue(writer.contains("client@example.com")),
() -> assertTrue(writer.contains("John Smith")),
() -> assertTrue(writer.contains(Long.toString(2))),
() -> assertTrue(writer.contains(LocalDateTime.of(2017,5,25,12,59).toString())),
() -> assertTrue(writer.contains("JSmith")),
() -> assertTrue(writer.contains("zzwvp0d9"))
);