简体   繁体   English

如何在 Spring Boot 中用测试模拟替换 bean 以进行集成测试

[英]How to replace a bean with a testing mock in Spring Boot for integration testing

I have a spring app and integration tests for this app.我有一个 spring 应用程序和此应用程序的集成测试。 I would like to replace a bean with a mock bean.我想用模拟 bean 替换 bean。

My real bean looks like this我真正的豆子是这样的

@Service
public class MyService {

}

and for testing I would like it to be replaced并且为了测试我希望它被替换

@Service
public class TestMyService {

}

All I can think of is to use profiles for different services.我能想到的就是为不同的服务使用配置文件。 For example:例如:

@Service
@Profile("!test")
public class MyService implements IMyService {

}

@Service
@Profile("test")
public class TestMyService implements IMyService {

}

And then I autowire the bean like this然后我像这样自动装配bean

@Autowired
private IMyService myService;

Is there a better way?有没有更好的办法?

My personal preference is to avoid loading the compete context for testing. 我个人的喜好是避免加载竞争环境进行测试。 Therefore I like my test to focus on a subset of beans. 因此,我希望我的测试专注于豆子集。 It usually means I outline beans which I use in tests: 这通常意味着我概述了在测试中使用的bean:

@RunWith(SpringRunner.class)
@SpringBootTest(
        classes = {TestMyService.class, OtherClassNeededForTesting.class}
)
public class DelaysProviderTest {

}

If more configuration is needed I tend to prepare a separate configuration class for tests: 如果需要更多配置,我倾向于准备单独的配置类以进行测试:

@RunWith(SpringRunner.class)
@SpringBootTest(
        classes = MyTest.Cfg.class
)
public class MyTest {

    @Import({
        // .. some classes to import including TestMyService.class
    })
    @Configuration
    public static class Cfg {

    }

}

When even more configuration is needed (or mocking), I use the test configuration for providing appropriate mocks 当需要更多配置(或模拟)时,我使用测试配置来提供适当的模拟

@RunWith(SpringRunner.class)
@SpringBootTest(
        classes = MyTest.Cfg.class
)
public class MyTest {

    @Import({
        // .. some classes to import
    })
    @Configuration
    public static class Cfg {

        @Bean
        public IMyService service() {
            IMyService mock = Mockito.mock(IMyService.class);
            when(mock.someMethod()).thenReturn("some data");

            return mock;
        }

    }

}

You can name your beans, in your case something like 您可以命名您的豆子,例如

@Service("testService")    
public class TestMyService implements IMyService {

}

and in your test class, you can explicitely ask for test service using @Qualifier , like 在您的测试课程中,您可以使用@Qualifier明确要求测试服务,例如

@Qualifier("testService")
@Autowired
private IMyService myService;

Spring Boot has @MockBean and @SpyBean annotations exactly for this purpose: Spring Boot正是为此目的提供了@MockBean@SpyBean批注:

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-mocking-beans https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-mocking-beans

Declaration is simple: 声明很简单:

@MockBean
private MyService myService;

Spring Boot will inject Mockito mock there instead of the actual bean. Spring Boot将在此处注入Mockito模拟,而不是实际的bean。

For Spring Boot, @MockBean (and @SpyBean ) are likely your best bets as noted in Grzegorz Poznachowski's answer.对于 Spring 引导, @MockBean (和@SpyBean )可能是您在 Grzegorz Poznachowski 的回答中指出的最佳选择。

For non Spring Boot Applications , an alternative is to use @Primary to annotate the mock/stub @Bean / @Service definition.对于非 Spring 引导应用程序,另一种方法是使用@Primary来注释模拟/存根@Bean / @Service定义。 That annotation "indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency."该注释“表明当多个候选者有资格自动装配单值依赖项时,应优先考虑 bean”。 As such, your mock will be used over the "real" one when wiring other Beans/Configurations.因此,在连接其他 Bean/配置时,您的模拟将用于“真实”模拟。

@SpringJUnitWebConfig(MyAppConfig.class)
public class MyUintTest 
{
    @Autowired
    private ServiceInterface serviceMock;

    @Autowired
    private SomeClass someClassMock;

    @Test
    public void myTest() 
    {
        when(someClassMock.getProperty()).thenReturn(properties.get());
        // . . .
    }

    @Service
    @Primary
    static class TestService implements ServiceInterface 
    {
        // . . .
    }

    @Configuration
    static class Config 
    {
        @Bean
        @Primary
        public SomeClass someClassBean() 
        {
            return Mockito.mock(SomeClass.class);
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM