ModelMapper 使用 JUnit Mockito 抛出 NPE
ModelMapper throws NPE using JUnit Mockito
我正在使用 ModelMapper 进行 NPE
目录服务测试
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class CatalogServiceTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@InjectMocks private CatalogService service;
@Mock ModelMapper modelMapper;
@Mock CatalogMapper catalogMapper;
@Mock CatalogRepository catalogRepository;
@Before
public void setUp() throws Exception {
// MockitoAnnotations.initMocks(this);
CatalogEntity catalogEntity = new CatalogEntity();
catalogEntity.setId("id");
catalogEntity.setCode("code");
catalogEntity.setType("type");
catalogEntity.setValue("value");
// Optional<CatalogEntity> optionalCatalog = Optional.of(catalogEntity);
when(catalogRepository.findByCode(any(String.class))).thenReturn(catalogEntity);
}
@Test
public void whenFindByCode() {
//Act
CatalogDto myCatalogDto = service.findByCode("code");
//Assert
assertTrue(myCatalogDto.getCode().equals("code"));
}
}
目录服务
@Service
public class CatalogService {
private static final Logger LOGGER = LoggerFactory.getLogger(CatalogService.class);
@Autowired
CatalogRepository catalogRepository;
@Autowired
CatalogMapper catalogMapper;
/**
*
* @param type
* @return catalog objects which type is type
*/
public List<CatalogDto> findByType(String type) {
LOGGER.info("Getting catalogs by type {}", type);
List<CatalogEntity> catalogsEntityList = catalogRepository.findByType(type);
List<CatalogDto> catalogDtoList = new ArrayList<>();
catalogsEntityList.forEach(catalogEntity -> {
catalogDtoList.add(catalogMapper.convertCatalogEntityToCatalogDto(catalogEntity));
});
return catalogDtoList;
}
/**
* Find catalog by code.
* @param code
* @return catalog
*/
public CatalogDto findByCode(String code) {
LOGGER.info("Getting catalogs by code {}", code);
return catalogMapper.convertCatalogEntityToCatalogDto(catalogRepository.findByCode(code));
}
}
目录映射器
@Component
public class CatalogMapper {
@Autowired
private ModelMapper modelMapper;
/**
* Converts CatalogEntity object to CatalogDto object
* @param catalogEntity
* @return converted CatalogDto object
*/
public CatalogDto convertCatalogEntityToCatalogDto(CatalogEntity catalogEntity) {
return modelMapper.map(catalogEntity, CatalogDto.class);
}
}
目录库
@Repository
public interface CatalogRepository extends MongoRepository<CatalogEntity, String> {
List<CatalogEntity> findByType(String type);
CatalogEntity findByCode(String code);
}
问题
catalogRepository.findByCode(code)
按预期 returning CatalogEntity 对象,问题出现在执行 catalogMapper.convertCatalogEntityToCatalogDto(catalogRepository.findByCode(code));
that return null.
之后
我正在使用 Intellij,这是刚好在执行 catalogMapper.convertCatalogEntityToCatalogDto
函数之前的断点。
catalogMapper 是一个没有存根方法的模拟。
有几种方法可以修复测试:
选项 1:仅测试与 CatalogMapper 的交互
在此选项中,您将对 catalogMapper.convertCatalogEntityToCatalogDto
的调用存根。这是一个精简单元测试,您只测试与协作服务的交互。
正如您所说,您想要测试映射器的实际实现,有两种选择:
方案二:使用SpringBootTest
在此选项中,您依赖 SpringBootTest 来设置整个应用程序上下文。
您需要进行以下更改:
- 使用
@Autowired
而不是 @InjectMock
让你的对象受到测试
- 对存储库使用
@MockBean
而不是 @Mock
。 SpringBootTest
忽略 @Mock
.
- 摆脱其他模拟
- 因为它会创建整个应用程序上下文,所以我会排除此选项,除非完整的集成测试是您的目标(您在代码中以
@SpringBootTest
开始)
@SpringBootTest
public class CatalogServiceTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Autowired
private CatalogService service;
@MockBean
CatalogRepository catalogRepository;
}
选项 3:自行构建您需要的服务
- 去掉
@SpringBootTest
- 仅模拟您想要模拟的对象 - 存储库
- 为其他服务创建真实对象
- 您可能需要在您的服务中将字段注入更改为构造函数注入,无论如何这是个好主意
@Service
public class CatalogService {
final CatalogRepository catalogRepository;
final CatalogMapper catalogMapper;
@Autowired
public CatalogService(CatalogRepository catalogRepository, CatalogMapper catalogMapper) {
this.catalogRepository = catalogRepository;
this.catalogMapper = catalogMapper;
}
}
这种方法只创建测试使用的对象,而不是整个应用程序上下文,因此可能会导致比选项 2 更精简的测试。
@RunWith(MockitoJUnitRunner.class)
public class CatalogServiceTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private CatalogService service;
@Mock
CatalogRepository catalogRepository;
@Before
public void setUp() throws Exception {
var modelMapper = new ModelMapper();
var catalogMapper =new CatalogMapper(modelMapper);
service = new CatalogService(catalogRepository, catalogMapper);
CatalogEntity catalogEntity = new CatalogEntity();
catalogEntity.setId("id");
catalogEntity.setCode("code");
when(catalogRepository.findByCode(any(String.class))).thenReturn(catalogEntity);
}
}
我正在使用 ModelMapper 进行 NPE
目录服务测试
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class CatalogServiceTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@InjectMocks private CatalogService service;
@Mock ModelMapper modelMapper;
@Mock CatalogMapper catalogMapper;
@Mock CatalogRepository catalogRepository;
@Before
public void setUp() throws Exception {
// MockitoAnnotations.initMocks(this);
CatalogEntity catalogEntity = new CatalogEntity();
catalogEntity.setId("id");
catalogEntity.setCode("code");
catalogEntity.setType("type");
catalogEntity.setValue("value");
// Optional<CatalogEntity> optionalCatalog = Optional.of(catalogEntity);
when(catalogRepository.findByCode(any(String.class))).thenReturn(catalogEntity);
}
@Test
public void whenFindByCode() {
//Act
CatalogDto myCatalogDto = service.findByCode("code");
//Assert
assertTrue(myCatalogDto.getCode().equals("code"));
}
}
目录服务
@Service
public class CatalogService {
private static final Logger LOGGER = LoggerFactory.getLogger(CatalogService.class);
@Autowired
CatalogRepository catalogRepository;
@Autowired
CatalogMapper catalogMapper;
/**
*
* @param type
* @return catalog objects which type is type
*/
public List<CatalogDto> findByType(String type) {
LOGGER.info("Getting catalogs by type {}", type);
List<CatalogEntity> catalogsEntityList = catalogRepository.findByType(type);
List<CatalogDto> catalogDtoList = new ArrayList<>();
catalogsEntityList.forEach(catalogEntity -> {
catalogDtoList.add(catalogMapper.convertCatalogEntityToCatalogDto(catalogEntity));
});
return catalogDtoList;
}
/**
* Find catalog by code.
* @param code
* @return catalog
*/
public CatalogDto findByCode(String code) {
LOGGER.info("Getting catalogs by code {}", code);
return catalogMapper.convertCatalogEntityToCatalogDto(catalogRepository.findByCode(code));
}
}
目录映射器
@Component
public class CatalogMapper {
@Autowired
private ModelMapper modelMapper;
/**
* Converts CatalogEntity object to CatalogDto object
* @param catalogEntity
* @return converted CatalogDto object
*/
public CatalogDto convertCatalogEntityToCatalogDto(CatalogEntity catalogEntity) {
return modelMapper.map(catalogEntity, CatalogDto.class);
}
}
目录库
@Repository
public interface CatalogRepository extends MongoRepository<CatalogEntity, String> {
List<CatalogEntity> findByType(String type);
CatalogEntity findByCode(String code);
}
问题
catalogRepository.findByCode(code)
按预期 returning CatalogEntity 对象,问题出现在执行 catalogMapper.convertCatalogEntityToCatalogDto(catalogRepository.findByCode(code));
that return null.
我正在使用 Intellij,这是刚好在执行 catalogMapper.convertCatalogEntityToCatalogDto
函数之前的断点。
catalogMapper 是一个没有存根方法的模拟。
有几种方法可以修复测试:
选项 1:仅测试与 CatalogMapper 的交互
在此选项中,您将对 catalogMapper.convertCatalogEntityToCatalogDto
的调用存根。这是一个精简单元测试,您只测试与协作服务的交互。
正如您所说,您想要测试映射器的实际实现,有两种选择:
方案二:使用SpringBootTest
在此选项中,您依赖 SpringBootTest 来设置整个应用程序上下文。
您需要进行以下更改:
- 使用
@Autowired
而不是@InjectMock
让你的对象受到测试 - 对存储库使用
@MockBean
而不是@Mock
。SpringBootTest
忽略@Mock
. - 摆脱其他模拟
- 因为它会创建整个应用程序上下文,所以我会排除此选项,除非完整的集成测试是您的目标(您在代码中以
@SpringBootTest
开始)
@SpringBootTest
public class CatalogServiceTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Autowired
private CatalogService service;
@MockBean
CatalogRepository catalogRepository;
}
选项 3:自行构建您需要的服务
- 去掉
@SpringBootTest
- 仅模拟您想要模拟的对象 - 存储库
- 为其他服务创建真实对象
- 您可能需要在您的服务中将字段注入更改为构造函数注入,无论如何这是个好主意
@Service
public class CatalogService {
final CatalogRepository catalogRepository;
final CatalogMapper catalogMapper;
@Autowired
public CatalogService(CatalogRepository catalogRepository, CatalogMapper catalogMapper) {
this.catalogRepository = catalogRepository;
this.catalogMapper = catalogMapper;
}
}
这种方法只创建测试使用的对象,而不是整个应用程序上下文,因此可能会导致比选项 2 更精简的测试。
@RunWith(MockitoJUnitRunner.class)
public class CatalogServiceTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
private CatalogService service;
@Mock
CatalogRepository catalogRepository;
@Before
public void setUp() throws Exception {
var modelMapper = new ModelMapper();
var catalogMapper =new CatalogMapper(modelMapper);
service = new CatalogService(catalogRepository, catalogMapper);
CatalogEntity catalogEntity = new CatalogEntity();
catalogEntity.setId("id");
catalogEntity.setCode("code");
when(catalogRepository.findByCode(any(String.class))).thenReturn(catalogEntity);
}
}