如何在Mockito中模拟部分字段

时间:2017-12-04 11:38:43

标签: java unit-testing spring-boot mockito junit4

我正在为我的服务层编写单元测试。我的服务层有多个自动装配的字段。我想模拟其中一个和其他人初始化为自动装配。

服务接口

public interface ProductSupplierService {

    Map<String, List<? extends BaseDTO>> getProductSuppliers(Long productId, Long tenantId);

    ProductSupplierDTO addProductSupplier(Long productId, Long tenantId, ProductSupplierDTO productSupplierDTO);

    ProductSupplierDTO editProductSupplier(Long productId, Long supplierId, Long tenantId,
            ProductSupplierDTO productSupplierDTO);

    void deleteProductSupplier(Long productId, Long supplierId, Long tenantId);
}

服务实施

@Service
public class ProductSupplierServiceImpl implements ProductSupplierService {

    private MapperFacade mapper;

    @Autowired
    public void setMapperFactory(MapperFactory mapperFactory) {
        this.mapper = mapperFactory.getMapperFacade();
    }

    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private ProductManager productManager;

    private static final Logger log = LoggerFactory.getLogger(ProductSupplierServiceImpl.class);

    @Override
    public Map < String, List << ? extends BaseDTO >> getProductSuppliers(Long productId, Long tenantId) {
        Product product = fetchProductByProductIdAndTenantId(productId, tenantId);
        ListResponse listResponse = new ListResponse();
        if (CollectionUtil.nonNullNonEmpty(product.getProductSuppliers())) {
            List < ProductSupplierDTO > productSupplierDTOS = new ArrayList < > (0);
            product.getProductSuppliers().stream().filter(Objects::nonNull)
                .forEach(productSupplier - > productSupplierDTOS
                    .add(productSupplier.toDTO(ProductSupplierDTO.class, mapper)));
            listResponse.addResponse("product_suppliers", productSupplierDTOS);
        }
        return listResponse.getResponse();
    }

    @Override
    public ProductSupplierDTO addProductSupplier(Long productId, Long tenantId, ProductSupplierDTO productSupplierDTO) {
        Product product = fetchProductByProductIdAndTenantId(productId, tenantId);
        ProductSupplier productSupplier = productSupplierDTO.toModel(ProductSupplier.class, mapper);
        if (product.getProductSuppliers().add(productSupplier)) {
            productManager.applyProductSupplier(product, tenantId, productSupplier);
            product.setModified(new Date());
            try {
                productRepository.save(product);
                Optional < ProductSupplier > savedProductSupplier = product.getProductSuppliers().stream()
                    .filter(Objects::nonNull)
                    .filter(ps - > ps.getSupplierId().equals(productSupplierDTO.getSupplierId())).findFirst();
                if (savedProductSupplier.isPresent()) {
                    return savedProductSupplier.get().toDTO(ProductSupplierDTO.class, mapper);
                } else {
                    throw new UnexpectedException();
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw new UnexpectedException();
            }
        } else {
            throw new BusinessValidationException("supplier already exists");
        }
    }

    @Override
    public ProductSupplierDTO editProductSupplier(Long productId, Long supplierId, Long tenantId,
        ProductSupplierDTO productSupplierDTO) {
        Product product = fetchProductByProductIdAndTenantId(productId, tenantId);
        Optional < ProductSupplier > productSupplierOptional = product.getProductSuppliers().stream()
            .filter(Objects::nonNull)
            .filter(productSupplier - > productSupplier.getSupplierId().equals(supplierId)).findFirst();
        if (productSupplierOptional.isPresent()) {
            ProductSupplier productSupplier = productSupplierOptional.get();
            if (Objects.nonNull(productSupplierDTO.getBuyPrice())) {
                productSupplier.setBuyPrice(productSupplierDTO.getBuyPrice());
            }
            if (Objects.nonNull(productSupplierDTO.isDefaultSupplier())) {
                if (productSupplierDTO.isDefaultSupplier()) {
                    product.getProductSuppliers().forEach(supplier - > supplier.setDefaultSupplier(false));
                    productSupplier.setDefaultSupplier(true);
                } else {
                    productSupplier.setDefaultSupplier(false);
                }
            }
            productSupplier.setModified(new Date());

            product.setModified(new Date());
            try {
                productRepository.save(product);
                return productSupplier.toDTO(ProductSupplierDTO.class, mapper);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw new UnexpectedException();
            }
        } else {
            throw new EntityNotFoundException(ProductSupplier.class, String.valueOf(supplierId));
        }
    }

    @Override
    public void deleteProductSupplier(Long productId, Long supplierId, Long tenantId) {
        Product product = fetchProductByProductIdAndTenantId(productId, tenantId);
        Optional < ProductSupplier > productSupplierOptional = product.getProductSuppliers().stream()
            .filter(Objects::nonNull)
            .filter(productSupplier - > productSupplier.getSupplierId().equals(supplierId)).findFirst();
        if (productSupplierOptional.isPresent()) {
            product.getProductSuppliers().remove(productSupplierOptional.get());
            product.setModified(new Date());
            try {
                productRepository.save(product);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw new UnexpectedException();
            }
        } else {
            throw new EntityNotFoundException(ProductSupplier.class, String.valueOf(supplierId));
        }
    }

    private Product fetchProductByProductIdAndTenantId(Long productId, Long tenantId) {
        Product product = productRepository.findByIdAndTenantId(productId, tenantId);
        if (Objects.nonNull(product)) {
            return product;
        } else {
            throw new EntityNotFoundException(Product.class, String.valueOf(productId));
        }
    }
}

测试课

@RunWith(MockitoJUnitRunner.class)
public class ProductSupplierServiceUnitTest {

