无法在 SpringBoot 项目的单元测试中注入 @Service

Posted

技术标签:

【中文标题】无法在 SpringBoot 项目的单元测试中注入 @Service【英文标题】:Cannot inject @Service in Unit Test in SpringBoot project 【发布时间】:2018-03-26 16:22:04 【问题描述】:

我有一个@Service,我试图在单元测试中模拟它,但到目前为止我得到一个空值。在应用程序类中,我指定什么是 scanBasePackages。我必须以不同的方式执行此操作吗?谢谢。

这是我实现接口的服务类:

@Service
public class DeviceService implements DeviceServiceDao 

private List<Device> devices;

@Override
public List<Device> getDevices(long homeId) 
    return devices;



这是我的单元测试。

public class SmartHomeControllerTest 

    private RestTemplate restTemplate = new RestTemplate();
    private static final String BASE_URL = “..”;

    @Mock
    private DeviceService deviceService;

@Test
public void getHomeRegisteredDevices() throws Exception 

    Device activeDevice = new DeviceBuilder()
            .getActiveDevice(true)
            .getName("Alexa")
            .getDeviceId(1)
            .getHomeId(1)
            .build();
    Device inativeDevice = new DeviceBuilder()
            .getInactiveDevice(false)
            .getName("Heater")
            .getDeviceId(2)
            .getHomeId(1)
            .build();

    UriComponentsBuilder builder = UriComponentsBuilder
            .fromUriString(BASE_URL + "/1/devices");

    List response = restTemplate.getForObject(builder.toUriString(), List.class);

    verify(deviceService, times(1)).getDevices(1);
    verifyNoMoreInteractions(deviceService);

【问题讨论】:

【参考方案1】:

你应该使用 spring boot runner 来运行你的测试

【讨论】:

【参考方案2】:

如果您想在测试执行期间加载和使用 Spring 上下文,则必须使用 Spring 测试运行器。 您没有指定任何运行程序,因此默认情况下它使用您的测试 API 的运行程序。这里可能是 JUnit 或 TestNG(使用的跑步者取决于指定的 @Test 注释)。 此外,根据您的测试逻辑,您想调用“真实” REST 服务:

List response = restTemplate.getForObject(builder.toUriString(), 
List.class);

要实现它,您应该加载 Spring 上下文并通过使用 @SpringBootTest 注释测试来加载 Spring Boot 容器。

如果使用 Spring Boot 上下文,要在 Spring 上下文中模拟依赖项,则不能使用 Mockito 中的 @Mock,而是 Spring Boot 中的 @MockBean。 要了解两者的区别,可以参考这个question。

请注意,如果您使用@SpringBootTest 注释,TestRestTemplate 将自动可用,并且可以自动连接到您的测试中。 但请注意,这是容错的。根据您的测试,它可能适合或不适合。

所以你的代码可能看起来像:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SmartHomeControllerTest 

    private static final String BASE_URL = “..”;

    @Autowired
    private TestRestTemplate restTemplate;

    @MockBean
    private DeviceService deviceService;

    @Test
    public void getHomeRegisteredDevices() throws Exception         
       ...
    

附带说明,避免将原始类型用作List,而是使用泛型类型。

【讨论】:

我没有修改这部分。我专注于注射问题。但你可能是对的。我还注意到,OP 没有对检索到的 List 做出任何断言。 我已经添加了 SpringBootTest 和 MockBean 注释,但现在我得到一个空上下文:“原因:java.lang.IllegalArgumentException:无法使用 NULL 'contextLoader' 加载 ApplicationContext。考虑注释你的测试带有@ContextConfiguration 或@ContextHierarchy 的类。"【参考方案3】:

我想通了,我正在使用 Mockito 并用它来注释我的测试类。这让我可以模拟我正在尝试使用的服务类。

@RunWith(MockitoJUnitRunner.class)
 public class SmartHomeControllerTest ..
     @Mock
     private DeviceService deviceService;
  

【讨论】:

【参考方案4】:

尝试使用@InjectMock 而不是@Mock

【讨论】:

【参考方案5】:
@RunWith(SpringJUnit4ClassRunner.class)  
@SpringBootTest(classes = NotificationApplication.class)  
public class EmailClientImplTest   
...  
  

并在中添加所需的属性/配置 /src/测试/resources/application.yml

祝你好运!

【讨论】:

以上是关于无法在 SpringBoot 项目的单元测试中注入 @Service的主要内容,如果未能解决你的问题,请参考以下文章

Java教程:Springboot项目如何使用Test单元测试

Springboot单元测试调用Service或Dao方法出现空指针异常

Springboot单元测试调用Service或Dao方法出现空指针异常

记录Spring Boot大坑一个,在bean中如果有@Test单元测试,不会注入成功

如何使用 SpringBoot2、JUnit5 和 Kotlin 将配置属性注入单元测试

SpringBoot中注入RedisTemplate实例异常解决