10 Kesalahan Kerangka Musim Semi Paling Umum
Diterbitkan: 2022-03-11Musim semi bisa dibilang salah satu kerangka kerja Java paling populer, dan juga binatang yang kuat untuk dijinakkan. Meskipun konsep dasarnya cukup mudah dipahami, menjadi pengembang Spring yang kuat membutuhkan waktu dan usaha.
Pada artikel ini kami akan membahas beberapa kesalahan umum di Spring, yang secara khusus berorientasi pada aplikasi web dan Spring Boot. Seperti yang dinyatakan oleh situs web Spring Boot, Spring Boot mengambil pandangan berpendirian tentang bagaimana aplikasi siap produksi harus dibangun, jadi artikel ini akan mencoba meniru tampilan itu dan memberikan gambaran umum tentang beberapa tip yang akan dimasukkan dengan baik ke dalam pengembangan aplikasi web Spring Boot standar.
Jika Anda tidak terlalu akrab dengan Spring Boot tetapi masih ingin mencoba beberapa hal yang disebutkan, saya telah membuat repositori GitHub yang menyertai artikel ini. Jika Anda merasa tersesat selama artikel ini, saya sarankan untuk mengkloning repositori dan bermain-main dengan kode di mesin lokal Anda.
Kesalahan Umum #1: Level Terlalu Rendah
Kami menyelesaikannya dengan kesalahan umum ini karena sindrom "tidak ditemukan di sini" cukup umum di dunia pengembangan perangkat lunak. Gejala termasuk menulis ulang secara teratur potongan kode yang umum digunakan dan banyak pengembang tampaknya menderita karenanya.
Meskipun memahami internal perpustakaan tertentu dan implementasinya sebagian besar baik dan perlu (dan juga bisa menjadi proses pembelajaran yang hebat), pengembangan Anda sebagai insinyur perangkat lunak akan merugikan jika terus-menerus menangani implementasi tingkat rendah yang sama. rincian. Ada alasan mengapa abstraksi dan kerangka kerja seperti Spring ada, yang justru memisahkan Anda dari pekerjaan manual yang berulang dan memungkinkan Anda untuk berkonsentrasi pada detail tingkat yang lebih tinggi– objek domain dan logika bisnis Anda.
Jadi merangkul abstraksi - saat berikutnya Anda dihadapkan dengan masalah tertentu, lakukan pencarian cepat terlebih dahulu dan tentukan apakah perpustakaan yang memecahkan masalah itu sudah terintegrasi ke dalam Spring; saat ini, kemungkinan Anda akan menemukan solusi yang ada yang cocok. Sebagai contoh pustaka yang berguna, saya akan menggunakan anotasi Project Lombok dalam contoh untuk sisa artikel ini. Lombok digunakan sebagai generator kode boilerplate dan pengembang malas di dalam diri Anda semoga tidak memiliki masalah untuk membiasakan diri dengan perpustakaan. Sebagai contoh, lihat seperti apa “kacang Jawa standar” di Lombok:
@Getter @Setter @NoArgsConstructor public class Bean implements Serializable { int firstBeanProperty; String secondBeanProperty; }
Seperti yang Anda bayangkan, kode di atas dikompilasi menjadi:
public class Bean implements Serializable { private int firstBeanProperty; private String secondBeanProperty; public int getFirstBeanProperty() { return this.firstBeanProperty; } public String getSecondBeanProperty() { return this.secondBeanProperty; } public void setFirstBeanProperty(int firstBeanProperty) { this.firstBeanProperty = firstBeanProperty; } public void setSecondBeanProperty(String secondBeanProperty) { this.secondBeanProperty = secondBeanProperty; } public Bean() { } }
Namun, perhatikan bahwa kemungkinan besar Anda harus menginstal plugin jika Anda ingin menggunakan Lombok dengan IDE Anda. Versi plugin IntelliJ IDEA dapat ditemukan di sini.
Kesalahan Umum #2: 'Bocor' Internal
Mengekspos struktur internal Anda bukanlah ide yang baik karena menciptakan ketidakfleksibelan dalam desain layanan dan akibatnya mendorong praktik pengkodean yang buruk. Internal 'bocor' dimanifestasikan dengan membuat struktur database dapat diakses dari titik akhir API tertentu. Sebagai contoh, katakanlah POJO berikut (“Objek Jawa Lama Biasa”) mewakili sebuah tabel di database Anda:
@Entity @NoArgsConstructor @Getter public class TopTalentEntity { @Id @GeneratedValue private Integer id; @Column private String name; public TopTalentEntity(String name) { this.name = name; } }
Katakanlah ada titik akhir yang perlu mengakses data TopTalentEntity
. Menggoda untuk mengembalikan TopTalentEntity
, solusi yang lebih fleksibel adalah membuat kelas baru untuk mewakili data TopTalentEntity
pada titik akhir API:
@AllArgsConstructor @NoArgsConstructor @Getter public class TopTalentData { private String name; }
Dengan begitu, membuat perubahan pada back-end database Anda tidak akan memerlukan perubahan tambahan apa pun di lapisan layanan. Pertimbangkan apa yang akan terjadi jika menambahkan bidang 'kata sandi' ke TopTalentEntity
untuk menyimpan hash kata sandi pengguna Anda di database - tanpa konektor seperti TopTalentData
, lupa mengubah front-end layanan akan secara tidak sengaja mengekspos beberapa informasi rahasia yang sangat tidak diinginkan !
Kesalahan Umum #3: Kurangnya Pemisahan Kekhawatiran
Seiring pertumbuhan aplikasi Anda, organisasi kode semakin menjadi hal yang semakin penting. Ironisnya, sebagian besar prinsip rekayasa perangkat lunak yang baik mulai rusak dalam skala besar – terutama dalam kasus di mana tidak banyak pemikiran yang diberikan pada desain arsitektur aplikasi. Salah satu kesalahan paling umum yang kemudian cenderung dilakukan oleh pengembang adalah mencampuradukkan masalah kode, dan ini sangat mudah dilakukan!
Apa yang biasanya memecah pemisahan masalah hanyalah 'membuang' fungsionalitas baru ke dalam kelas yang ada. Ini, tentu saja, solusi jangka pendek yang bagus (sebagai permulaan, ini membutuhkan lebih sedikit pengetikan) tetapi itu pasti menjadi masalah di masa depan, baik selama pengujian, pemeliharaan, atau di antara keduanya. Pertimbangkan pengontrol berikut, yang mengembalikan TopTalentData
dari repositorinya:
@RestController public class TopTalentController { private final TopTalentRepository topTalentRepository; @RequestMapping("/toptal/get") public List<TopTalentData> getTopTalent() { return topTalentRepository.findAll() .stream() .map(this::entityToData) .collect(Collectors.toList()); } private TopTalentData entityToData(TopTalentEntity topTalentEntity) { return new TopTalentData(topTalentEntity.getName()); } }
Pada awalnya, sepertinya tidak ada yang salah dengan potongan kode ini; ini menyediakan daftar TopTalentData
yang sedang diambil dari TopTalentEntity
. Namun, jika dilihat lebih dekat, kita dapat melihat bahwa sebenarnya ada beberapa hal yang TopTalentController
di sini; yaitu, memetakan permintaan ke titik akhir tertentu, mengambil data dari repositori, dan mengonversi entitas yang diterima dari TopTalentRepository
ke dalam format yang berbeda. Solusi 'lebih bersih' akan memisahkan kekhawatiran itu ke dalam kelas mereka sendiri. Mungkin terlihat seperti ini:
@RestController @RequestMapping("/toptal") @AllArgsConstructor public class TopTalentController { private final TopTalentService topTalentService; @RequestMapping("/get") public List<TopTalentData> getTopTalent() { return topTalentService.getTopTalent(); } } @AllArgsConstructor @Service public class TopTalentService { private final TopTalentRepository topTalentRepository; private final TopTalentEntityConverter topTalentEntityConverter; public List<TopTalentData> getTopTalent() { return topTalentRepository.findAll() .stream() .map(topTalentEntityConverter::toResponse) .collect(Collectors.toList()); } } @Component public class TopTalentEntityConverter { public TopTalentData toResponse(TopTalentEntity topTalentEntity) { return new TopTalentData(topTalentEntity.getName()); } }
Keuntungan tambahan untuk hierarki ini adalah memungkinkan kita untuk menentukan di mana fungsionalitas berada hanya dengan memeriksa nama kelas. Selanjutnya, selama pengujian kita dapat dengan mudah mengganti salah satu kelas dengan implementasi tiruan jika diperlukan.
Kesalahan Umum #4: Inkonsistensi dan Penanganan Kesalahan yang Buruk
Topik konsistensi tidak selalu eksklusif untuk Spring (atau Java, dalam hal ini), tetapi masih merupakan aspek penting untuk dipertimbangkan ketika mengerjakan proyek Spring. Meskipun gaya pengkodean dapat diperdebatkan (dan biasanya merupakan masalah kesepakatan di dalam tim atau di dalam seluruh perusahaan), memiliki standar yang sama ternyata merupakan bantuan produktivitas yang hebat. Hal ini terutama berlaku dengan tim multi-orang; konsistensi memungkinkan hand-off terjadi tanpa banyak sumber daya yang dihabiskan untuk berpegangan tangan atau memberikan penjelasan panjang lebar mengenai tanggung jawab kelas yang berbeda
Pertimbangkan proyek Spring dengan berbagai file konfigurasi, layanan, dan pengontrolnya. Menjadi konsisten secara semantik dalam menamai mereka menciptakan struktur yang mudah dicari di mana setiap pengembang baru dapat mengatur jalannya di sekitar kode; menambahkan sufiks Config ke kelas konfigurasi Anda, Sufiks Service ke layanan Anda, dan sufiks Controller ke pengontrol Anda, misalnya.
Berkaitan erat dengan topik konsistensi, penanganan kesalahan di sisi server perlu mendapat penekanan khusus. Jika Anda pernah harus menangani tanggapan pengecualian dari API yang ditulis dengan buruk, Anda mungkin tahu mengapa– mungkin sulit untuk menguraikan pengecualian dengan benar, dan bahkan lebih menyakitkan untuk menentukan alasan mengapa pengecualian itu terjadi sejak awal.
Sebagai pengembang API, idealnya Anda ingin mencakup semua titik akhir yang dihadapi pengguna dan menerjemahkannya ke dalam format kesalahan umum. Ini biasanya berarti memiliki kode kesalahan umum dan deskripsi daripada solusi cop-out dari a) mengembalikan pesan "500 Internal Server Error", atau b) hanya membuang jejak tumpukan ke pengguna (yang sebenarnya harus dihindari dengan cara apa pun karena mengekspos internal Anda selain sulit untuk menangani sisi klien).
Contoh format respons kesalahan umum mungkin:
@Value public class ErrorResponse { private Integer errorCode; private String errorMessage; }
Sesuatu yang mirip dengan ini biasanya ditemui di sebagian besar API populer, dan cenderung berfungsi dengan baik karena dapat dengan mudah dan sistematis didokumentasikan. Menerjemahkan pengecualian ke dalam format ini dapat dilakukan dengan memberikan anotasi @ExceptionHandler
ke suatu metode (contoh anotasi ada di Kesalahan Umum #6).
Kesalahan Umum #5: Salah Menghadapi Multithreading
Terlepas dari apakah itu ditemui di desktop atau aplikasi web, Spring atau tidak Spring, multithreading bisa menjadi kacang yang sulit untuk dipecahkan. Masalah yang disebabkan oleh eksekusi paralel program sangat sulit dipahami dan seringkali sangat sulit untuk di-debug - pada kenyataannya, karena sifat masalahnya, begitu Anda menyadari bahwa Anda sedang berhadapan dengan masalah eksekusi paralel, Anda mungkin akan harus melepaskan debugger sepenuhnya dan memeriksa kode Anda "dengan tangan" sampai Anda menemukan akar penyebab kesalahan. Sayangnya, solusi cookie-cutter tidak ada untuk memecahkan masalah seperti itu; tergantung pada kasus spesifik Anda, Anda harus menilai situasi dan kemudian menyerang masalah dari sudut yang Anda anggap terbaik.
Idealnya Anda tentu saja ingin menghindari bug multithreading sama sekali. Sekali lagi, pendekatan satu ukuran untuk semua tidak ada untuk melakukannya, tetapi berikut adalah beberapa pertimbangan praktis untuk men-debug dan mencegah kesalahan multithreading:
Hindari Negara Global
Pertama, selalu ingat isu “negara global”. Jika Anda membuat aplikasi multithread, segala sesuatu yang dapat dimodifikasi secara global harus dipantau secara ketat dan, jika mungkin, dihapus sama sekali. Jika ada alasan mengapa variabel global harus tetap dapat dimodifikasi, gunakan sinkronisasi dengan hati-hati dan lacak kinerja aplikasi Anda untuk mengonfirmasi bahwa itu tidak lamban karena masa tunggu yang baru diperkenalkan.
Hindari Mutabilitas
Yang ini datang langsung dari pemrograman fungsional dan, disesuaikan dengan OOP, menyatakan bahwa mutabilitas kelas dan status perubahan harus dihindari. Singkatnya, ini berarti metode penyetel sebelumnya dan memiliki bidang final pribadi di semua kelas model Anda. Satu-satunya waktu nilai mereka bermutasi adalah selama konstruksi. Dengan cara ini Anda dapat yakin bahwa tidak ada masalah pertentangan yang muncul dan bahwa mengakses properti objek akan memberikan nilai yang benar setiap saat.
Catat Data Penting
Nilai di mana aplikasi Anda dapat menyebabkan masalah dan catat semua data penting terlebih dahulu. Jika terjadi kesalahan, Anda akan bersyukur memiliki informasi yang menyatakan permintaan mana yang diterima dan memiliki wawasan yang lebih baik tentang mengapa aplikasi Anda berperilaku tidak semestinya. Sekali lagi perlu diperhatikan bahwa logging memperkenalkan file I/O tambahan dan oleh karena itu tidak boleh disalahgunakan karena dapat sangat memengaruhi kinerja aplikasi Anda.

Gunakan Kembali Implementasi yang Ada
Kapan pun Anda perlu memunculkan utas Anda sendiri (misalnya untuk membuat permintaan asinkron ke layanan yang berbeda), gunakan kembali implementasi aman yang ada daripada membuat solusi Anda sendiri. Ini akan, sebagian besar, berarti memanfaatkan ExecutorServices dan CompletableFutures gaya fungsional Java 8 yang rapi untuk pembuatan utas. Spring juga memungkinkan pemrosesan permintaan asinkron melalui kelas DeferredResult.
Kesalahan Umum #6: Tidak Menggunakan Validasi Berbasis Anotasi
Bayangkan layanan TopTalent kami dari sebelumnya membutuhkan titik akhir untuk menambahkan Top Talent baru. Lebih lanjut, katakanlah, untuk beberapa alasan yang benar-benar valid, setiap nama baru harus panjangnya tepat 10 karakter. Salah satu cara untuk melakukan ini mungkin sebagai berikut:
@RequestMapping("/put") public void addTopTalent(@RequestBody TopTalentData topTalentData) { boolean nameNonExistentOrHasInvalidLength = Optional.ofNullable(topTalentData) .map(TopTalentData::getName) .map(name -> name.length() == 10) .orElse(true); if (nameNonExistentOrInvalidLength) { // throw some exception } topTalentService.addTopTalent(topTalentData); }
Namun, di atas (selain dibangun dengan buruk) sebenarnya bukan solusi 'bersih'. Kami memeriksa lebih dari satu jenis validitas (yaitu, bahwa TopTalentData
tidak nol, dan bahwa TopTalentData.name
tidak nol, dan bahwa TopTalentData.name
10 karakter), serta melemparkan pengecualian jika data tidak valid .
Ini dapat dieksekusi jauh lebih bersih dengan menggunakan validator Hibernate dengan Spring. Pertama-tama mari kita refactor metode addTopTalent
untuk mendukung validasi:
@RequestMapping("/put") public void addTopTalent(@Valid @NotNull @RequestBody TopTalentData topTalentData) { topTalentService.addTopTalent(topTalentData); } @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException) { // handle validation exception }
Selain itu, kita harus menunjukkan properti apa yang ingin kita validasi di kelas TopTalentData
:
public class TopTalentData { @Length(min = 10, max = 10) @NotNull private String name; }
Sekarang Spring akan mencegat permintaan dan memvalidasinya sebelum metode dipanggil – tidak perlu menggunakan tes manual tambahan.
Cara lain kita bisa mencapai hal yang sama adalah dengan membuat anotasi kita sendiri. Meskipun Anda biasanya hanya akan menggunakan anotasi khusus ketika kebutuhan Anda melebihi set batasan bawaan Hibernate, untuk contoh ini mari kita berpura-pura bahwa @Length tidak ada. Anda akan membuat validator yang memeriksa panjang string dengan membuat dua kelas tambahan, satu untuk validasi dan satu lagi untuk properti anotasi:
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = { MyAnnotationValidator.class }) public @interface MyAnnotation { String message() default "String length does not match expected"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; int value(); } @Component public class MyAnnotationValidator implements ConstraintValidator<MyAnnotation, String> { private int expectedLength; @Override public void initialize(MyAnnotation myAnnotation) { this.expectedLength = myAnnotation.value(); } @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { return s == null || s.length() == this.expectedLength; } }
Perhatikan bahwa dalam kasus ini, praktik terbaik tentang pemisahan masalah mengharuskan Anda untuk menandai properti sebagai valid jika itu null ( s == null
dalam metode isValid
), lalu gunakan anotasi @NotNull
jika itu merupakan persyaratan tambahan untuk Properti:
public class TopTalentData { @MyAnnotation(value = 10) @NotNull private String name; }
Kesalahan Umum #7: (Masih) Menggunakan Konfigurasi Berbasis XML
Meskipun XML merupakan kebutuhan untuk versi Spring sebelumnya, saat ini sebagian besar konfigurasi dapat dilakukan secara eksklusif melalui kode/anotasi Java; Konfigurasi XML hanya berperan sebagai kode boilerplate tambahan dan tidak perlu.
Artikel ini (serta repositori GitHub yang menyertainya) menggunakan anotasi untuk mengonfigurasi Spring dan Spring mengetahui kacang mana yang harus ditransfer karena paket root telah dianotasi dengan anotasi komposit @SpringBootApplication
, seperti:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Anotasi komposit (Anda dapat mempelajari lebih lanjut tentangnya di dokumentasi Spring hanya memberi Spring petunjuk tentang paket mana yang harus dipindai untuk mengambil kacang. Dalam kasus konkret kami, ini berarti paket di bawah atas (co.kukurin) berikut akan digunakan untuk kabel:
-
@Component
(TopTalentConverter
,MyAnnotationValidator
) -
@RestController
(TopTalentController
) -
@Repository
(TopTalentRepository
) -
@Service
(TopTalentService
) kelas
Jika kami memiliki kelas beranotasi @Configuration
tambahan, mereka juga akan diperiksa untuk konfigurasi berbasis Java.
Kesalahan Umum #8: Melupakan Profil
Masalah yang sering dihadapi dalam pengembangan server adalah membedakan antara jenis konfigurasi yang berbeda, biasanya konfigurasi produksi dan pengembangan Anda. Daripada secara manual mengganti berbagai entri konfigurasi setiap kali Anda beralih dari pengujian ke penerapan aplikasi Anda, cara yang lebih efisien adalah dengan menggunakan profil.
Pertimbangkan kasus di mana Anda menggunakan database dalam memori untuk pengembangan lokal, dengan database MySQL dalam produksi. Ini pada dasarnya berarti Anda akan menggunakan URL yang berbeda dan (semoga) kredensial yang berbeda untuk mengakses masing-masing dari keduanya. Mari kita lihat bagaimana ini bisa dilakukan dengan dua file konfigurasi yang berbeda:
file application.yaml
# set default profile to 'dev' spring.profiles.active: dev # production database details spring.datasource.url: 'jdbc:mysql://localhost:3306/toptal' spring.datasource.username: root spring.datasource.password:
file application-dev.yaml
spring.datasource.url: 'jdbc:h2:mem:' spring.datasource.platform: h2
Agaknya Anda tidak ingin secara tidak sengaja melakukan tindakan apa pun pada basis data produksi Anda saat mengutak-atik kode, jadi masuk akal untuk mengatur profil default ke dev. Di server, Anda kemudian dapat mengganti profil konfigurasi secara manual dengan memberikan parameter -Dspring.profiles.active=prod
ke JVM. Atau, Anda juga dapat mengatur variabel lingkungan OS Anda ke profil default yang diinginkan.
Kesalahan Umum #9: Gagal Menerima Injeksi Ketergantungan
Menggunakan injeksi ketergantungan dengan benar dengan Spring berarti memungkinkannya untuk menyambungkan semua objek Anda dengan memindai semua kelas konfigurasi yang diinginkan; ini terbukti berguna untuk memisahkan hubungan dan juga membuat pengujian menjadi jauh lebih mudah. Alih-alih kelas kopling ketat dengan melakukan sesuatu seperti ini:
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController() { this.topTalentService = new TopTalentService(); } }
Kami mengizinkan Spring untuk melakukan pengkabelan untuk kami:
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController(TopTalentService topTalentService) { this.topTalentService = topTalentService; } }
Pembicaraan Google Misko Hevery menjelaskan 'mengapa' injeksi ketergantungan secara mendalam, jadi mari kita lihat bagaimana penggunaannya dalam praktik. Di bagian pemisahan masalah (Kesalahan Umum #3), kami membuat kelas layanan dan pengontrol. Katakanlah kita ingin menguji pengontrol dengan asumsi bahwa TopTalentService
berperilaku dengan benar. Kita dapat menyisipkan objek tiruan sebagai pengganti implementasi layanan yang sebenarnya dengan menyediakan kelas konfigurasi terpisah:
@Configuration public class SampleUnitTestConfig { @Bean public TopTalentService topTalentService() { TopTalentService topTalentService = Mockito.mock(TopTalentService.class); Mockito.when(topTalentService.getTopTalent()).thenReturn( Stream.of("Mary", "Joel").map(TopTalentData::new).collect(Collectors.toList())); return topTalentService; } }
Kemudian kita dapat menyuntikkan objek tiruan dengan memberi tahu Spring untuk menggunakan SampleUnitTestConfig
sebagai pemasok konfigurasinya:
@ContextConfiguration(classes = { SampleUnitTestConfig.class })
Ini kemudian memungkinkan kita untuk menggunakan konfigurasi konteks untuk menyuntikkan kacang kustom ke dalam pengujian unit.
Kesalahan Umum #10: Kurangnya Pengujian, atau Pengujian yang Tidak Benar
Meskipun ide pengujian unit telah bersama kami untuk waktu yang lama sekarang, banyak pengembang tampaknya "lupa" untuk melakukan ini (terutama jika tidak diperlukan ), atau hanya menambahkannya sebagai renungan. Ini jelas tidak diinginkan karena pengujian tidak hanya memverifikasi kebenaran kode Anda, tetapi juga berfungsi sebagai dokumentasi tentang bagaimana aplikasi harus berperilaku dalam situasi yang berbeda.
Saat menguji layanan web, Anda jarang melakukan pengujian unit 'murni' secara eksklusif, karena komunikasi melalui HTTP biasanya mengharuskan Anda untuk memanggil Spring's DispatcherServlet
dan melihat apa yang terjadi ketika HttpServletRequest
yang sebenarnya diterima (menjadikannya sebagai tes integrasi , menangani validasi, serialisasi , dll). REST Assured, Java DSL untuk pengujian layanan REST yang mudah, selain MockMVC, telah terbukti memberikan solusi yang sangat elegan. Pertimbangkan cuplikan kode berikut dengan injeksi ketergantungan:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { Application.class, SampleUnitTestConfig.class }) public class RestAssuredTestDemonstration { @Autowired private TopTalentController topTalentController; @Test public void shouldGetMaryAndJoel() throws Exception { // given MockMvcRequestSpecification givenRestAssuredSpecification = RestAssuredMockMvc.given() .standaloneSetup(topTalentController); // when MockMvcResponse response = givenRestAssuredSpecification.when().get("/toptal/get"); // then response.then().statusCode(200); response.then().body("name", hasItems("Mary", "Joel")); } }
SampleUnitTestConfig
implementasi tiruan dari TopTalentService
ke TopTalentController
sementara semua kelas lainnya ditransfer menggunakan konfigurasi standar yang disimpulkan dari paket pemindaian yang berakar pada paket kelas Aplikasi. RestAssuredMockMvc
hanya digunakan untuk menyiapkan lingkungan yang ringan dan mengirim permintaan GET
ke /toptal/get
endpoint.
Menjadi Master Musim Semi
Musim semi adalah kerangka kerja yang kuat yang mudah untuk memulai tetapi membutuhkan beberapa dedikasi dan waktu untuk mencapai penguasaan penuh. Meluangkan waktu untuk membiasakan diri dengan kerangka kerja pasti akan meningkatkan produktivitas Anda dalam jangka panjang dan pada akhirnya membantu Anda menulis kode yang lebih bersih dan menjadi pengembang yang lebih baik.
Jika Anda mencari sumber daya lebih lanjut, Spring In Action adalah buku praktis yang mencakup banyak topik inti Musim Semi.