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

12

我有一个Spring应用程序和与此应用程序相关的集成测试。我想用模拟bean替换一个bean。

我的真实bean长这样:

@Service
public class MyService {

}

并且为了测试,我希望它被替换掉

@Service
public class TestMyService {

}

我所能想到的是为不同的服务使用不同的配置文件。例如:

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

}

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

}

然后我像这样自动装配bean

@Autowired
private IMyService myService;

有更好的方法吗?


你是否曾经得到过令人满意的答案?我正在从Springockito迁移,它有一个名为ReplaceWithMock的注解处理程序,可以完全实现您所描述的功能。我还没有找到类似的东西。 - John Camerin
困惑了... - AlikElzin-kilaka
4个回答

7

6
不会替换原有的bean,只是创建一个相同类型的模拟bean并注入它。然而,在某些配置中,这可能会引发重复bean异常。 - simar
4
这是一个糟糕的回答。 - KitKarson
新链接:https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing.spring-boot-applications.mocking-beans - Eugen Labun
1
@KitKarson 如果你只是简单地评论“这个回答很糟糕”,却没有提供任何进一步的解释,那么你干脆就不要评论了。 - Matías Santurio

6

我个人偏好避免为测试加载完整的上下文。因此我喜欢我的测试侧重于一小部分bean。这通常意味着我在测试中列出我使用的bean:

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

}

如果需要更多的配置,我倾向于为测试准备一个单独的配置类:
@RunWith(SpringRunner.class)
@SpringBootTest(
        classes = MyTest.Cfg.class
)
public class MyTest {

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

    }

}

当需要更多的配置(或模拟)时,我使用测试配置来提供适当的模拟

@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;
        }

    }

}

@Import部分,你最终会得到10个、20个还是30个bean呢? - Sergei Ledvanov
我有一个应用程序(一种模块/包)的多个配置,其中我主要配置外部依赖项(适配器、遵循端口和适配器架构)。这有助于我进行封装。查看代码时,我在单个测试中看不到超过6-8个导入。 - Jakub Marchwicki
不错!我一直在尝试使用@Spybean,但无论我如何使用它,它都无法正常工作...谢谢! - funder7

0
对于Spring Boot,如Grzegorz Poznachowski的回答所述,@MockBean(和@SpyBean)可能是您最好的选择。
对于非Spring Boot应用程序,另一种选择是使用@Primary注释模拟/存根@Bean/@Service定义。该注释“表示当多个候选项有资格自动装配单值依赖项时,应优先考虑给定bean。”因此,在连接其他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);
        }
    }
}

0

你可以为你的Bean命名,例如:

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

}

在你的测试类中,你可以使用@Qualifier显式地请求测试服务,例如:

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

2
IMyService是您业务代码中的下游依赖项(适配器)时,这种方法不起作用。 @Qualifier允许您在测试类中注入bean,但它非常有限。 - Jakub Marchwicki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接