0
votes

My system

  • spring-boot-version 2.0.1.RELEASE
  • spring-Cloud-Version Finchley.M9
  • java 1.8
  • org.springframework.boot:spring-boot-starter-webflux
  • org.javamoney:moneta:1.2.1
  • org.springframework.restdocs:spring-restdocs-webtestclient

custom SimpleModule

  @Bean
  public SimpleModule moneyModule() {
    return new MoneyModule();
  }

  public MoneyModule() {
      addSerializer(Money.class, new MoneySerializer());
      addValueInstantiator(Money.class, new MoneyInstantiator());
  }

Integration test

@RunWith(SpringRunner.class)
@ActiveProfiles("integTest")
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class XxxxHandlerTest{
    @Autowired
  WebTestClient webTestClient;

  @Rule
  public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();

  @Autowired
  ApplicationContext context;

  @Before
  public void init() throws Exception {
    this.webTestClient = WebTestClient.bindToApplicationContext(this.context)
        .configureClient()
        .baseUrl("http://local.com.cn")
        .filter(WebTestClientRestDocumentation
            .documentationConfiguration(this.restDocumentation)
            .operationPreprocessors()
            .withResponseDefaults(prettyPrint())
        )
        .build();
  }

  @Test
  public void testStoreVoucher() {
    Operator mockUser = Operator.builder().name("jack").id(ObjectId.get().toString()).build();
    List<AccountingEntry> accountingEntries = Arrays.asList(AccountingEntry.builder()
        .code("12121").summary("receipt").debit(Money.of(100, "CNY"))
        .credit(Money.of(100, "CNY")).build());
    VoucherPost voucherPost = VoucherPost.builder().operator(mockUser)
        .accountingEntries(accountingEntries).build();
    webTestClient.post()
        .uri(mockUrl)
        .body(BodyInserters.fromObject(voucherPost))
        .exchange().expectStatus().isOk();
}

test error

org.springframework.core.codec.DecodingException: JSON decoding error: Cannot construct instance of `org.javamoney.moneta.Money` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `org.javamoney.moneta.Money` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)

When I remove the following code,test code is working properly

this.webTestClient = WebTestClient.bindToApplicationContext(this.context)
        .configureClient()
        .baseUrl("http://local.com.cn")
        .filter(WebTestClientRestDocumentation
            .documentationConfiguration(this.restDocumentation)
            .operationPreprocessors()
            .withResponseDefaults(prettyPrint())
        )
        .build();

I guess the webtestclient's configuration has caused it to ignore the custom jackson module, so I would like to know how to solve this problem. Maybe there is no problem at all, but my configuration is wrong. Please give me some advice. Thank you.

1
If you literally do what you have described (remove the entire initialisation of this.webTestClient) then I would expect a NullPointerException. Can you describe precisely how you've initialized this.webTestClient in the case where the test code is working properly? - Andy Wilkinson
Sorry I didn't describe it clearly.I tried two initialization methods,@Autowired WebTestClient webTestClient and WebTestClient webTestClient; WebTestClient.bindToApplicationContext.... first way, the Money serializer/deserializer all right, but restdocs has error, because it can not find restDocumentation configuration. the second way, restdocs is ok, but Money deserilzer has exception. - Djack

1 Answers

0
votes

As you are using Spring Boot, I'd recommend that you use @AutoConfigureRestDocs rather than manually configuring a WebTestClient to use REST Docs. This is the easiest way to get a WebTestClient that's been configured with a Jackson ObjectMapper that will use your custom module.

To do so, your test class would look something like this:

@RunWith(SpringRunner.class)
@ActiveProfiles("integTest")
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs(uriHost="local.com.cn")
public class XxxxHandlerTest{

    @Autowired
    WebTestClient webTestClient;

    @Test
    public void testStoreVoucher() {
        Operator mockUser = Operator.builder().name("jack").id(ObjectId.get().toString()).build();
        List<AccountingEntry> accountingEntries = Arrays.asList(AccountingEntry.builder()
            .code("12121").summary("receipt").debit(Money.of(100, "CNY"))
            .credit(Money.of(100, "CNY")).build());
        VoucherPost voucherPost = VoucherPost.builder().operator(mockUser)
            .accountingEntries(accountingEntries).build();
        webTestClient.post()
            .uri(mockUrl)
            .body(BodyInserters.fromObject(voucherPost))
            .exchange().expectStatus().isOk();
    }

}