Unit pengujian serialisasi Json di Spring Boot



Pendahuluan


Salah satu tugas utama dari setiap layanan web adalah mengembalikan model ke sisi klien, dan dalam hal ini, Boot Spring memberikan tingkat abstraksi yang nyaman, memungkinkan pengembang untuk tetap pada level bekerja dengan model, dan meninggalkan proses serialisasi model di luar kode sumber program. Tetapi bagaimana jika serialisasi itu sendiri menjadi bagian dari logika bisnis aplikasi dan karenanya memerlukan cakupan uji kasus?

Dalam artikel ini, kita akan mempertimbangkan salah satu skenario ketika kita mungkin perlu mempertimbangkan fitur-fitur logika bisnis dari aplikasi selama serialisasi (skenario pembulatan jumlah uang), pada contoh di mana kita akan menemukan mekanisme serialisasi di Spring Boot, dan juga menjelaskan metode pengujian yang mungkin.

Pernyataan masalah


Biarkan layanan web kami bertanggung jawab untuk menyediakan informasi tentang pengeluaran pelanggan, dan kami perlu memberikan data dengan akurasi yang dapat dikonfigurasi. Solusi logis adalah membuat semua transformasi model ke pinggiran layanan, sambil mempertahankan visibilitas penerapan pembulatan logika.

Pertimbangkan solusi yang memungkinkan


Bayangkan pengontrol aplikasi kita, yang akan mengembalikan model yang diinginkan.

@RestController public class AccountController { //        , //       . @Autowired private AccountService accountService; @RequestMapping(value = "/account/{clientId}", method = RequestMethod.GET, produces = "application/json") public Account getAccount(@PathVariable long clientId) throws Exception { Account result = accountService.getAccount(clientId); //  ,    - //      ,    json, //    . return result; } } 

Sekarang mari kita lihat model kita.

 public class Account { private Long clientId; //    Spring Boot   FasterXML/jackson, //    API  ,   . // ,       //     MoneySerializer @JsonSerialize(using = MoneySerializer.class) private BigDecimal value; //    getter'  setter'    } 

Anda mungkin harus berurusan dengan anotasi lain untuk penyesuaian. Fitur dari anotasi ini adalah kemampuan untuk menentukan layanan Anda yang bertanggung jawab untuk membuat serialisasi bidang model yang dianotasi.

Sebelum kita melihat apa yang dimaksud dengan serializer, kita akan memperumit tugas: biarkan parameter pembulatan dapat dikonfigurasi melalui beberapa layanan internal yang abstrak setiap manifestasi resolusi parameter dinamis.

Komplikasi ini adalah poin kunci kami yang ingin kami pertimbangkan. Seperti yang bisa kita lihat dari implementasi model, API kerangka kerja kita mengambil kelas serialisasi dalam argumen, yang berarti bahwa siklus hidup serializer berada di bawah kendali kerangka serializer. Ini menimbulkan pertanyaan, apa yang harus dilakukan jika kita ingin menyuntikkan ketergantungan dari konteks aplikasi kita ke dalam serializer? Untuk melakukan ini, pertimbangkan penerapan serializer di atas.

 //     , //  Jackson   Spring, // API    //    Spring DI @JsonComponent public class MoneySerializer extends JsonSerializer<BigDecimal> { //  ,   , //    Spring Boot    Bean'. private RoundingHolder roundingHolder; @Autowired public MoneySerializer(RoundingHolder roundingHolder) { this.roundingHolder = roundingHolder; } //       , // ,   ,      - //      . @Override public void serialize(BigDecimal value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeNumber(value.setScale(roundingHolder.getPrecision(), roundingHolder.getRoundingMode())); } } 

Layanan kami sudah siap, tetapi sebagai pengembang yang bertanggung jawab, kami ingin memastikan bahwa dapur yang kami kumpulkan berfungsi.

Mari kita beralih ke pengujian.


Mari kita lihat apa yang ditawarkan kerangka kerja pengujian kepada kami.

