为什么测试基于 class 的 autowire 注解会抛出 null 异常?
Why tested class based autowire annotation throw null exception?
我在我的项目中使用 Spring Boot 5 和 JUnit。我创建了一个单元测试来测试服务。
这是我正在测试的服务:
@Service
@RequiredArgsConstructor
@Slf4j
public class BuilderServiceImpl implements BuilderService{
@Autowired
public AutoMapper autoMapper;
private final BuilderRepository builderRepository;
private final AdminUserRepository adminUserRepository;
@Override
public BuilderDto getByEmail(String email){
}
@Override
public List<BuilderMinDto> getAll() {}
@Override
public List<BuilderMinDto> getAll(int page, int size) {}
@Override
public SaveBuilderResponse create(Builder builder){
var str = autoMapper.getDummyText();
Builder savedBuilder = builderRepository.save(builder);
return new SaveBuilderResponse(savedBuilder);
}
}
下面是测试上述服务的测试 class:
@SpringBootTest
@RequiredArgsConstructor
@Slf4j
class BuilderServiceImplTest {
@Mock
private BuilderRepository builderRepository;
@Mock
private AdminUserRepository adminUserRepository;
private AutoCloseable autoCloseable;
private BuilderService underTest;
@BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
@AfterEach
void tearDown () throws Exception{
autoCloseable.close();
}
@Test
void getByEmail(){}
@Test
@Disabled
void getAll() { }
@Test
@Disabled
void testGetAll() {}
@Test
void create() {
//given
Builder builder = new Builder();
builder.setName("John Johnson");
builder.setCompanyName("Builders Test");
builder.setEmail("test@builders.com");
//when
underTest.create(builder);
//then
ArgumentCaptor<Builder> builderArgumentCaptor = ArgumentCaptor.forClass(Builder.class);
verify(builderRepository)
.save(builderArgumentCaptor.capture());
Builder captureBuilder = builderArgumentCaptor.getValue();
assertThat(captureBuilder).isEqualTo(builder);
}
}
当我开始 运行 测试时,class BuilderServiceImpl 中的创建方法被触发并在这一行:
var str = autoMapper.getDummyText();
我得到 NullPointerException(autoMapper 实例为空)。
这里是AutoMapper的定义class:
@Component
@Slf4j
@RequiredArgsConstructor
public class AutoMapper {
public String getDummyText(){
return "Hello From AutoMapper.";
}
}
如您所见,我使用 @Component 注释将 AutoMapper class 注册到 IoC 容器,并使用 Autowired 注释将其注入 BuilderServiceImpl 属性 中的 autoMapper class。
为什么autoMapper实例为空?如何让autoMapper初始化?
在以下字段中添加 @Autowire
注释注释。由于您未在 BuilderServiceImpl
中初始化以下对象而出错
@Autowire
private final BuilderRepository builderRepository;
@Autowire
private final AdminUserRepository adminUserRepository;
为了使 @Autowire
工作,您必须使用 spring 本身创建的 BuilderServiceImpl
(被测对象)的实例。
当您像这样创建对象时(自己手动创建):
@BeforeEach
void setUp(){
....
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
Spring 对该对象一无所知,因此自动装配将不起作用
另一件可能有用的事情:
您已将 @Mock
用于 BuilderRepository
和 AdminUserRepository
。
这是普通的 mockito 注释,如果您使用 integration/system 测试在引擎盖下运行 spring,这可能不是您想要的:
当然,它会创建一个模拟,但不会将其放入应用程序上下文中,也不会替换可能由 spring 创建的这些 类 的 bean .
因此,如果这是您想要实现的目标,您应该改用 @MockBean
。
此注释属于 Spring 测试框架,而不是普通的 mockito 注释。
总而言之,您可能会得到这样的结果:
@SpringBootTest
class MyTest
{
@MockBean
BuilderRepository builderRepo;
@MockBean
AdminUserRepository adminUserRepo;
@Autowired // spring will inject your mock repository implementations
// automatically
BuilderServiceImpl underTest;
@Test
void mytest() {
...
}
}
为什么要手动创建 BuildService?如果这样做,请手动将 AutoMapper 设置为。
@BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
underTest.setAutoMapper(new AutoMapper());
}
您没有使用 di。
我在我的项目中使用 Spring Boot 5 和 JUnit。我创建了一个单元测试来测试服务。
这是我正在测试的服务:
@Service
@RequiredArgsConstructor
@Slf4j
public class BuilderServiceImpl implements BuilderService{
@Autowired
public AutoMapper autoMapper;
private final BuilderRepository builderRepository;
private final AdminUserRepository adminUserRepository;
@Override
public BuilderDto getByEmail(String email){
}
@Override
public List<BuilderMinDto> getAll() {}
@Override
public List<BuilderMinDto> getAll(int page, int size) {}
@Override
public SaveBuilderResponse create(Builder builder){
var str = autoMapper.getDummyText();
Builder savedBuilder = builderRepository.save(builder);
return new SaveBuilderResponse(savedBuilder);
}
}
下面是测试上述服务的测试 class:
@SpringBootTest
@RequiredArgsConstructor
@Slf4j
class BuilderServiceImplTest {
@Mock
private BuilderRepository builderRepository;
@Mock
private AdminUserRepository adminUserRepository;
private AutoCloseable autoCloseable;
private BuilderService underTest;
@BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
@AfterEach
void tearDown () throws Exception{
autoCloseable.close();
}
@Test
void getByEmail(){}
@Test
@Disabled
void getAll() { }
@Test
@Disabled
void testGetAll() {}
@Test
void create() {
//given
Builder builder = new Builder();
builder.setName("John Johnson");
builder.setCompanyName("Builders Test");
builder.setEmail("test@builders.com");
//when
underTest.create(builder);
//then
ArgumentCaptor<Builder> builderArgumentCaptor = ArgumentCaptor.forClass(Builder.class);
verify(builderRepository)
.save(builderArgumentCaptor.capture());
Builder captureBuilder = builderArgumentCaptor.getValue();
assertThat(captureBuilder).isEqualTo(builder);
}
}
当我开始 运行 测试时,class BuilderServiceImpl 中的创建方法被触发并在这一行:
var str = autoMapper.getDummyText();
我得到 NullPointerException(autoMapper 实例为空)。
这里是AutoMapper的定义class:
@Component
@Slf4j
@RequiredArgsConstructor
public class AutoMapper {
public String getDummyText(){
return "Hello From AutoMapper.";
}
}
如您所见,我使用 @Component 注释将 AutoMapper class 注册到 IoC 容器,并使用 Autowired 注释将其注入 BuilderServiceImpl 属性 中的 autoMapper class。
为什么autoMapper实例为空?如何让autoMapper初始化?
在以下字段中添加 @Autowire
注释注释。由于您未在 BuilderServiceImpl
@Autowire
private final BuilderRepository builderRepository;
@Autowire
private final AdminUserRepository adminUserRepository;
为了使 @Autowire
工作,您必须使用 spring 本身创建的 BuilderServiceImpl
(被测对象)的实例。
当您像这样创建对象时(自己手动创建):
@BeforeEach
void setUp(){
....
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
Spring 对该对象一无所知,因此自动装配将不起作用
另一件可能有用的事情:
您已将 @Mock
用于 BuilderRepository
和 AdminUserRepository
。
这是普通的 mockito 注释,如果您使用 integration/system 测试在引擎盖下运行 spring,这可能不是您想要的:
当然,它会创建一个模拟,但不会将其放入应用程序上下文中,也不会替换可能由 spring 创建的这些 类 的 bean .
因此,如果这是您想要实现的目标,您应该改用 @MockBean
。
此注释属于 Spring 测试框架,而不是普通的 mockito 注释。
总而言之,您可能会得到这样的结果:
@SpringBootTest
class MyTest
{
@MockBean
BuilderRepository builderRepo;
@MockBean
AdminUserRepository adminUserRepo;
@Autowired // spring will inject your mock repository implementations
// automatically
BuilderServiceImpl underTest;
@Test
void mytest() {
...
}
}
为什么要手动创建 BuildService?如果这样做,请手动将 AutoMapper 设置为。
@BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
underTest.setAutoMapper(new AutoMapper());
}
您没有使用 di。