2013-04-17 40 views
3

由于我是Spring Test MVC的新手,我不明白这个问题。我把下面的代码从http://markchensblog.blogspot.in/search/label/SpringMockito在使用@Mock时将空值注入到Spring bean中?

变量mockproductService不是从应用程序上下文注入,它同时使用@Mock注释并获得assetion错误包含null值。

断言错误我目前遇到的情况如下:

java.lang.AssertionError: Model attribute 'Products' expected:<[[email protected], [email protected]]> but was:<[]> 
    at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:60) 
    at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:89) 
    at org.springframework.test.web.servlet.result.ModelResultMatchers$2.match(ModelResultMatchers.java:68) 
    at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:141) 
    at com.pointel.spring.test.ProductControllerTest.testMethod(ProductControllerTest.java:84) 

注:如果我使用@Autowired代替@Mock这是工作的罚款。

测试控制器类

RunWith(SpringJUnit4ClassRunner.class) 
@WebAppConfiguration 
@ContextConfiguration(locations={"classpath:mvc-dispatcher-servlet.xml"}) 
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class}) 
public class ProductControllerTest { 

    @Autowired 
    private WebApplicationContext wac; 

    private MockMvc mockMvc; 

    @InjectMocks 
    private ProductController productController; 

    @Mock 
    //@Autowired 
    private ProductService mockproductService; 


    @Before 
    public void setup() { 

    MockitoAnnotations.initMocks(this); 

    List<Product> products = new ArrayList<Product>(); 
    Product product1 = new Product(); 
    product1.setId(new Long(1)); 

    Product product2 = new Product(); 
    product2.setId(new Long(2)); 

    products.add(product1); 
    products.add(product2); 

    Mockito.when(mockproductService.findAllProducts()).thenReturn(products); 

    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); 

    } 

    @Test 
    public void testMethod() throws Exception { 

    List<Product> products = new ArrayList<Product>(); 

    Product product1 = new Product(); 
    product1.setId(new Long(1)); 

    Product product2 = new Product(); 
    product2.setId(new Long(2)); 

    products.add(product1); 
    products.add(product2); 

    RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/products"); 

    this.mockMvc.perform(requestBuilder). 
     andExpect(MockMvcResultMatchers.status().isOk()) 
     .andExpect(MockMvcResultMatchers.model().attribute("Products", products)) 
      //.andExpect(MockMvcResultMatchers.model().size(2)) 
     .andExpect(MockMvcResultMatchers.view().name("show_products")); 


    } 
} 

Controller类

@Controller 
public class ProductController { 

    @Autowired 
    private ProductService productService; 

    @RequestMapping("/products") 
    public String testController(ModelMap model){ 
     model.addAttribute("Products",productService.findAllProducts()); 
     return "show_products"; 
    } 
} 

WebServletContext MVC-调度-servlet.xml中

<bean id="someDependencyMock" class="org.mockito.Mockito" factory-method="mock"> 
    <constructor-arg value="com.pointel.spring.test.ProductService" /> 
</bean> 
    <context:component-scan base-package="com.pointel.spring.test" /> 

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" >  
    <property name="prefix" value="/WEB-INF/jsp/" /> 
    <property name="suffix" value=".jsp" /> 
</bean> 
+0

见你的答案http://stackoverflow.com/questions/2457239/injecting-mockito-mocks-into-a-spring-bean – Vadzim

回答

7

对于我来说,目前还不清楚春秋的的Mockito的组合您从引用的博客来源获取它应该可以按照预期工作。至少我可以解释你的观察:

  • 你的测试(this.mockMvc.perform())正在处理由Spring创建的web应用程序上下文。在此情况下,ProductController由Spring实例化(context:component-scan)。 productService随后自动装配,您在mvc-dispatcher-servlet.xml中创建的Mockito模拟为someDependencyMock
  • 如果您通过@Autowired,注入mockproductService弹簧从其上下文中注入someDependencyMock实例。因此,您的Mockito.when()调用在此实例上正常工作,如前所述,该实例已正确连接到ProductController
  • 但是,如果你注入mockproductService通过@Mock的Mockito注入的ProductService,Spring上下文的不是一个实例,因为它知道任何关于春天的。因此,您的Mockito.when()调用不会在Spring自动装配的模拟上运行,因此someDependencyMock保持未初始化状态。

那么还剩下什么不清楚的我关于博客的原始代码是如何工作的根本是:

  • productController财产与@InjectMocks注释将被的Mockito,甚至正确连线在初始化为mockproductService测试类。但是Spring对这个对象一无所知,并且不会在this.mockMvc.perform()调用中使用它。因此,我假设您只在@Autowired中注入mockproductService,即使您在测试课程中同时删除productController属性和MockitoAnnotations.initMocks()调用,您的测试也会按预期工作。
2

我没有看过你提到的教程,因为你提供的代码足够说明原作者的专业性或缺乏它。

测试的一般规则是,您不要混合不同类型的测试。第一级的测试是单元测试这意味着你正在测试单一工作单元(通常是一个班级)。一旦单元测试通过,您可以编写集成测试,它们结合了某些组件(类)并测试它们如何一起工作。

一个类很少依赖于任何东西,所以创建一个真正的单元测试,你需要模拟所有的依赖关系

@RunWith(MockitoJUnitRunner.class) 
public class ProductControllerTest { 
    @Mock private ProductService mockproductService; 
    @InjectMocks private ProductController productController; 

