рдРрд╕реА рд╕реНрдерд┐рддрд┐ рдХреА рдХрд▓реНрдкрдирд╛ рдХрд░реЗрдВ рдЬрд╣рд╛рдВ рдЖрдкрдХреА рдкрд░рд┐рдпреЛрдЬрдирд╛ рдХреЛ рд╡рд┐рднрд┐рдиреНрди рд╡рд╛рддрд╛рд╡рд░рдгреЛрдВ рдореЗрдВ рд╕рдВрдХрд▓рд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред
рдЕрдм рдХрд▓реНрдкрдирд╛ рдХрд░реЗрдВ рдХрд┐ рдЗрди рд╡рд╛рддрд╛рд╡рд░рдгреЛрдВ рдореЗрдВ рд╕рднреА рдкрд░реАрдХреНрд╖рдг рдкрд╛рд╕ рдирд╣реАрдВ рд╣реЛрдиреЗ рдЪрд╛рд╣рд┐рдП - рдкреНрд░рддреНрдпреЗрдХ рдХреЗ рдкрд╛рд╕ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХрд╛ рдЕрдкрдирд╛ рд╕реЗрдЯ рд╣реИред
рдФрд░ рдпрд╣ рдкрд╕рдВрдж рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмреЗрд╣рддрд░ рд╣реИ рдХрд┐ рдХреМрди рд╕рд╛ рдкрд░реАрдХреНрд╖рдг рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП ... application.properties
рдлрд╝рд╛рдЗрд▓ - рдкреНрд░рддреНрдпреЗрдХ рдкрд░реАрдХреНрд╖рдг рдХрд╛ рдЕрдкрдирд╛ / рдмрдВрдж рд╕реНрд╡рд┐рдЪ рд╣реИред
рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд▓рдЧрддрд╛ рд╣реИ, рд╣реИ рдирд╛?
рдлрд┐рд░ рдмрд┐рд▓реНрд▓реА рдХрд╛ рд╕реНрд╡рд╛рдЧрдд рдХрд░рддреЗ рд╣реИрдВ, рдЬрд╣рд╛рдВ рд╣рдо рд╕реНрдкреНрд░рд┐рдВрдЧрдмреВрдЯ 2 рдФрд░ рдЬреБрдирд╛рдЗрдЯ 5 рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдпрд╣ рд╕рдм рд▓рд╛рдЧреВ рдХрд░рддреЗ рд╣реИрдВред
рдкреНрд░реАрд╕реЗрдЯ
рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, JUnit 4 рдХреЛ рдмрдВрдж рдХрд░реЗрдВ, рдЬреЛ рдХрд┐ рд╕реНрдкреНрд░рд┐рдВрдЧрдмреВрдЯ 2 рдореЗрдВ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдЖрддрд╛ рд╣реИ, рдФрд░ JUnit 5 рдХреЛ рдЪрд╛рд▓реВ рдХрд░реЗрдВред
рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, 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>
рдкреНрд░рд╕реНрддрд╛рд╡рд┐рдд рд╕рдорд╛рдзрд╛рди
рд╣рдо рдкреНрд░рддреНрдпреЗрдХ рдкрд░реАрдХреНрд╖рдг рдХреЛ рдПрдХ рд╕рд░рд▓ рдПрдиреЛрдЯреЗрд╢рди рдХреЗ рд╕рд╛рде рдПрдХ рд╕рдВрдкрддреНрддрд┐ рдХреЗ рд╕рд╛рде рдПрдиреЛрдЯреЗрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдЬреЛ рдпрд╣ рджрд░реНрд╢рд╛рддрд╛ рд╣реИ рдХрд┐ рдкрд░реАрдХреНрд╖рдг рд╕рдХреНрд╖рдо рд╣реИ рдпрд╛ рдирд╣реАрдВред рдпрд╛рдж рд░рдЦреЗрдВ рдХрд┐ рд╣рдо рдЗрд╕ рдкреНрд░реЙрдкрд░реНрдЯреА рдХреЗ рдореВрд▓реНрдпреЛрдВ рдХреЛ application.properties
рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рд╕реНрдЯреЛрд░ рдХрд░рдиреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВред
рдЕрдореВрд░реНрдд
рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдмрдирд╛рдПрдВ:
@Retention(RetentionPolicy.RUNTIME) @ExtendWith(TestEnabledCondition.class) public @interface TestEnabled { String property(); }
рдПрдиреЛрдЯреЗрд╢рди рдкреНрд░реЛрд╕реЗрд╕рд┐рдВрдЧ
рдПрдХ рдПрдиреЛрдЯреЗрд╢рди рд╣реИрдВрдбрд▓рд░ рдЕрдкрд░рд┐рд╣рд╛рд░реНрдп рд╣реИред
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") ); } }
рдЖрдкрдХреЛ рдПрдХ рд╡рд░реНрдЧ рдмрдирд╛рдирд╛ рд╣реЛрдЧрд╛ (рдмрд┐рдирд╛ рд╕реНрдкреНрд░рд┐рдВрдЧ рдХрд╛ рдПрдиреЛрдЯреЗрд╢рди @Component
) рдЬреЛ ExecutionCondition
рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реИред
рдЗрд╕ рд╡рд░реНрдЧ рдореЗрдВ, рдЖрдкрдХреЛ рдЗрд╕ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреА рдПрдХ рд╡рд┐рдзрд┐ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ - ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context)
ред
рдпрд╣ рд╡рд┐рдзрд┐ JUnit рджреНрд╡рд╛рд░рд╛ рдЪрд▓рд╛рдП рдЬрд╛ рд░рд╣реЗ рдкрд░реАрдХреНрд╖рдг рдХрд╛ рд╕рдВрджрд░реНрдн рд▓реЗрддреА рд╣реИ рдФрд░ рдпрд╣ рд╢рд░реНрдд рд▓реМрдЯрд╛рддреА рд╣реИ рдХрд┐ рдкрд░реАрдХреНрд╖рдг рдЪрд▓рд╛рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП рдпрд╛ рдирд╣реАрдВред
рдЖрдзрд┐рдХрд╛рд░рд┐рдХ рджрд╕реНрддрд╛рд╡реЗрдЬ рдореЗрдВ JUnit5 рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рд╕рд╢рд░реНрдд рдирд┐рд╖реНрдкрд╛рджрди рдкрд░ рдЕрдзрд┐рдХ рдкрдврд╝реЗрдВред
рд▓реЗрдХрд┐рди рд╣рдо рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ application.properties
рдореЗрдВ рджрд░реНрдЬ рдХреА рдЧрдИ рд╕рдВрдкрддреНрддрд┐ рдХреЗ рдореВрд▓реНрдп рдХреА рдЬрд╛рдВрдЪ рдХреИрд╕реЗ рдХрд░рддреЗ рд╣реИрдВ?
JUnit рдкреНрд░рд╕рдВрдЧ рд╕реЗ рд╡рд╕рдВрдд рд╕рдВрджрд░реНрдн рддрдХ рдкрд╣реБрдБрдЪ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛
рдЗрд╕ рддрд░рд╣ рд╣рдо рд╕реНрдкреНрд░рд┐рдВрдЧ рдкрд░реНрдпрд╛рд╡рд░рдг рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬрд┐рд╕рдХреЗ рд╕рд╛рде рд╣рдорд╛рд░рд╛ рдЬреЗрдиреЗрдЯ рдкрд░реАрдХреНрд╖рдг рд╡рд┐рд╕реНрддрд╛рд░рд╛рдХреЛрдЯреЗрдХреНрд╕реНрдЯ рд╕реЗ рдЪрд▓рд╛рдпрд╛ рдЧрдпрд╛ рдерд╛ред
Environment environment = SpringExtension.getApplicationContext(context).getEnvironment();
рдЖрдк TestEnabledCondition рд╡рд░реНрдЧ рдХреЗ рдкреВрд░реНрдг рдХреЛрдб рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓ рд╕рдХрддреЗ рд╣реИрдВред
рдЯреЗрд╕реНрдЯ рдмрдирд╛рдПрдВ
рдЖрдЗрдП рдХреБрдЫ рдкрд░реАрдХреНрд╖рдг рдмрдирд╛рдПрдВ рдФрд░ рдЙрдирдХреЗ рд▓реЙрдиреНрдЪ рдХреЛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВ:
@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); } }
рд╣рдорд╛рд░реА application.properties
рдлрд╝рд╛рдЗрд▓ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрд╛рдИ рджреЗрддреА рд╣реИ:
app.skip.test.first=true app.skip.test.second=false
рдЗрд╕рд▓рд┐рдП ...
рд╕реНрдЯрд╛рд░реНрдЯрдЕрдк рдкрд░рд┐рдгрд╛рдо:

