2015-02-23 53 views
0

我有以下成立:喷吹的Mockito嘲笑成通过工厂方法返回豆

需要测试期间被模拟的样品类:

@Component 
class MyConfig 
{ 
    public String getConfig() 
    { 
     return "RealValue"; 
    } 
} 

其定义了一个方法,并且具有一个接口两种实现:

interface MyInterface 
{ 
    String myMethod(); 
} 

class MyImpl1 implements MyInterface 
{ 
    private final MyInterface delegate; 
    private final MyConfig config; 

    public MyImpl1(final MyInterface delegate, final MyConfig config) 
    { 
     this.delegate = delegate; 
     this.config = config; 
    } 

    @Override 
    public String myMethod() 
    { 
     return this.getClass().getSimpleName() + ": " + config.getConfig() + ", " + delegate.myMethod() + ";"; 
    } 
} 

class MyImpl2 implements MyInterface 
{ 
    private final MyConfig config; 

    public MyImpl2(final MyConfig config) 
    { 
     this.config = config; 
    } 

    @Override 
    public String myMethod() 
    { 
     return this.getClass().getSimpleName() + ": " + config.getConfig(); 
    } 
} 

甲工厂类,准备使用MyConfig对象,它是一个SPRI MyInterface类型的豆纳克豆注入MyFactory

@Component 
class MyFactory 
{ 
    @Autowired 
    private MyConfig config; 

    // Factory method to create the bean. 
    @Bean(name = "myInterface") 
    protected MyInterface myInterface() 
    { 
     final MyImpl2 myImpl2 = new MyImpl2(config); 
     final MyImpl1 myImpl1 = new MyImpl1(myImpl2, config); 
     return myImpl1; 
    } 

    // A simple getter that prepares MyInterface on the fly. 
    // This is just to demonstrate that getter picks the mock where as 
    // the factory bean doesn't 
    public MyInterface getInterface() 
    { 
     final MyImpl2 myImpl2 = new MyImpl2(config); 
     final MyImpl1 myImpl1 = new MyImpl1(myImpl2, config); 
     return myImpl1; 
    } 
} 

一个简单的测试的情况下,检查是否的MyConfig模拟版本越来越到bean使用创建@Bean与否:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = { 
    "classpath:application-context.xml" 
}) 
public class SpringBeanMockExampleTest 
{ 
    @Mock 
    private MyConfig config; 

    @InjectMocks 
    @Autowired 
    protected MyFactory factory; 

    @Resource 
    private MyInterface myInterface; 

    @Before 
    public void setupMocks() 
    { 
     MockitoAnnotations.initMocks(this); 
    } 

    /** 
    * Fails as the return value is "MyImpl1: RealValue, MyImpl2: RealValue;" 
    */ 
    @Test 
    public void testBean() 
    { 
     Mockito.when(config.getConfig()).thenReturn("MockValue"); 
     Assert.assertEquals("MyImpl1: MockValue, MyImpl2: MockValue;", myInterface.myMethod()); 
    } 

    /** 
    * Assertion passes here.  
    */ 
    @Test 
    public void testGetter() 
    { 
     Mockito.when(config.getConfig()).thenReturn("MockValue"); 
     Assert.assertEquals("MyImpl1: MockValue, MyImpl2: MockValue;", factory.getInterface().myMethod()); 
    } 
} 

我预期testBean方法传递此外,但显然该模拟没有被注入到在MyFactory中创建的工厂bean中。

在工厂bean创建步骤完成后,似乎mocks正在替换实际的bean。由于这个原因,工厂bean中的引用不会被模拟更新。

我该如何解决这个问题,以便testBean按预期工作?

+0

能否请您尝试与SpringBeanMockExampleTest#setUpMocks用模拟对象添加调用setConfig到MyFactory和设置作为factory.setConfig(config)//你的Mock对象。 – erhun 2015-02-23 08:57:54

+0

@erhun,那仍然没有工作。在工厂bean创建步骤之后,似乎mock正在替换实际的bean。由于这个原因,工厂bean中的引用不会被mock更新。 – Vikdor 2015-02-23 10:13:38

回答

1

这是行不通的。

您的Spring上下文首先被初始化。之后,TestExecutionListener被执行,处理测试中的依赖注入(例如@Autowired)。

然后,在每次测试运行之前,您的@Before方法将初始化SpringBeanMockExampleTest测试实例中的Mockito模拟,从而有效地覆盖自动布线的Spring依赖关系。

为什么? Mockito用@InjectMocks,@Mock,@Spy@Captor注释的所有属性创建一个新实例。

可能的解决方案是在工厂手动设置模拟配置,而不是使用@InjectMocks,覆盖Spring配置bean。

@Before 
public void setupMocks(){ 
    Config config = mock(Config.class); 
    factory.setConfig(config);  
} 

请注意,有(春季)结合嘲弄集成测试是不好的做法,因为嘲讽应该只在单元测试来完成。

一个更好的设置将使用配置文件,是建立在你的Spring上下文独立Config豆如:

@Profile("testing") 
@Component 
public class TestConfig implements Config { 
    public String getConfig(){ 
     return "testValue";  
    } 
}