【问题标题】:Error Unit Testing of Spring MVC Controllers calling REST API调用 REST API 的 Spring MVC 控制器的错误单元测试
【发布时间】:2016-01-31 06:05:57
【问题描述】:

我在我的小应用程序中对 Spring MVC 控制器进行单元测试,但出现以下错误:

INFO: **Initializing Spring FrameworkServlet ''**
Oct 30, 2015 5:37:38 PM org.springframework.test.web.servlet.TestDispatcherServlet initServletBean
INFO: FrameworkServlet '': initialization started
Oct 30, 2015 5:37:38 PM org.springframework.test.web.servlet.TestDispatcherServlet initServletBean
INFO: FrameworkServlet '': initialization completed in 2 ms
Running com.sky.testmvc.product.controller.ProductSelectionControllerTest
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.031 sec <<< FAILURE!
testRoot(com.sky.testmvc.product.controller.ProductSelectionControllerTest)  Time elapsed: 0.031 sec  <<< FAILURE!
*java.lang.AssertionError: **Status expected:<200> but was:<204>***
    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.StatusResultMatchers$5.match(StatusResultMatchers.java:549)
    at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:141)

其余控制器如下:

 @RestController
    public class ItemRestController {   

        @Autowired
        ItemService catalogueService;   
        @Autowired
        LocationService locationService;   

        @RequestMapping(value = "/product/{customerId}", produces = {MediaType.APPLICATION_JSON_VALUE}, method = RequestMethod.GET)
        public ResponseEntity<List<Item>> listCustomerProducts(@PathVariable("customerId") int customerId) {

            Integer customerLocation=(Integer) locationService.findLocationByCustomerId(customerId);
            List<Product> products = catalogueService.findCustomerProducts(customerLocation);

            if(products.isEmpty()){
                return new ResponseEntity<List<Product>>(HttpStatus.NO_CONTENT);
            }
            return new ResponseEntity<List<Product>>(products, HttpStatus.OK);
        }   
    }



  @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration
    @ContextConfiguration(classes = {TestContext.class, AppConfiguration.class}) //load your mvc config classes here

    public class ItemControllerTest {

        private MockMvc mockMvc;
        @Autowired
        private ItemService productServiceMock;
        @Autowired
        private WebApplicationContext webApplicationContext;

        @Before
        public void setUp() {
            Mockito.reset(productServiceMock);
            mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
        }



  @Test
public void testRoot() throws Exception 
{      

    Product prod1= new Product(1L,"Orange", new ProductCategory(1,"Sports"),new CustomerLocation(2,"London"));
    Product prod2= new Product(2L,"Banana", new ProductCategory(1,"Sports"),new CustomerLocation(2,"London"));

    when(productServiceMock.findCustomerProducts(1L)).thenReturn(Arrays.asList(prod1, prod2));

    mockMvc.perform(get("/product/{id}", 1L))
    .andExpect(status().isOk())
    .andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8))
    .andExpect(jsonPath("$", hasSize(2)))
            .andExpect(jsonPath("$[0].productId", is(1)))
            .andExpect(jsonPath("$[0].productName", is("Orange")))
            .andExpect(jsonPath("$[1].productId", is(2)))
            .andExpect(jsonPath("$[1].productName", is("Banana")));

    verify(productServiceMock, times(1)).findCustomerProducts(1L);
    verifyNoMoreInteractions(productServiceMock);       
}

    }

