3

我有一个运行良好的 Web 应用程序。现在我正在尝试为它编写单元测试。我的 webapp 有以下转换服务

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <list>
                <bean class="....Class1ToStringConverter"/>
                <bean class="....StringToClass1Converter"/>
            </list>
        </property>
</bean>
<mvc:annotation-driven  conversion-service="conversionService" />

哪个很好用,当我提出请求时

/somepath/{class1-object-string-representation}/xx 

一切都按预期工作(字符串被解释为 Class1 对象)。

我的问题是尝试向我的控制器编写单元测试。只是没有使用转换服务,春天只是告诉我

Cannot convert value of type [java.lang.String] to required type [Class1]: no matching editors or conversion strategy found

到目前为止我的测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/applicationContext.xml", "file:src/main/webapp/WEB-INF/jpm-servlet.xml"})
@WebAppConfiguration()
public class GeneralTest {

    @Autowired
    private WebApplicationContext ctx;
    private MockMvc mockMvc;
    private TestDAO testDAO = org.mockito.Mockito.mock(TestDAO.class);

    @Before
    public void setUp() throws Exception {
        Mockito.reset(testDAO);
        mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();
    }

@Test
public void testList() throws Exception {
    final Test first = new Test(1L, "Hi", 10, new Date(), true);
    final Test second = new Test(2L, "Bye", 50, new Date(), false);
    first.setTest(second);

    when(testDAO.list()).thenReturn(Arrays.asList(first, second));

    mockMvc.perform(get("/jpm/class1-id1"))
            .andExpect(status().isOk())
            .andExpect(view().name("list"))
            .andExpect(forwardedUrl("/WEB-INF/jsp/list.jsp"));
}

我缺少什么?谢谢

4

4 回答 4

5

模拟转换器像这样,

  GenericConversionService conversionService = new GenericConversionService();
  conversionService.addConverter(new StringToClass1Converter());



Deencapsulation.setField(FIXTURE, conversionService);
于 2015-10-22T04:44:55.973 回答
2

这篇文章有点过时了,但在搜索这个问题时仍然是谷歌的第一批结果之一。

所以这里是Spring Framework 5的更新。

当您像Spring Documentation中记录的那样配置 WebMvc 上下文时,您会编写如下内容:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
}

但是,这不是在 JUnit 上下文中加载的!天知道为什么。但是你需要像这样声明你的配置类:

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
}

@EnableWebMvc因此,如上所述,删除注释并从另一个类扩展。您可能不需要更改任何其他内容。然后你的addFormatters方法也会在你的单元测试中被调用。

这是非常不直观和奇怪的。但这是真的。您可以使用断点调试 spring-test 源代码,并查看WebMvcConfigurer接口的所有其他方法都被调用,但addFormatters不是。也许他们只是忘记了称呼它,或者他们有其他“原因”。通过WebMvcConfigurationSupport根据需要调用每个方法的扩展,您的 JUnit 测试会成功。

特别是,这段代码最终会被执行:

@Bean
public FormattingConversionService mvcConversionService() {
    FormattingConversionService conversionService = new DefaultFormattingConversionService();
    addFormatters(conversionService);
    return conversionService;
}

老实说,当只是按照 Spring 文档中的描述实现接口时,我不知道究竟是什么失败了。

于 2018-03-23T16:33:49.693 回答
0

我遇到过同样的问题。如果您尝试测试单个控制器,您可以尝试以下操作:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/applicationContext.xml", "file:src/main/webapp/WEB-INF/jpm-servlet.xml"})
@WebAppConfiguration()
public class GeneralTest {

  @Autowired
  private WebApplicationContext ctx;
  @Autowired
  private FormattingConversionServiceFactoryBean conversionService;

  private MockMvc mockMvc;

  @Mock
  private TestDAO testDAO;

  /* The following assumes you are injecting your DAO into your controller
   * If you are using a service layer (most likely), you should 
   * inject your DAO into your service and your service into your controller.
   */
  @InjectMocks
  private YourControllerClass controllerToTest;

  @Before
  public void setUp() throws Exception {
      MockitoAnnotations.initMocks(this);

      //get the conversion service from the factory bean
      FormattingConversionService cs = conversionService.getObject();
      Mockito.reset(testDAO);

      //setup MockMvc using the conversion service
      mockMvc = MockMvcBuilders.standaloneSetup(controllerToTest)
            .setConversionService(cs)
            .build();
  }

  @Test
  public void testList() throws Exception {
    final Test first = new Test(1L, "Hi", 10, new Date(), true);
    final Test second = new Test(2L, "Bye", 50, new Date(), false);
    first.setTest(second);

    when(testDAO.list()).thenReturn(Arrays.asList(first, second));

    mockMvc.perform(get("/jpm/class1-id1"))
            .andExpect(status().isOk())
            .andExpect(view().name("list"))
            .andExpect(forwardedUrl("/WEB-INF/jsp/list.jsp"));
}

希望有帮助!

于 2013-09-30T20:23:59.600 回答
0

我意识到这是一个旧线程,但像我一样,在花了几个小时排除为什么他们的自定义转换器没有被调用之后,将来会有其他人会偶然发现这个问题。

@Mariano D'Ascanio 提出的解决方案在根本没有控制器的情况下是不够的,至少不是您编写的控制器,例如当您使用 Spring JPA 时。MockMvcBuilders.standaloneSetup至少需要将一个控制器传递给构造函数,因此您不能在这种情况下使用它。解决这个问题的方法是注入org.springframework.core.convert.converter.ConverterRegistry或更好,它是子类org.springframework.core.convert.converter.FormatterRegistry,然后在一个@PostContruct方法中,注册您的自定义转换器/格式化程序,如下所示:

@PostConstruct
void init() {
    formatterRegistry.removeConvertible(String.class, OffsetDateTime.class);

    formatterRegistry.addFormatter(customOffsetDateTimeFormatter);
    formatterRegistry.addConverter(customOffsetDateTimeConverter);
}

诀窍是ConverterRegistry使用名称而不是类型来注入,因为对于 Web 测试,有两个转换器注册表,默认和 mvc。Spring 测试使用默认转换器,所以这是您需要的。

// GOTCHA ALERT: There's also a mvcConversionService; tests DO NOT use that
@Resource(name = "defaultConversionService")
private FormatterRegistry formatterRegistry;

希望这可以帮助。

于 2015-10-22T04:38:26.030 回答