SpringBoot中普通类无法注入service的解决方案

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot中普通类无法注入service的解决方案相关的知识,希望对你有一定的参考价值。

参考技术A 先摆出实际问题:本人在项目中写了钩子方法,在service方法中,通过父类方法,调用子类的实现,结果出现,service无法注入问题?

解决方案:既然spring无法完成普通类的依赖注入,那么我们就手动getBean(思路就是手动调用ApplicationContext.getBean() )。

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

【中文标题】无法在 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的解决方案的主要内容,如果未能解决你的问题,请参考以下文章

Springboot 定时任务,service层无法注入问题详细解决

Springboot学习笔记-常用注入组件方式

springboot中如何注入一个多个实现类service接口

SpringBoot 拦截器中无法使用 @Autowired 注入 Service

Springboot中一个service接口多个实现类,如何注入

java定时器无法自动注入的问题解析(原来Spring定时器可以这样注入service)