 //     ,   //    Spring,       . //     JsonTest,      //      , //    JSON-. @JsonTest @ContextConfiguration(classes = {AccountSerializationTest.Config.class}) @RunWith(SpringRunner.class) public class AccountSerializationTest { //  ,      //     ObjectMapper,    . //       . //    , //      . @Autowired private ObjectMapper objectMapper; @Test public void testAccountMoneyRounding() throws Exception { Account account = new Account(); account.setClientId(1L); account.setValue(BigDecimal.valueOf(1.123456789)); String expectedResult = "{\"clientId\":1,\"value\":\1.123\}"; // ,          JSON, //     -. assertEquals(expectedResult, objectMapper.writeValueAsString(account)); } //   MoneySerializer   API  //    ,       //    Jackson.   ,   , //   Spring , ,  //      . @TestConfiguration public static class Config { @Bean public static RoundingHolder roundingHolder() { RoundingHolder roundingHolder = Mockito.mock(RoundingHolder.class); //   ,         Mockito.when(roundingHolder.getMathContext()).thenReturn(new MathContext(3, RoundingMode.HALF_EVEN)); return roundingHolder; } } } 

Mari kita renungkan momen ini secara lebih rinci. Jackson menggunakan kelas ObjectMapper untuk membuat serial dan deserialize model. Ini persis objek konteks yang bertanggung jawab untuk transformasi model, oleh karena itu, untuk memastikan bagaimana model akan disajikan, Anda perlu memeriksa bagaimana ObjectMapper memprosesnya dari konteks.

Jika Anda ingin membuat ObjectMapper kustom Anda sendiri, Anda dapat menemukan contoh khas berikut: ObjectMapper mapper = ObjectMapper baru . Namun, lihat bagaimana Spring Boot membuat instance kelas ini secara default. Untuk melakukan ini, lihat kode sumber autoconfiguration JacksonAutoConfiguration yang bertanggung jawab untuk membuat objek:

 @Bean public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) { return builder.createXmlMapper(false).build(); } 

Dan jika kita melangkah lebih jauh dan melihat build () , kami menemukan bahwa untuk serialisasi, yang bisa kita gunakan ketika bekerja dengan pembuat peta default (seperti menyuntikkan layanan ke pembuat serial kustom), tidak cukup hanya dengan membuat pembuat map Bean, Anda harus beralih ke pembuat yang disediakan . Ngomong-ngomong, dalam dokumentasi Spring Boot sendiri, ini dinyatakan secara eksplisit.

Dengan penyimpangan, saya ingin menambahkan referensi ke JacksonTester . Sebagai perwakilan dari shell untuk pengujian serialisasi BDD dalam konteks Mockito.

Untuk meringkas


  • Spring Boot menyediakan kemampuan untuk menyesuaikan serialisasi model melalui anotasi JsonSerializer
  • Untuk menguji serialisasi, gunakan mapper dalam konfigurasi yang sama seperti pada aplikasi
  • Saat mengganti kacang dari konfigurasi otomatis Boot Musim Semi, perhatikan bagaimana kacang ini membuat Boot Musim Semi itu sendiri, agar tidak ketinggalan peluang yang dimiliki kacang default
  • Anda dapat menggunakan anotasi JsonTest untuk menentukan konteks terbatas yang diperlukan untuk menguji serialisasi.

Kesimpulan


Terima kasih atas perhatian anda! Contoh ini akan relevan untuk versi Spring Boot 2.1.x saat ini, serta untuk versi sebelumnya hingga 1.4.x. Juga, teknik ini cocok untuk situasi dengan deserialisasi model. Cari di bawah kap kerangka kerja inti Anda untuk pemahaman yang lebih baik tentang mekanisme operasi aplikasi dan mengambil pendekatan yang bertanggung jawab untuk pengujian.

Source: https://habr.com/ru/post/id452188/


All Articles