рдЕрдЧрд▓рд╛ рдХрджрдо рд╣рдорд╛рд░реА рд╕рдВрдкрддреНрддрд┐рдпреЛрдВ рдХреЗ рдЙрдкрд╕рд░реНрдЧреЛрдВ рдХреЛ рд╡рд░реНрдЧ рдПрдиреЛрдЯреЗрд╢рди рдореЗрдВ рдЕрд▓рдЧ рдХрд░рдирд╛ рд╣реИ
рдкреНрд░рддреНрдпреЗрдХ рдкрд░реАрдХреНрд╖рдг рд╕реЗ рдкрд╣рд▓реЗ application.properties
рд╕реЗ рдкреВрд░реА рд╕рдВрдкрддреНрддрд┐ рдХреЗ рдирд╛рдо рд▓рд┐рдЦрдирд╛ рдПрдХ рдХрдард┐рди рдХрд╛рдо рд╣реИред рдЗрд╕рд▓рд┐рдП, рдЙрдирдХреЗ рдЙрдкрд╕рд░реНрдЧ рдХреЛ рдкрд░реАрдХреНрд╖рдг рд╡рд░реНрдЧ рдХреЗ рд╕реНрддрд░ рдкрд░ - рдПрдХ рдЕрд▓рдЧ рдПрдиреЛрдЯреЗрд╢рди рдореЗрдВ рд░рдЦрдирд╛ рдЙрдЪрд┐рдд рд╣реИред
рдЙрдкрд╕рд░реНрдЧреЛрдВ рдХреЛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдиреЛрдЯреЗрд╢рди рдмрдирд╛рдПрдВ - TestEnabledPrefix
:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestEnabledPrefix { String prefix(); }
TestEnabledPrefix рдПрдиреЛрдЯреЗрд╢рди рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдФрд░ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛
рд╣рдо рдирдП рдПрдиреЛрдЯреЗрд╢рди рдХреЗ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд▓рд┐рдП рдЖрдЧреЗ рдмрдврд╝рддреЗ рд╣реИрдВред
рдЖрдЗрдП рдПрдиреЛрдЯреЗрд╢рдирдбреЗрд╕рдХреНрд░рд┐рдкреНрд╢рди рд╣реЗрд▓реНрдкрд░ рдХреНрд▓рд╛рд╕ рдмрдирд╛рдПрдВ
рдЗрд╕ рд╡рд░реНрдЧ рдХреЗ рд╕рд╛рде, рд╣рдо application.properties
рдФрд░ рдЙрд╕рдХреЗ рдореВрд▓реНрдп рд╕реЗ рд╕рдВрдкрддреНрддрд┐ рдХрд╛ рдирд╛рдо рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
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; } } }
рдпрд╣ рд╡рд░реНрдЧ рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧреА рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╣рдо рд▓реИрдореНрдмрдбрд╛ рдПрдХреНрд╕рдкреНрд░реЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВред
рдЪрд▓рд┐рдП рдПрдХ рд╡рд┐рдзрд┐ рдмрдирд╛рддреЗ рд╣реИрдВ рдЬреЛ 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); } }
рдФрд░ рдЕрдм рдЪрд▓реЛ рдкрд░реАрдХреНрд╖рдг рдПрдиреЛрдЯреЗрд╢рди рдореЗрдВ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдирд╛рдо рд╕реЗ application.properties
рд╕реЗ рд╕рдВрдкрддреНрддрд┐ рдХреЗ рдореВрд▓реНрдп рдХреА рдЬрд╛рдВрдЪ рдХрд░реЗрдВ
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") ); } }
рдкреВрд░рд╛ рдХреНрд▓рд╛рд╕ рдХреЛрдб рдпрд╣рд╛рдБ рдЙрдкрд▓рдмреНрдз рд╣реИред
рдирдП рдПрдиреЛрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛
рдЕрдм рд╣рдорд╛рд░реЗ рдПрдиреЛрдЯреЗрд╢рди рдХреЛ рдЯреЗрд╕реНрдЯ рдХреНрд▓рд╛рд╕ рдореЗрдВ рд▓рд╛рдЧреВ рдХрд░реЗрдВ:
@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); } }
рдЕрдм рд╣рдорд╛рд░рд╛ рдкрд░реАрдХреНрд╖рдг рдХреЛрдб рдХреНрд▓реАрдирд░ рдФрд░ рд╕рд░рд▓ рд╣реИред
рдореИрдВ Reddit рдХреЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рдЙрдирдХреА рд╕рд▓рд╛рд╣ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж рджреЗрдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ:
1) рд╕рд▓рд╛рд╣ рдХреЗ рд▓рд┐рдП dpash
2) рд╕рд▓рд╛рд╣ рдХреЗ рд▓рд┐рдП BoyRobot777
рдкреБрдирд╢реНрдЪ
рд▓реЗрдЦ рдПрдХ рдЕрдиреБрд╡рд╛рдж рд╣реИред рдЕрдВрдЧреНрд░реЗрдЬреА рд╕рдВрд╕реНрдХрд░рдг рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдХреЛрдб рдХреЗ рдЖрдЧреЗ README.md рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдкреНрд░рдХрд╛рд╢рд┐рдд рд╣реБрдЖ рд╣реИред