Java 转换服务在单元测试中不工作(spring 3.2)

Java 转换服务在单元测试中不工作(spring 3.2),java,spring,spring-mvc,junit4,mockito,Java,Spring,Spring Mvc,Junit4,Mockito,我有一个网络应用程序运行良好。现在我正试图为它编写单元测试。我的webapp具有以下转换服务 <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <list> <bean c

我有一个网络应用程序运行良好。现在我正试图为它编写单元测试。我的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" />
一切正常,字符串被解释为Class1对象

我的问题是试图将单元测试写入控制器。转换服务没有被使用,spring告诉我

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"));
}

我错过了什么?谢谢,我也有同样的问题。如果要测试单个控制器,可以尝试以下操作:

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

希望有帮助

我也有同样的问题。如果要测试单个控制器,可以尝试以下操作:

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

希望有帮助

我意识到这是一个古老的线程,但与我一样,在花了几个小时排除自定义转换器为何未被调用的原因后,将来还会有其他人遇到这个问题

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

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

    formatterRegistry.addFormatter(customOffsetDateTimeFormatter);
    formatterRegistry.addConverter(customOffsetDateTimeConverter);
}
诀窍是使用名称而不是类型注入ConverterRegistry,因为对于web测试,有两个转换器注册表:default和mvc。Spring测试使用默认的转换器,因此这就是您需要的转换器

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

希望这能有所帮助。

我意识到这是一个旧的线程,但和我一样,在花了几个小时排除自定义转换器为何未被调用后,将来还会有其他人遇到这个问题

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

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

    formatterRegistry.addFormatter(customOffsetDateTimeFormatter);
    formatterRegistry.addConverter(customOffsetDateTimeConverter);
}
诀窍是使用名称而不是类型注入ConverterRegistry,因为对于web测试,有两个转换器注册表:default和mvc。Spring测试使用默认的转换器,因此这就是您需要的转换器

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

希望这有帮助。

像这样模拟转换器

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



Deencapsulation.setField(FIXTURE, conversionService);

像这样的模拟转换器

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



Deencapsulation.setField(FIXTURE, conversionService);

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

下面是SpringFramework5的更新

当您配置WebMvc上下文(如中所述)时,您可以编写如下内容:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
}
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
}
但是,这不是在JUnit上下文中加载的!天知道为什么。但您需要像这样声明您的配置类:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
}
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
}
因此,删除@EnableWebMvc注释并如上所述从另一个类进行扩展。你可能不需要改变其他任何事情。 然后在单元测试中也会调用addFormatters方法

这是非常不直观和奇怪的。但这是真的。您可以使用断点调试spring测试源代码,并查看是否调用了WebMVCConfiguer接口的所有其他方法,但没有调用addFormatters。也许他们只是忘记打电话了,或者他们有其他原因。通过从WebMvcConfigurationSupport扩展,可以根据需要调用每个方法,并且JUnit测试成功

特别是,此代码最终会执行:

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

老实说,我不知道在实现Spring文档中描述的界面时,到底是什么失败了。

这篇文章有点过时,但在搜索这个问题时,仍然是第一个Google结果之一

下面是SpringFramework5的更新

当您配置WebMvc上下文(如中所述)时,您可以编写如下内容:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
}
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
}
但是,这不是在JUnit上下文中加载的!天知道为什么。但您需要像这样声明您的配置类:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
}
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ...
    }
}
因此,删除@EnableWebMvc注释并如上所述从另一个类进行扩展。你可能不需要改变其他任何事情。 然后在单元测试中也会调用addFormatters方法

这是非常不直观和奇怪的。但这是真的。您可以使用断点调试spring测试源代码,并查看是否调用了WebMVCConfiguer接口的所有其他方法,但没有调用addFormatters。也许他们忘了给它打电话了 或者他们有其他原因。通过从WebMvcConfigurationSupport扩展,可以根据需要调用每个方法,并且JUnit测试成功

特别是,此代码最终会执行:

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

老实说,我不知道在实现Spring文档中描述的接口时,到底失败了什么。

您是否在applicationContext.xml或jpm-servlet.xml中包含了转换服务?日志里有线索吗?我所能想到的可能性是spring没有找到您的conversionService并使用了默认的conversionService,那么将conversionService更改为conversionService 1呢?conversionService位于jpm-servlet.xml文件中,但对于一些未知的原因,测试上下文使用了默认的conversionService,而不是我的:看起来它被忽略了。我重新命名了它,但没有帮助。您可以检查日志集级别以进行调试吗,应该有类似于定义bean[conversionService 1,mvcContentNegotiationManager],您是否发现正在创建其他conversionService?是的,它就在那里。2013/09/04 15:53:15[main]INFO org.springframework.beans.factory.support.DefaultListableBeanFactory-在org.springframework.beans.factory.support中预实例化单例。DefaultListableBeanFactory@1fa62593:定义bean[…,conversionService,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping1,…]如果回答了这个问题,请批准回答您是否在applicationContext.xml或jpm-servlet.xml中包含了conversionService?日志中是否有任何线索?我能想到的可能是spring没有找到您的conversionService并使用了默认的conversionService,那么将conversionService更改为conversionService 1呢?conversionService I是的,对于一些未知的原因,测试上下文使用默认的转换服务,而不是我的转换服务:看起来它被忽略了。我重命名了它,但没有帮助。您可以检查日志集级别以进行调试吗,应该有类似定义bean的内容[conversionService 1,mvcContentNegotiationManager,,您是否发现正在创建其他conversionService?是的,它就在那里。2013/09/04 15:53:15[main]INFO org.springframework.beans.factory.support.DefaultListableBeanFactory-在org.springframework.beans.factory.support中预实例化单例。DefaultListableBeanFactory@1fa62593:定义bean[…,conversionService,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping1,…]如果这个问题得到了回答,请批准回答。我会看一看。谢谢!我会看一看。谢谢!投票通过了,因为它现在更相关了。但是,我发现在我的配置类中添加@EnableWebMvc修复了它,而不是在测试中修复了它,尽管使用了WebMVCConfigure。也许它已经被修复了。投票通过了,因为它更相关但是,我发现将@EnableWebMvc添加到我的配置类中修复了它,而不是在测试中,尽管使用了webmvcconfiguer。也许从那时起它就被修复了。