【问题标题】:Spring Boot Integration Testing with mocked Services/Components使用模拟服务/组件进行 Spring Boot 集成测试
【发布时间】:2017-03-07 06:42:59
【问题描述】:

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

背景是,在下面的代码中,设置的实现依赖于第三方服务器和其他外部系统。 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;
    }
}

【问题讨论】:

  • 将你拥有的东西移到@Before的方法中,因为spring测试不会调用@Bean,如果你需要调用你的bean,那么用@testconfiguration注释类并创建模拟服务
  • 感谢您的回答!这将适用于 contextLoads 测试中的设置对象(值为“应用程序机密”)。但是对于应用程序中自动装配设置的所有其他组件,使用没有方法定义的默认模拟对象。有什么想法也可以在那里注入模拟对象吗?

标签: java spring-boot junit4 spring-test


【解决方案1】:

看起来您在指定其模拟行为之前正在使用 Settings 对象。 你必须运行

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();

它不会运行 settings::getApplicationSecret

【讨论】:

  • 我刚刚意识到我从未接受过这个答案。谢谢!
  • 这种方法很棒。救了我的命!
【解决方案2】:

当您使用 @MockBean 注释字段时,spring 将创建一个已注释类的模拟,并使用它来自动装配应用程序上下文的所有 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);
}

【讨论】:

  • 很遗憾这并不能解决我的问题,所以我刚刚更新了我的问题以更好地反映我的问题。
猜你喜欢
  • 2015-08-02
  • 2018-02-01
  • 2015-06-15
  • 2020-07-24
  • 1970-01-01
  • 1970-01-01
  • 2015-10-03
  • 2014-08-15
  • 2020-04-09
相关资源
最近更新 更多