通过单元测试测试服务方法



我没有测试经验,尝试通过单元测试来测试一个方法。我看到的所有示例都使用mock值执行操作。我知道,我还将在mockito中使用我在项目中使用的mock值。这是我想测试的服务方法:

ProductServiceImpl:

public List<ProductDTO> findAllByCategoryUuid(UUID categoryUuid) {
// code omitted
return result;
}

这是我的单元测试课程:

ProductServiceImplTest:

// ? @InjectMocks
@Autowired 
ProductServiceImpl productService;
@Mock
ProductRepository productRepository;

@Test
public void testFindAllByCategoryUuid() {
UUID categoryUuid = UUID.randomUUID();
final List<Product> productList = new ArrayList<>();
for (int i = 0; i < size; i++) {
// create product by setting "categoryUuid" and add to productList
}
productRepository.saveAll(productList);

when(productService.findAllByCategoryUuid(categoryUuid)
.thenReturn(productList);
}

我的问题:

1.为了测试服务方法,上述方法是否正确?我认为我不应该处理内部服务方法,只通过categoryUuid并检查该方法的结果进行测试?这是真的吗?

2.在测试类中,我使用@Autowired访问服务方法,但我不确定是否应该使用@Mock。有什么错误吗?

如有任何帮助,我们将不胜感激。


更新:我还使用DiffBlue插件创建了单元测试,它生成了一个测试方法,如下所示。但我认为这似乎是在测试存储库方法,而不是服务方法。不是吗?

@Test
public void testFindAllByCategoryUuid() {
when(this.productRepository.findAllByCategoryUuid((UUID) any()))
.thenReturn(new ArrayList<Product>());
assertTrue(this.productService.findAllByCategoryUuid(UUID.randomUUID())
.isEmpty());
verify(this.productRepository).findAllByCategoryUuid((UUID) any());
// ...
}

我不是专家,但我会尽力回答你的问题

单元测试方法的一般方法应该是针对所有可能的输入集测试输出。在您的特定情况下,您可以测试

  • 输入:现有UUID输出:非空列表
  • 输入:不存在UUID输出:空列表
  • input:null:空列表

现在您在这里所做的是正确的,您需要自动连接正在为其编写测试用例的类,并模拟该类中的依赖关系。唯一错误的是

when(productService.findAllByCategoryUuid(categoryUuid)
.thenReturn(productList);

应该是

when(productRepository.findAllByCategoryUuid(categoryUuid)
.thenReturn(productList);   

在这里,您正在嘲笑productRepository.findAllByCategoryUuid,因为您的目标是在服务类中测试该方法。

在这之后,只需为上面提到的所有条件添加适当的断言语句。

此外,每当针对某些代码记录错误时,我通常会遵循一条规则,我会在Junit中使用assert来覆盖输入和输出情况,这样每次我都会测试所有可能的输入和输出场景。

针对服务层编写单元测试有缺点:

  1. 您违反了测试中方法的封装。如果它因为您开始调用不同的类/方法而发生变化,那么测试就会中断。即使该方法可能工作正常
  2. 因为您打算使用mocking,所以部分测试将只是检查您的mock是否设置为通过测试。所以基本上你将测试测试逻辑

通常将逻辑向下移动(例如,移动到模型(会更有效率。然后可以在没有mock的情况下对这些类进行单元测试。然后,您可以编写一个更高级别的测试(包括DB(,检查所有内容是否协同工作。

读数:

  • 贫血架构-测试的敌人
  • 构建测试金字塔以优化自动化测试

使用Mockito 编写Junit测试时需要记住的重要事项

  1. 所有类级别@Runwith((
  2. 测试类应该使用@InjectMocks
  3. 所有测试都应标注@Test
  4. 任何外部服务都应使用@Mock进行模拟
  5. 任何对DB或其他服务的调用都应该被模拟,并相应地返回值
  6. 您应该使用断言来测试结果

我会写这样的东西:

@RunWith(MockitoJUnitRunner.class)
public class ProductServiceImplTest {
@InjectMocks
ProductServiceImpl productService;
@Mock
ProductRepository productRepository;
@Test
public void testFindAllByCategoryUuid() {
UUID categoryUuid = UUID.randomUUID();
final List<Product> productList = new ArrayList<>();
for (int i = 0; i < size; i++) {
// create product by setting "categoryUuid" and add to productList
}

when(productRepository.saveAll(ArgumentMatchers.any()).thenReturn(productList); 
// Or below might work for newer version of test cases when we get Null Pointer Exp using older version of Junit test cases
//doReturn(productList).when(productRepository).saveAll(any(List.class));

List<ProductDTO> response = productService.findAllByCategoryUuid(categoryUuid);

Assert.assertNotNull(response);
Assert.assertEquals("Object Value", response.getXXX());
}

最新更新