使用模拟服务/组件进行Spring Boot集成测试

问题描述:

情况和问题:在Spring Boot中,如何将一个或多个模拟类/ bean注入应用程序以进行集成测试?在*上有几个答案,但是他们专注于Spring Boot 1.4之前的情况,或者只是不适合我。使用模拟服务/组件进行Spring Boot集成测试

背景是,在下面的代码中,设置的实现依赖于第三方服务器和其他外部系统。 Settings的功能已经在单元测试中进行过测试,因此对于完整的集成测试,我想模拟对这些服务器或系统的依赖关系,并提供虚拟值。

MockBean将忽略所有现有的bean定义并提供一个虚拟对象,但该对象在其他注入此类的类中不提供方法行为。在测试之前使用@Before方法设置行为不会影响注入的对象,或者不会注入其他应用程序服务(如AuthenticationService)。

我的问题:如何将我的bean注入应用程序上下文? 我的测试:

package ch.swaechter.testapp; 

import ch.swaechter.testapp.utils.settings.Settings; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.mockito.Mockito; 
import org.springframework.boot.test.context.SpringBootTest; 
import org.springframework.boot.test.mock.mockito.MockBean; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Primary; 
import org.springframework.test.context.junit4.SpringRunner; 

@TestConfiguration 
@RunWith(SpringRunner.class) 
@SpringBootTest(classes = {MyApplication.class}) 
public class MyApplicationTests { 

    @MockBean 
    private Settings settings; 

    @Before 
    public void before() { 
     Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret"); 
    } 

    @Test 
    public void contextLoads() { 
     String applicationsecret = settings.getApplicationSecret(); 
     System.out.println("Application secret: " + applicationsecret); 
    } 
} 

而且吼叫应该用嘲笑的类,但没有收到此嘲笑类服务:

package ch.swaechter.testapp; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Service; 

@Service 
public class AuthenticationServiceImpl implements AuthenticationService { 

    private final Settings settings; 

    @Autowired 
    public AuthenticationServiceImpl(Settings settings) { 
     this.settings = settings; 
    } 

    @Override 
    public boolean loginUser(String token) { 
     // Use the application secret to check the token signature 
     // But here settings.getApplicationSecret() will return null (Instead of Application Secret as specified in the mock)! 
     return false; 
    } 
} 
+1

如果您需要调用bean然后用'@ testconfiguration'注释类并创建模拟服务,那么将'@ Before'的方法移动到'@ Before'的方法中,因为Spring测试不会调用'@ Bean',谢谢您的支持 – rajadilipkolli

+0

回答!这将适用于contextLoads测试中的设置对象(值为“应用程序密钥”)。但是对于自动装配设置的应用程序中的所有其他组件,将使用缺少方法定义的默认模拟对象。任何想法也注入嘲弄的对象? – swaechter

当你用注释一@MockBean场,春天将创建模仿注释类,并使用它来自动装载应用程序上下文的所有bean。

您必须创建嘲笑自己与

Settings settings = Mockito.mock(Settings.class); 

这将创建第二个模拟,导致了上述问题。

解决方案:

@MockBean 
private Settings settings; 

@Before 
public void before() { 
Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret"); 
} 

@Test 
public void contextLoads() { 
    String applicationsecret = settings.getApplicationSecret(); 
    System.out.println("Application secret: " + applicationsecret); 
} 
+0

可悲的是,这并没有解决我的问题,所以我只是更新我的问题,以更好的方式反映我的问题。 – swaechter

看起来你正在使用设置对象指定的嘲笑行为之前。 您必须在配置设置期间运行

Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret"); 

。为了防止您可以创建特殊配置类来进行测试。

@RunWith(SpringRunner.class) 
@SpringBootTest(classes = {MyApplication.class, MyApplicationTest.TestConfig.class}) 
public class MyApplicationTest { 

    private static final String SECRET = "Application Secret"; 

    @TestConfiguration 
    public static class TestConfig { 
     @Bean 
     @Primary 
     public Settings settingsBean(){ 
      Settings settings = Mockito.mock(Settings.class); 
      Mockito.when(settings.getApplicationSecret()).thenReturn(SECRET); 
      Mockito.doReturn(SECRET).when(settings).getApplicationSecret(); 
      return settings; 
     } 

    } 

..... 

} 

此外,我会建议你使用下一个符号来表示嘲讽:

Mockito.doReturn(SECRET).when(settings).getApplicationSecret(); 

它将无法运行设置:: getApplicationSecret

+1

非常感谢,解决了我的问题! – swaechter

您可以使用构造函数注入在你的测试:

@Test 
public void consumeService() { 
    AuthenticationService authenticationService = new AuthenticationServiceImpl(settings); 

    Boolean logedIn authenticationService.loginUser("token"); 

} 

这将ma确保使用模拟的实例。

+0

我很抱歉,但这并不能解决我的问题。我的问题是/被嘲笑的实例不被用作依赖注入中的对象。手动创建这些服务会使依赖注入类型过时。 – swaechter