Bayangkan situasi di mana proyek Anda perlu dikompilasi di berbagai lingkungan.
Sekarang bayangkan tidak semua tes harus lulus dalam lingkungan ini - masing-masing memiliki serangkaian tes sendiri.
Dan lebih baik untuk mengonfigurasi pilihan tes mana yang harus dilakukan dalam file ... application.properties
- setiap tes memiliki sakelar hidup / mati sendiri.
Kedengarannya bagus, bukan?
Kemudian selamat datang di kucing, tempat kami menerapkan semua ini menggunakan SpringBoot 2 dan JUnit 5.
Preset
Pertama, mari kita matikan JUnit 4, yang datang secara default di SpringBoot 2, dan nyalakan JUnit 5 .
Untuk melakukan ini, lakukan perubahan pada pom.xml
:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>junit</groupId> <artifactId>junit</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.3.2</version> <scope>test</scope> </dependency> </dependencies>
Solusi yang diajukan
Kami ingin memberi anotasi pada setiap pengujian dengan anotasi sederhana dengan properti yang menunjukkan apakah pengujian diaktifkan atau tidak. Biarkan saya mengingatkan Anda bahwa kami akan menyimpan nilai-nilai properti ini dalam file application.properties
.
Anotasi
Buat anotasi :
@Retention(RetentionPolicy.RUNTIME) @ExtendWith(TestEnabledCondition.class) public @interface TestEnabled { String property(); }
Pemrosesan anotasi
Penangan anotasi sangat diperlukan.
public class TestEnabledCondition implements ExecutionCondition { @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { Optional<TestEnabled> annotation = context.getElement().map(e -> e.getAnnotation(TestEnabled.class)); return context.getElement() .map(e -> e.getAnnotation(TestEnabled.class)) .map(annotation -> { String property = annotation.property(); return Optional.ofNullable(environment.getProperty(property, Boolean.class)) .map(value -> { if (Boolean.TRUE.equals(value)) { return ConditionEvaluationResult.enabled("Enabled by property: "+property); } else { return ConditionEvaluationResult.disabled("Disabled by property: "+property); } }).orElse( ConditionEvaluationResult.disabled("Disabled - property <"+property+"> not set!") ); }).orElse( ConditionEvaluationResult.enabled("Enabled by default") ); } }
Anda harus membuat kelas (tanpa anotasi Spring @Component
) yang mengimplementasikan antarmuka ExecutionCondition
.
Di kelas ini, Anda perlu menerapkan satu metode antarmuka ini - ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context)
.
Metode ini mengambil konteks pengujian yang dijalankan oleh JUnit dan mengembalikan kondisi apakah pengujian harus dijalankan atau tidak.
Baca lebih lanjut tentang pelaksanaan bersyarat tes JUnit5 dalam dokumentasi resmi.
Tetapi bagaimana kita memeriksa nilai properti yang terdaftar di application.properties
dalam hal ini?
Mendapatkan akses ke konteks Spring dari konteks JUnit
Dengan cara ini kita bisa mendapatkan lingkungan Spring dengan mana uji JUnit kami dijalankan dari ExtensionContext
.
Environment environment = SpringExtension.getApplicationContext(context).getEnvironment();
Anda dapat melihat kode lengkap dari kelas TestEnabledCondition .
Buat Tes
Mari kita membuat beberapa tes dan mencoba mengendalikan peluncurannya:
@SpringBootTest public class SkiptestApplicationTests { @TestEnabled(property = "app.skip.test.first") @Test public void testFirst() { assertTrue(true); } @TestEnabled(property = "app.skip.test.second") @Test public void testSecond() { assertTrue(false); } }
File application.properties
kami terlihat seperti ini:
app.skip.test.first=true app.skip.test.second=false
Jadi ...
Hasil Startup:

Langkah selanjutnya adalah memisahkan prefiks dari properti kami ke dalam anotasi kelas
Menulis nama properti lengkap dari application.properties
sebelum setiap tes adalah tugas yang membosankan. Oleh karena itu, masuk akal untuk menempatkan awalan mereka pada level kelas tes - dalam anotasi terpisah.
Buat anotasi untuk menyimpan awalan - TestEnabledPrefix
:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestEnabledPrefix { String prefix(); }
Memproses dan menggunakan anotasi TestEnabledPrefix
Kami melanjutkan ke pemrosesan anotasi baru.
Mari kita membuat kelas pembantu AnnotationDescription
Dengan kelas ini, kita dapat menyimpan nama properti dari application.properties
dan nilainya.
public class TestEnabledCondition implements ExecutionCondition { static class AnnotationDescription { String name; Boolean annotationEnabled; AnnotationDescription(String prefix, String property) { this.name = prefix + property; } String getName() { return name; } AnnotationDescription setAnnotationEnabled(Boolean value) { this.annotationEnabled = value; return this; } Boolean isAnnotationEnabled() { return annotationEnabled; } } }
Kelas ini bermanfaat bagi kita, karena kita akan menggunakan ekspresi lambda.
Mari kita membuat metode yang akan mengekstraksi nilai properti awalan dari penjelasan kelas TestEnabledPrefix
public class TestEnabledCondition implements ExecutionCondition { private AnnotationDescription makeDescription(ExtensionContext context, String property) { String prefix = context.getTestClass() .map(cl -> cl.getAnnotation(TestEnabledPrefix.class)) .map(TestEnabledPrefix::prefix) .map(pref -> !pref.isEmpty() && !pref.endsWith(".") ? pref + "." : "") .orElse(""); return new AnnotationDescription(prefix, property); } }
Dan sekarang mari kita periksa nilai properti dari application.properties
dengan nama yang ditentukan dalam anotasi tes
public class TestEnabledCondition implements ExecutionCondition { @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { Environment environment = SpringExtension.getApplicationContext(context).getEnvironment(); return context.getElement() .map(e -> e.getAnnotation(TestEnabled.class)) .map(TestEnabled::property) .map(property -> makeDescription(context, property)) .map(description -> description.setAnnotationEnabled(environment.getProperty(description.getName(), Boolean.class))) .map(description -> { if (description.isAnnotationEnabled()) { return ConditionEvaluationResult.enabled("Enabled by property: "+description.getName()); } else { return ConditionEvaluationResult.disabled("Disabled by property: "+description.getName()); } }).orElse( ConditionEvaluationResult.enabled("Enabled by default") ); } }
Kode kelas lengkap tersedia di sini.
Menggunakan anotasi baru
Sekarang terapkan anotasi kami ke kelas tes :
@SpringBootTest @TestEnabledPrefix(property = "app.skip.test") public class SkiptestApplicationTests { @TestEnabled(property = "first") @Test public void testFirst() { assertTrue(true); } @TestEnabled(property = "second") @Test public void testSecond() { assertTrue(false); } }
Sekarang kode pengujian kami lebih bersih dan sederhana.
Saya ingin mengucapkan terima kasih kepada pengguna reddit atas saran mereka:
1) dpash untuk saran
2) BoyRobot777 untuk saran
PS
Artikel ini adalah terjemahan. Versi bahasa Inggris diterbitkan dalam file README.md di sebelah kode proyek.