    @Test 
    public void testMethod() throws Exception { 
     // Prepare sample test data. 
     final Product product1 = Mockito.mock(Product.class); 
     final Product product2 = Mockito.mock(Product.class); 
     final ArrayList<Product> products = new ArrayList<Product>(); 
     products.add(product1); 
     products.add(product2); 
     final ModelMap mmap = Mockito.mock(ModelMap.class); 

     // Configure the mocked objects. 
     Mockito.when(product1.getId()).thenReturn(new Long(1)); 
     Mockito.when(product2.getId()).thenReturn(new Long(2)); 
     Mockito.when(mockproductService.findAllProducts()).thenReturn(products); 
     final mmap = Mockito.mock(ModelMap.class); 

     // Call the method under test. 
     final String returned = productController.testController(mmap); 

     // Check if everything went as planned. 
     Mockito.verify(mmap).addAttribute("Products", products); 
     assertNotNull(returned); 
     assertEquals("show_products", returned); 
    } 
} 

这就是单元测试的样子。首先你准备数据(对象) - 注意他们都被嘲笑。此外,使用final可防止意外分配,即意外覆盖现有值。

其次,你配置每个模拟对象的行为。如果Product将被要求输入ID,那么您可以指定在这种情况下模拟实例将返回的内容。 BTW我真的没有看到这些设置产品ID的目的,所以测试的第一部分看起来是这样的:

 final Product product1 = Mockito.mock(Product.class); 
     final Product product2 = Mockito.mock(Product.class); 
     final ArrayList<Product> products = new ArrayList<Product>(); 
     products.add(product1); 
     products.add(product2); 
     final mmap = Mockito.mock(ModelMap.class); 

     // Configure the mocked objects. 
     Mockito.when(mockproductService.findAllProducts()).thenReturn(products); 
     final mmap = Mockito.mock(ModelMap.class); 

三,拨打测试的方法和存储其结果是:

 final String returned = productController.testController(mmap); 

最后你检查被测试的类是否按预期行事。在这种情况下,ModelMapaddAttribute()方法应该使用那些确切的参数值进行调用。并且返回的字符串不应该是null,而应该是"show_products" - 请注意参数顺序为assertEquals(expected, actual)方法,因为在测试失败的情况下,JUnit将打印出一条消息,指出“期望这样做但是得到了这个。”。

 Mockito.verify(mmap).addAttribute("Products", products); 
     assertNotNull(returned); 
     assertEquals("show_products", returned); 

祝你好运!

P.S. 我忘了解释开头:

@RunWith(MockitoJUnitRunner.class) 
    public class ProductControllerTest { 
     @Mock private ProductService mockproductService; 
     @InjectMocks private ProductController productController; 

为了使@InjectMocks工作就像Spring的@Autowired测试必须MockitoJUnitRunner类跑出 - 它会找出所有@Mock成员,创建它们并注入是正确的进会员标记为@InjectMocks

+0

+1 ..很好的例子。 –

1

我认为@Cebence提供的答案存在问题,因为它没有考虑到OP对spring-webmvc-test @WebApplication的使用。如果你是在运行提供

@RunWith(MockitoJUnitRunner.class) 

的例子,你还有你的

@Autowired private WebApplicationContext wac; 

然后会导致测试失败。我遇到了与@Human Being相同的问题,我发现一个简单的解决方案是将控制器中的服务设置为不需要。不理想,但这里的解决方案:

控制器:

@Controller 
public class MyController 
{ 
    @Autowired(required=false) 
    MyService myService; 
    . 
    . 
    . 
} 

测试:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations="classpath:/META-INF/spring/applicationContext-test.xml") 
@WebAppConfiguration 
public class MyControllerTest 
{ 
    // This is the backend service we are going to mock 
    @Mock 
    MyService myService; 

    // This is the component we are testing and we inject our mocked 
    // objects into it 
    @InjectMocks 
    @Resource 
    private MyController myController; 

    @Autowired 
    private WebApplicationContext wac; 

    private MockMvc mockMvc; 


    @Before 
    public void setup() 
    { 
     MockitoAnnotations.initMocks(this); 
     this.mockMvc = webAppContextSetup(this.wac).build(); 

     List<Object> data = new ArrayList<Object>(); 

     // Mock one of the service mthods 
     when(myService.getAll()).thenReturn(datasets); 
    } 

    @Test 
    public void testQuery() throws Exception 
    { 
     this.mockMvc.perform(get("/data").accept(MediaType.APPLICATION_JSON)) 
       .andExpect(status().isOk()) 
       .andExpect(content().contentType("application/json;charset=UTF-8")) 
       .andExpect(jsonPath("$.value").value("Hello")); 
    } 

} 

和应用程序上下文:

<?xml version="1.0" encoding="UTF-8" standalone="no"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:neo4j="http://www.springframework.org/schema/data/neo4j" 
xmlns:mvc="http://www.springframework.org/schema/mvc" 
xsi:schemaLocation=" 
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd 
http://www.springframework.org/schema/data/neo4j 
http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd 
http://www.springframework.org/schema/mvc 
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd 
"> 



    <mvc:annotation-driven/> 
    <context:annotation-config/> 
    <context:component-scan base-package="com.me.controller" /> 

</beans> 
+0

'MockitoAnnotations.initMocks(this);'是调用'@InjectMocks'的关键。 – Arpit

+0

是的。实质上,任何标有'@ Mock'或'@InjectMocks'的字段都不会被初始化,除非您调用它。总是最好在'@ Before'注释的方法中完成 – Sparm

相关问题