    @Mock
    private ProductRepository productRepository;

    @Autowired
    @InjectMocks
    private ProductSupplierServiceImpl productSupplierService;

    @Test(expected = EntityNotFoundException.class)
    public void productNotFound() {
        Mockito.when(productRepository.findByIdAndTenantId(invalidProductId, tenantId)).thenReturn(null);
        productSupplierService.getProductSuppliers(invalidProductId, tenantId);
    }

    @Test
    public void getProductSuppliersSuccess() {

        initProduct();
        initProductSupplier();

        Set < ProductSupplier > productSuppliers = new HashSet < > (Collections.singletonList(productSupplierBuilder.get()));
        Product product = productBuilder.setProductSuppliers(productSuppliers).get();
        product.setId(validProductId);

        Mockito.when(productRepository.findByIdAndTenantId(validProductId, tenantId)).thenReturn(product);

        Map < String, List << ? extends BaseDTO >> result = productSupplierService.getProductSuppliers(validProductId,
            tenantId);
        Assert.assertEquals(result.size(), 1);
        Assert.assertTrue(result.containsKey("product_suppliers"));
    }

    @Test
    public void getProductSuppliersEmpty() {

        initProduct();
        initProductSupplier();

        Product product = productBuilder.setProductSuppliers(Collections.emptySet()).get();
        product.setId(validProductId);

        Mockito.when(productRepository.findByIdAndTenantId(validProductId, tenantId)).thenReturn(product);

        Map < String, List << ? extends BaseDTO >> result = productSupplierService.getProductSuppliers(validProductId,
            tenantId);

        Assert.assertTrue(result.isEmpty());
    }

}

我只想模拟productRepository字段,而是productManager&amp;映射器字段必须自动初始化。

问题是productManager&amp;运行测试时映射器字段为空。

是否可以自动初始化它们?就像在使用完全加载的上下文运行spring boot应用程序时初始化它们一样。

2 个答案:

答案 0 :(得分:0)

如果是“productManager”&amp; “mapper”是简单的类,只需添加:

@Spy
ProductManager productManager = new ProductManager();

@Spy
Mapper mapper = new Mapper();

Mockito知道使用@Spy为@InjectMocks注释的对象。如果这些类具有自己的@Autowired字段,那么它超出了Mockito的功能,常见的解决方案是使用SpringJUnit4ClassRunner并在Spring上下文配置中使用Mockito:

<bean id="mockBean" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="some.real.bean.Class" />
</bean>

答案 1 :(得分:0)

要回答@Harshit的最新评论中的问题,可以为测试配置上下文:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ExampleTestConfiguration.class})

...在ExampleTestConfiguration的@ComponentScan中定义包含和排除的包