请参阅下面的 TestContext:

 @Configuration
    public class TestContext {

        private static final String MESSAGE_SOURCE_BASE_NAME = "i18n/messages";

        @Bean
        public MessageSource messageSource() {
            ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();

            messageSource.setBasename(MESSAGE_SOURCE_BASE_NAME);
            messageSource.setUseCodeAsDefaultMessage(true);

            return messageSource;
        }

        @Bean
        public ItemService catalogueService() {
            return Mockito.mock(ItemService .class);
        }
    }


 @Configuration
    @EnableWebMvc
    @ComponentScan(basePackages = "com.uk.springmvc")
    public class AppConfiguration extends WebMvcConfigurerAdapter{

        private static final String VIEW_RESOLVER_PREFIX = "/WEB-INF/views/";
        private static final String VIEW_RESOLVER_SUFFIX = ".jsp";


        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
            viewResolver.setViewClass(JstlView.class);
            viewResolver.setPrefix(VIEW_RESOLVER_PREFIX);
            viewResolver.setSuffix(VIEW_RESOLVER_SUFFIX);
            registry.viewResolver(viewResolver);
        }

        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/static/**").addResourceLocations("/static/");
        }

        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }

        @Bean
        public SimpleMappingExceptionResolver exceptionResolver() {
            SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();

            Properties exceptionMappings = new Properties();

            exceptionMappings.put("java.lang.Exception", "error/error");
            exceptionMappings.put("java.lang.RuntimeException", "error/error");

            exceptionResolver.setExceptionMappings(exceptionMappings);

            Properties statusCodes = new Properties();

            statusCodes.put("error/404", "404");
            statusCodes.put("error/error", "500");

            exceptionResolver.setStatusCodes(statusCodes);

            return exceptionResolver;
        }


    }

如前所述,应用程序工作正常,我可以读取通过浏览器返回的 json 并将其显示在我的 angularjs 视图中,但单元测试不起作用。错误是 java.lang.AssertionError:预期状态: 但原为:。 204 - 响应为空... 可能是 > Initializing Spring FrameworkServlet '' 即框架 servlet 未初始化?不确定。 任何帮助将不胜感激

我的休息控制器

@RestController 公共类 ItemRestController {

@Autowired
ItemService catalogueService;  //Service which will do product retrieval

@Autowired
LocationService locationService;  //Service which will retrieve customer location id using customer id

//-------------------Retrieve All Customer Products--------------------------------------------------------

@RequestMapping(value = "/product/{customerId}", produces = {MediaType.APPLICATION_JSON_VALUE}, method = RequestMethod.GET)
public ResponseEntity<List<Product>> listCustomerProducts(@PathVariable("customerId") int customerId) {

    Integer customerLocation=(Integer) locationService.findLocationByCustomerId(customerId);
    List<Product> products = catalogueService.findCustomerProducts(customerLocation);

    if(products.isEmpty()){
        return new ResponseEntity<List<Product>>(HttpStatus.NO_CONTENT);
    }
    return new ResponseEntity<List<Product>>(products, HttpStatus.OK);
}

}

【问题讨论】:

  • 你测试的控制器长什么样?
  • 嗨 Kejml,我已经用控制器更新了原始帖子。谢谢
  • 嗯,这可能会导致很长的问题。显然,您的目录 CatalogueService 返回空结果。服务从哪里获取数据?某种数据库?典型的设置是,普通上下文使用与测试上下文不同的数据库(HSQL 是测试的流行选择),因此您必须确保测试数据库包含一些测试数据。这是在数据库启动/打开连接时或在测试的 @Before/@BeforeClass 注释方法中完成的
  • 或者你可以模拟服务,这取决于你是进行单元测试还是集成测试......
  • 我已经更新了测试类中的 testRoot()。响应是从硬编码列表中填充的。我已经尝试过很多次并忽略了它,我的错。但是,它仍然给了我一个 204。

标签: spring spring-mvc spring-restcontroller spring-mvc-test


【解决方案1】:

正如我们从堆栈跟踪中看到的那样,由于断言错误,测试失败:它需要 HTTP 状态 200,但得到 204,即 HTTPStatus.NO_CONTENT,当列表为空。

        if(products.isEmpty()){
            return new ResponseEntity<List<Product>>(HttpStatus.NO_CONTENT);
        }

如果你想让你的控制器返回 200,你需要你的 CatalogueService 模拟返回一些东西......

【讨论】:

  • 我已经更新了测试类中的 testRoot()。响应是从硬编码列表中填充的。我已经尝试过很多次并忽略了它,我的错。但是,它仍然给了我一个 204。
  • 问题是,products 是空的,如果是,为什么...这就是调试器的用途...
【解决方案2】:

在您的测试中,您正在模拟 ItemService(使用 productServiceMock),但您的控制器正在调用 CatalogueService 的实例

还要确保你的模拟被正确注入,调试器真的是你最好的朋友。我猜从你的语法来看,你正在使用 Mockito。在这种情况下,模拟对象在其getClass().getName() 方法中具有“$$EnhancedByMockito”

【讨论】:

  • 它的ItemService一路。在试图保持谨慎时,这只是一个错字!对不起!
  • 模拟注入了吗?正如我所说,调试器可以告诉你......或者使用普通的旧System.out.println()
  • ItemControllerTest 中的以下行填充模拟 >> when(productServiceMock.findCustomerProducts(1L)).thenReturn(Arrays.asList(prod1, prod2));我如何测试是否注入了模拟。我是模拟测试的新手,因此提出了一些愚蠢的问题。
  • 不不,我的意思是在测试期间在控制器中。您可以在控制器中放置断点并检查 CatalogueService 实例的类名吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-25
  • 2016-02-08
  • 2018-07-31
  • 2023-03-15
  • 2022-12-05
  • 2015-07-16
相关资源
最近更新 更多