├── .gitignore ├── LICENSE ├── README.md ├── annotation ├── pom.xml └── src │ ├── main │ ├── java │ │ └── me │ │ │ └── chanjar │ │ │ └── annotation │ │ │ ├── activeprofiles │ │ │ ├── Bar.java │ │ │ ├── Config.java │ │ │ └── Foo.java │ │ │ ├── overrideac │ │ │ └── AutoConfigurationEnableLogger.java │ │ │ └── testconfig │ │ │ └── Foo.java │ └── resources │ │ └── META-INF │ │ └── spring.factories │ └── test │ ├── java │ └── me │ │ └── chanjar │ │ └── annotation │ │ ├── activeprofiles │ │ ├── ex1 │ │ │ └── ActiveProfileTest.java │ │ └── ex2 │ │ │ └── ActiveProfileTest.java │ │ ├── jsontest │ │ ├── ex1 │ │ │ ├── Foo.java │ │ │ └── SimpleJsonTest.java │ │ ├── ex2 │ │ │ ├── FooJsonComponent.java │ │ │ └── JsonComponentJacksonTest.java │ │ └── ex3 │ │ │ └── ThinJsonTest.java │ │ ├── overrideac │ │ ├── ex1 │ │ │ └── BootTest.java │ │ └── ex2 │ │ │ └── BootTest.java │ │ ├── testconfig │ │ ├── ex1 │ │ │ └── TestConfigurationTest.java │ │ ├── ex2 │ │ │ ├── Config.java │ │ │ ├── TestConfig.java │ │ │ └── TestConfigurationTest.java │ │ └── ex3 │ │ │ ├── ExcludeConfig1.java │ │ │ ├── ExcludeConfig2.java │ │ │ ├── IncludeConfig.java │ │ │ ├── TestConfig.java │ │ │ ├── TestConfigExclude_1_Test.java │ │ │ ├── TestConfigExclude_2_Test.java │ │ │ └── TestConfigIncludedTest.java │ │ └── testps │ │ ├── ex1 │ │ ├── PropertySourceConfig.java │ │ └── TestPropertyTest.java │ │ └── ex2 │ │ └── TestPropertyTest.java │ └── resources │ └── me │ └── chanjar │ └── annotation │ ├── jsontest │ └── ex1 │ │ └── expected.json │ └── testps │ └── ex1 │ ├── property-source.properties │ └── test-property-source.properties ├── aop ├── pom.xml └── src │ ├── main │ └── java │ │ └── me │ │ └── chanjar │ │ └── aop │ │ ├── aspect │ │ └── FooAspect.java │ │ ├── config │ │ └── AopConfig.java │ │ └── service │ │ ├── FooService.java │ │ └── FooServiceImpl.java │ └── test │ └── java │ └── me │ └── chanjar │ └── aop │ ├── ex1 │ └── SpringAop_1_Test.java │ ├── ex2 │ └── SpringAop_2_Test.java │ └── ex3 │ └── SpringBootAopTest.java ├── appendix_i.md ├── appendix_ii.md ├── basic ├── pom.xml └── src │ ├── main │ └── java │ │ └── me │ │ └── chanjar │ │ └── basic │ │ └── service │ │ ├── FooService.java │ │ └── FooServiceImpl.java │ └── test │ └── java │ └── me │ └── chanjar │ └── basic │ ├── spring │ ├── ex1 │ │ └── FooServiceImplTest.java │ ├── ex2 │ │ └── FooServiceImplTest.java │ └── ex3 │ │ ├── Config.java │ │ └── FooServiceImplTest.java │ ├── springboot │ ├── ex1 │ │ └── FooServiceImplTest.java │ ├── ex2 │ │ └── FooServiceImplTest.java │ ├── ex3 │ │ ├── Config.java │ │ └── FooServiceImplTest.java │ ├── ex4 │ │ ├── Config.java │ │ └── FooServiceImplTest.java │ ├── ex5 │ │ ├── Config.java │ │ └── FooServiceImplTest.java │ └── ex6 │ │ ├── Config.java │ │ └── FooServiceImplTest.java │ └── testng │ └── ex1 │ └── FooServiceImplTest.java ├── chapter_0_concept.md ├── chapter_1_intro.md ├── chapter_1_s1_testng.md ├── chapter_1_s2_spring_testing.md ├── chapter_1_s3_spring_boot_testing.md ├── chapter_2_intro.md ├── chapter_2_s1_test_property_source.md ├── chapter_2_s2_active_profile.md ├── chapter_2_s3_json_test.md ├── chapter_2_s4_override_auto_configuration.md ├── chapter_2_s5_test_configuration.md ├── chapter_3_mockito.md ├── chapter_4_s1_basic.md ├── chapter_4_s2_using_docker.md ├── chapter_5_mvc.md ├── chapter_6_aop.md ├── chapter_7_configuration.md ├── chapter_8_share_test_config.md ├── configuration ├── pom.xml └── src │ ├── main │ └── java │ │ └── me │ │ └── chanjar │ │ └── configuration │ │ └── service │ │ ├── Bar.java │ │ └── Foo.java │ └── test │ └── java │ └── me │ └── chanjar │ └── configuration │ ├── ex1 │ ├── FooConfiguration.java │ └── FooConfigurationTest.java │ ├── ex2 │ ├── FooConfiguration.java │ └── FooConfigurationTest.java │ ├── ex3 │ ├── FooConfiguration.java │ └── FooConfigurationTest.java │ └── ex4 │ ├── BarConfiguration.java │ └── BarConfigurationTest.java ├── mock ├── pom.xml └── src │ ├── main │ └── java │ │ └── me │ │ └── chanjar │ │ └── common │ │ ├── Bar.java │ │ ├── Foo.java │ │ └── FooImpl.java │ └── test │ └── java │ └── me │ └── chanjar │ ├── mockito │ └── MockitoTest.java │ ├── no_mock │ └── NoMockTest.java │ ├── spring1 │ └── Spring_1_Test.java │ ├── spring2 │ ├── Loo.java │ ├── LooImpl.java │ └── Spring_2_Test.java │ └── springboot1 │ └── Boot_1_Test.java ├── mvc ├── pom.xml └── src │ ├── main │ └── java │ │ └── me │ │ └── chanjar │ │ └── web │ │ ├── Foo.java │ │ ├── FooController.java │ │ └── FooImpl.java │ └── test │ └── java │ └── me │ └── chanjar │ ├── spring1 │ └── SpringMvc_1_Test.java │ ├── spring2 │ └── SpringMvc_2_Test.java │ ├── springboot1 │ └── BootMvc_1_Test.java │ └── springboot2 │ └── BootMvc_2_Test.java ├── pom.xml ├── rdbs-docker ├── pom.xml └── src │ ├── main │ ├── java │ │ └── me │ │ │ └── chanjar │ │ │ └── domain │ │ │ ├── Foo.java │ │ │ ├── FooRepository.java │ │ │ └── FooRepositoryImpl.java │ └── resources │ │ └── db │ │ └── migration │ │ └── V1.0.0__foo-ddl.sql │ └── test │ └── java │ └── me │ └── chanjar │ └── domain │ ├── FooRepositoryImplIT.java │ └── FooTest.java ├── rdbs ├── pom.xml └── src │ ├── main │ ├── java │ │ └── me │ │ │ └── chanjar │ │ │ └── domain │ │ │ ├── Foo.java │ │ │ ├── FooRepository.java │ │ │ └── FooRepositoryImpl.java │ └── resources │ │ ├── db │ │ └── migration │ │ │ └── V1.0.0__foo-ddl.sql │ │ └── me │ │ └── chanjar │ │ └── domain │ │ └── foo-ddl.sql │ └── test │ └── java │ └── me │ └── chanjar │ ├── spring1 │ ├── Spring_1_IT.java │ ├── Spring_1_IT_Configuration.java │ └── index.jsp │ ├── spring2 │ ├── Spring_2_IT.java │ └── Spring_2_IT_Configuration.java │ └── springboot1 │ └── Boot_1_IT.java └── share-config ├── pom.xml └── src ├── main ├── java │ └── me │ │ └── chanjar │ │ └── shareconfig │ │ ├── config │ │ └── FooRepositoryConfiguration.java │ │ └── service │ │ ├── Foo.java │ │ ├── FooRepository.java │ │ └── FooRepositoryImpl.java └── resources │ └── db │ └── migration │ └── V1.0.0__foo-ddl.sql └── test └── java └── me └── chanjar └── shareconfig ├── annotation └── FooRepositoryIT.java ├── configuration └── FooRepositoryIT.java ├── inter └── FooRepositoryIT.java ├── service └── FooRepositoryTestBase.java └── testconfig ├── AnnotationConfiguration.java ├── InterfaceConfiguration.java └── PlainConfiguration.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | 24 | target 25 | *.iml 26 | .project 27 | .classpath 28 | .settings 29 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring、Spring Boot和TestNG测试指南 2 | 3 | Spring、Spring Boot都提供了非常便利的测试工具,但遗憾的是官方文档的大多数例子都是基于JUnit的。本人比较喜欢用TestNG做单元、集成测试,所以开启了本项目收集了在Spring、Spring Boot项目中利用TestNG测试的例子。 4 | 5 | ## 章节列表 6 | 7 | 1. [Chapter 0: 基本概念][chapter_0_concept] 8 | 1. Chapter 1: 基本用法 9 | 1. [引言][chapter_1_intro] 10 | 1. [认识TestNG][chapter_1_s1_testng] 11 | 1. [使用Spring Testing工具][chapter_1_s2_spring_testing] 12 | 1. [使用Spring Boot Testing工具][chapter_1_s3_spring_boot_testing] 13 | 1. Chapter 2: Annotations 14 | 1. [引言][chapter_2_intro] 15 | 1. [@TestPropertySource][chapter_2_s1_test_property_source] 16 | 1. [@ActiveProfile][chapter_2_s2_active_profile] 17 | 1. [@JsonTest][chapter_2_s3_json_test] 18 | 1. [@OverrideAutoConfiguration][chapter_2_s4_override_auto_configuration] 19 | 1. [@TestConfiguration][chapter_2_s5_test_configuration] 20 | 1. [Chapter 3: 使用Mockito][chapter_3_mockito] 21 | 1. Chapter 4: 测试关系型数据库 22 | 1. [基本做法][chapter_4_s1_basic] 23 | 1. [使用Docker创建临时数据库][chapter_4_s2_using_docker] 24 | 1. [Chapter 5: 测试Spring MVC][chapter_5_mvc] 25 | 1. [Chapter 6: 测试AOP][chapter_6_aop] 26 | 1. [Chapter 7: 测试@Configuration][chapter_7_configuration] 27 | 1. [Chapter 8: 共享测试配置][chapter_8_share_test_config] 28 | 1. [附录I Spring Mock Objects][appendix_i] 29 | 1. [附录II Spring Test Utils][appendix_ii] 30 | 31 | 32 | [doc-spring-test-utils]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#unit-testing-support-classes 33 | [chapter_0_concept]: chapter_0_concept.md 34 | 35 | [chapter_1_intro]: chapter_1_intro.md 36 | [chapter_1_s1_testng]: chapter_1_s1_testng.md 37 | [chapter_1_s2_spring_testing]: chapter_1_s2_spring_testing.md 38 | [chapter_1_s3_spring_boot_testing]: chapter_1_s3_spring_boot_testing.md 39 | 40 | [chapter_2_intro]: chapter_2_intro.md 41 | [chapter_2_s1_test_property_source]: chapter_2_s1_test_property_source.md 42 | [chapter_2_s2_active_profile]: chapter_2_s2_active_profile.md 43 | [chapter_2_s3_json_test]: chapter_2_s3_json_test.md 44 | [chapter_2_s4_override_auto_configuration]: chapter_2_s4_override_auto_configuration.md 45 | [chapter_2_s5_test_configuration]: chapter_2_s5_test_configuration.md 46 | 47 | [chapter_3_mockito]: chapter_3_mockito.md 48 | 49 | [chapter_4_s1_basic]: chapter_4_s1_basic.md 50 | [chapter_4_s2_using_docker]: chapter_4_s2_using_docker.md 51 | 52 | [chapter_5_mvc]: chapter_5_mvc.md 53 | [chapter_6_aop]: chapter_6_aop.md 54 | [chapter_7_configuration]: chapter_7_configuration.md 55 | [chapter_8_share_test_config]: chapter_8_share_test_config.md 56 | [appendix_i]: appendix_i.md 57 | [appendix_ii]: appendix_ii.md 58 | -------------------------------------------------------------------------------- /annotation/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | me.chanjar 7 | spring-test-examples-aggregator 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | me.chanjar 12 | spring-test-examples-annotation 13 | jar 14 | 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-autoconfigure 26 | 27 | 28 | 29 | org.springframework 30 | spring-web 31 | 32 | 33 | 34 | org.slf4j 35 | slf4j-api 36 | 37 | 38 | 39 | ch.qos.logback 40 | logback-classic 41 | 42 | 43 | 44 | org.apache.commons 45 | commons-lang3 46 | 47 | 48 | 49 | org.apache.commons 50 | commons-collections4 51 | 52 | 53 | 54 | joda-time 55 | joda-time 56 | 57 | 58 | 59 | com.fasterxml.jackson.core 60 | jackson-core 61 | 62 | 63 | 64 | com.fasterxml.jackson.core 65 | jackson-databind 66 | 67 | 68 | 69 | com.fasterxml.jackson.datatype 70 | jackson-datatype-joda 71 | 72 | 73 | 74 | com.fasterxml.jackson.datatype 75 | jackson-datatype-jsr310 76 | 77 | 78 | 79 | 80 | 81 | org.testng 82 | testng 83 | test 84 | 85 | 86 | 87 | org.springframework.boot 88 | spring-boot-starter-test 89 | test 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.apache.maven.plugins 98 | maven-jar-plugin 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /annotation/src/main/java/me/chanjar/annotation/activeprofiles/Bar.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.activeprofiles; 2 | 3 | public class Bar { 4 | 5 | private final String name; 6 | 7 | public Bar(String name) { 8 | this.name = name; 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /annotation/src/main/java/me/chanjar/annotation/activeprofiles/Config.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.activeprofiles; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.Profile; 6 | 7 | @Configuration 8 | public class Config { 9 | 10 | @Bean 11 | @Profile("dev") 12 | public Foo fooDev() { 13 | return new Foo("dev"); 14 | } 15 | 16 | @Bean 17 | @Profile("product") 18 | public Foo fooProduct() { 19 | return new Foo("product"); 20 | } 21 | 22 | @Bean 23 | @Profile("default") 24 | public Foo fooDefault() { 25 | return new Foo("default"); 26 | } 27 | 28 | @Bean 29 | public Bar bar() { 30 | return new Bar("no profile"); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /annotation/src/main/java/me/chanjar/annotation/activeprofiles/Foo.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.activeprofiles; 2 | 3 | public class Foo { 4 | 5 | private final String name; 6 | 7 | public Foo(String name) { 8 | this.name = name; 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /annotation/src/main/java/me/chanjar/annotation/overrideac/AutoConfigurationEnableLogger.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.overrideac; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | public class AutoConfigurationEnableLogger { 9 | 10 | private static final Logger LOGGER = LoggerFactory.getLogger(AutoConfigurationEnableLogger.class); 11 | 12 | public AutoConfigurationEnableLogger() { 13 | LOGGER.info("Auto Configuration Enabled"); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /annotation/src/main/java/me/chanjar/annotation/testconfig/Foo.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testconfig; 2 | 3 | public class Foo { 4 | 5 | private final String name; 6 | 7 | public Foo(String name) { 8 | this.name = name; 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /annotation/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | me.chanjar.annotation.overrideac.AutoConfigurationEnableLogger 3 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/activeprofiles/ex1/ActiveProfileTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.activeprofiles.ex1; 2 | 3 | import me.chanjar.annotation.activeprofiles.Bar; 4 | import me.chanjar.annotation.activeprofiles.Config; 5 | import me.chanjar.annotation.activeprofiles.Foo; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.test.context.ContextConfiguration; 8 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 9 | import org.testng.annotations.Test; 10 | 11 | import static org.testng.Assert.assertEquals; 12 | 13 | @ContextConfiguration(classes = Config.class) 14 | public class ActiveProfileTest extends AbstractTestNGSpringContextTests { 15 | 16 | @Autowired 17 | private Foo foo; 18 | 19 | @Autowired 20 | private Bar bar; 21 | 22 | @Test 23 | public void test() { 24 | assertEquals(foo.getName(), "default"); 25 | assertEquals(bar.getName(), "no profile"); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/activeprofiles/ex2/ActiveProfileTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.activeprofiles.ex2; 2 | 3 | import me.chanjar.annotation.activeprofiles.Bar; 4 | import me.chanjar.annotation.activeprofiles.Config; 5 | import me.chanjar.annotation.activeprofiles.Foo; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.test.context.ActiveProfiles; 8 | import org.springframework.test.context.ContextConfiguration; 9 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 10 | import org.testng.annotations.Test; 11 | 12 | import static org.testng.Assert.assertEquals; 13 | 14 | @ContextConfiguration(classes = Config.class) 15 | @ActiveProfiles("product") 16 | public class ActiveProfileTest extends AbstractTestNGSpringContextTests { 17 | 18 | @Autowired 19 | private Foo foo; 20 | 21 | @Autowired 22 | private Bar bar; 23 | 24 | @Test 25 | public void test() { 26 | assertEquals(foo.getName(), "product"); 27 | assertEquals(bar.getName(), "no profile"); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/jsontest/ex1/Foo.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.jsontest.ex1; 2 | 3 | import org.apache.commons.lang3.builder.ToStringBuilder; 4 | 5 | /** 6 | * Created by qianjia on 2017/7/18. 7 | */ 8 | public class Foo { 9 | 10 | private String name; 11 | 12 | private int age; 13 | 14 | public Foo() { 15 | } 16 | 17 | public Foo(String name, int age) { 18 | this.name = name; 19 | this.age = age; 20 | } 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public void setName(String name) { 27 | this.name = name; 28 | } 29 | 30 | public int getAge() { 31 | return age; 32 | } 33 | 34 | public void setAge(int age) { 35 | this.age = age; 36 | } 37 | 38 | @Override 39 | public boolean equals(Object o) { 40 | if (this == o) return true; 41 | if (o == null || getClass() != o.getClass()) return false; 42 | 43 | Foo foo = (Foo) o; 44 | 45 | if (age != foo.age) return false; 46 | return name != null ? name.equals(foo.name) : foo.name == null; 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | int result = name != null ? name.hashCode() : 0; 52 | result = 31 * result + age; 53 | return result; 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return new ToStringBuilder(this) 59 | .append("name", name) 60 | .append("age", age) 61 | .toString(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/jsontest/ex1/SimpleJsonTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.jsontest.ex1; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.boot.test.json.JacksonTester; 7 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 8 | import org.testng.annotations.Test; 9 | 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | @SpringBootTest(classes = SimpleJsonTest.class) 13 | @JsonTest 14 | public class SimpleJsonTest extends AbstractTestNGSpringContextTests { 15 | 16 | @Autowired 17 | private JacksonTester json; 18 | 19 | @Test 20 | public void testSerialize() throws Exception { 21 | Foo details = new Foo("Honda", 12); 22 | // 使用通包下的json文件测试结果是否正确 23 | assertThat(this.json.write(details)).isEqualToJson("expected.json"); 24 | // 或者使用基于JSON path的校验 25 | assertThat(this.json.write(details)).hasJsonPathStringValue("@.name"); 26 | assertThat(this.json.write(details)).extractingJsonPathStringValue("@.name").isEqualTo("Honda"); 27 | assertThat(this.json.write(details)).hasJsonPathNumberValue("@.age"); 28 | assertThat(this.json.write(details)).extractingJsonPathNumberValue("@.age").isEqualTo(12); 29 | } 30 | 31 | @Test 32 | public void testDeserialize() throws Exception { 33 | String content = "{\"name\":\"Ford\",\"age\":13}"; 34 | Foo actual = this.json.parseObject(content); 35 | assertThat(actual).isEqualTo(new Foo("Ford", 13)); 36 | assertThat(actual.getName()).isEqualTo("Ford"); 37 | assertThat(actual.getAge()).isEqualTo(13); 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/jsontest/ex2/FooJsonComponent.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.jsontest.ex2; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.core.JsonProcessingException; 6 | import com.fasterxml.jackson.core.JsonToken; 7 | import com.fasterxml.jackson.databind.DeserializationContext; 8 | import com.fasterxml.jackson.databind.JsonDeserializer; 9 | import com.fasterxml.jackson.databind.JsonSerializer; 10 | import com.fasterxml.jackson.databind.SerializerProvider; 11 | import me.chanjar.annotation.jsontest.ex1.Foo; 12 | import org.springframework.boot.jackson.JsonComponent; 13 | 14 | import java.io.IOException; 15 | 16 | /** 17 | * Created by qianjia on 2017/7/19. 18 | */ 19 | @JsonComponent 20 | public class FooJsonComponent { 21 | 22 | public static class Serializer extends JsonSerializer { 23 | @Override 24 | public void serialize(Foo value, JsonGenerator gen, SerializerProvider serializers) 25 | throws IOException, JsonProcessingException { 26 | gen.writeString("name=" + value.getName() + ",age=" + value.getAge()); 27 | } 28 | 29 | } 30 | 31 | public static class Deserializer extends JsonDeserializer { 32 | 33 | @Override 34 | public Foo deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 35 | JsonToken t = p.getCurrentToken(); 36 | 37 | if (t == JsonToken.VALUE_STRING) { 38 | String trim = p.getText().trim(); 39 | 40 | String[] split = trim.split(","); 41 | Foo foo = new Foo(); 42 | foo.setName(split[0].split("=")[1]); 43 | foo.setAge(Integer.parseInt(split[1].split("=")[1])); 44 | return foo; 45 | } 46 | 47 | return (Foo) ctxt.handleUnexpectedToken(handledType(), p); 48 | 49 | } 50 | 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/jsontest/ex2/JsonComponentJacksonTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.jsontest.ex2; 2 | 3 | import me.chanjar.annotation.jsontest.ex1.Foo; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.boot.test.json.JacksonTester; 8 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 9 | import org.testng.annotations.Test; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | @SpringBootTest(classes = { JsonComponentJacksonTest.class, FooJsonComponent.class }) 14 | @JsonTest 15 | public class JsonComponentJacksonTest extends AbstractTestNGSpringContextTests { 16 | 17 | @Autowired 18 | private JacksonTester json; 19 | 20 | @Test 21 | public void testSerialize() throws Exception { 22 | Foo details = new Foo("Honda", 12); 23 | assertThat(this.json.write(details).getJson()).isEqualTo("\"name=Honda,age=12\""); 24 | } 25 | 26 | @Test 27 | public void testDeserialize() throws Exception { 28 | String content = "\"name=Ford,age=13\""; 29 | Foo actual = this.json.parseObject(content); 30 | assertThat(actual).isEqualTo(new Foo("Ford", 13)); 31 | assertThat(actual.getName()).isEqualTo("Ford"); 32 | assertThat(actual.getAge()).isEqualTo(13); 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/jsontest/ex3/ThinJsonTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.jsontest.ex3; 2 | 3 | import me.chanjar.annotation.jsontest.ex1.Foo; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.autoconfigure.json.JsonTest; 6 | import org.springframework.boot.test.json.JacksonTester; 7 | import org.springframework.test.context.ContextConfiguration; 8 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 9 | import org.testng.annotations.Test; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | @JsonTest 14 | @ContextConfiguration(classes = ThinJsonTest.class) 15 | public class ThinJsonTest extends AbstractTestNGSpringContextTests { 16 | 17 | @Autowired 18 | private JacksonTester json; 19 | 20 | @Test 21 | public void testSerialize() throws Exception { 22 | Foo details = new Foo("Honda", 12); 23 | // 或者使用基于JSON path的校验 24 | assertThat(this.json.write(details)).hasJsonPathStringValue("@.name"); 25 | assertThat(this.json.write(details)).extractingJsonPathStringValue("@.name").isEqualTo("Honda"); 26 | assertThat(this.json.write(details)).hasJsonPathNumberValue("@.age"); 27 | assertThat(this.json.write(details)).extractingJsonPathNumberValue("@.age").isEqualTo(12); 28 | } 29 | 30 | @Test 31 | public void testDeserialize() throws Exception { 32 | String content = "{\"name\":\"Ford\",\"age\":13}"; 33 | Foo actual = this.json.parseObject(content); 34 | assertThat(actual).isEqualTo(new Foo("Ford", 13)); 35 | assertThat(actual.getName()).isEqualTo("Ford"); 36 | assertThat(actual.getAge()).isEqualTo(13); 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/overrideac/ex1/BootTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.overrideac.ex1; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 6 | import org.testng.annotations.Test; 7 | 8 | @SpringBootTest 9 | @SpringBootApplication 10 | public class BootTest extends AbstractTestNGSpringContextTests { 11 | 12 | @Test 13 | public void testName() throws Exception { 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/overrideac/ex2/BootTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.overrideac.ex2; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 7 | import org.testng.annotations.Test; 8 | 9 | @SpringBootTest 10 | @OverrideAutoConfiguration(enabled = false) 11 | @SpringBootApplication 12 | public class BootTest extends AbstractTestNGSpringContextTests { 13 | 14 | @Test 15 | public void testName() throws Exception { 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testconfig/ex1/TestConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testconfig.ex1; 2 | 3 | import me.chanjar.annotation.testconfig.Foo; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.SpringBootConfiguration; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.boot.test.context.TestConfiguration; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 10 | import org.testng.annotations.Test; 11 | 12 | import static org.testng.Assert.assertEquals; 13 | 14 | @SpringBootTest 15 | @SpringBootConfiguration 16 | public class TestConfigurationTest extends AbstractTestNGSpringContextTests { 17 | 18 | @Autowired 19 | private Foo foo; 20 | 21 | @Test 22 | public void testPlusCount() throws Exception { 23 | assertEquals(foo.getName(), "from test config"); 24 | } 25 | 26 | @TestConfiguration 27 | public class TestConfig { 28 | 29 | @Bean 30 | public Foo foo() { 31 | return new Foo("from test config"); 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testconfig/ex2/Config.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testconfig.ex2; 2 | 3 | import me.chanjar.annotation.testconfig.Foo; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | public class Config { 9 | 10 | @Bean 11 | public Foo foo() { 12 | return new Foo("from config"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testconfig/ex2/TestConfig.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testconfig.ex2; 2 | 3 | import me.chanjar.annotation.testconfig.Foo; 4 | import org.springframework.boot.test.context.TestConfiguration; 5 | import org.springframework.context.annotation.Bean; 6 | 7 | @TestConfiguration 8 | public class TestConfig { 9 | 10 | @Bean 11 | public Foo foo() { 12 | return new Foo("from test config"); 13 | } 14 | 15 | @Bean 16 | public Foo foo2() { 17 | return new Foo("from test config2"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testconfig/ex2/TestConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testconfig.ex2; 2 | 3 | import me.chanjar.annotation.testconfig.Foo; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.annotation.Qualifier; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 8 | import org.testng.annotations.Test; 9 | 10 | import static org.testng.Assert.assertEquals; 11 | 12 | @SpringBootTest(classes = { Config.class, TestConfig.class }) 13 | public class TestConfigurationTest extends AbstractTestNGSpringContextTests { 14 | 15 | @Qualifier("foo") 16 | @Autowired 17 | private Foo foo; 18 | 19 | @Qualifier("foo2") 20 | @Autowired 21 | private Foo foo2; 22 | 23 | @Test 24 | public void testPlusCount() throws Exception { 25 | assertEquals(foo.getName(), "from test config"); 26 | assertEquals(foo2.getName(), "from test config2"); 27 | 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/ExcludeConfig1.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testconfig.ex3; 2 | 3 | import org.springframework.boot.SpringBootConfiguration; 4 | import org.springframework.boot.context.TypeExcludeFilter; 5 | import org.springframework.context.annotation.ComponentScan; 6 | import org.springframework.context.annotation.FilterType; 7 | 8 | @SpringBootConfiguration 9 | @ComponentScan(excludeFilters = { 10 | @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class) 11 | }) 12 | public interface ExcludeConfig1 { 13 | } 14 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/ExcludeConfig2.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testconfig.ex3; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | @SpringBootApplication 6 | public interface ExcludeConfig2 { 7 | } 8 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/IncludeConfig.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testconfig.ex3; 2 | 3 | import org.springframework.boot.SpringBootConfiguration; 4 | import org.springframework.context.annotation.ComponentScan; 5 | 6 | @SpringBootConfiguration 7 | @ComponentScan 8 | public interface IncludeConfig { 9 | } 10 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/TestConfig.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testconfig.ex3; 2 | 3 | import me.chanjar.annotation.testconfig.Foo; 4 | import org.springframework.boot.test.context.TestConfiguration; 5 | import org.springframework.context.annotation.Bean; 6 | 7 | @TestConfiguration 8 | public class TestConfig { 9 | 10 | @Bean 11 | public Foo foo() { 12 | return new Foo("from test config"); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/TestConfigExclude_1_Test.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testconfig.ex3; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 6 | import org.testng.annotations.Test; 7 | 8 | import static org.testng.Assert.assertNull; 9 | 10 | @SpringBootTest(classes = ExcludeConfig1.class) 11 | public class TestConfigExclude_1_Test extends AbstractTestNGSpringContextTests { 12 | 13 | @Autowired(required = false) 14 | private TestConfig testConfig; 15 | 16 | @Test 17 | public void test() throws Exception { 18 | assertNull(testConfig); 19 | 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/TestConfigExclude_2_Test.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testconfig.ex3; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 6 | import org.testng.annotations.Test; 7 | 8 | import static org.testng.Assert.assertNull; 9 | 10 | @SpringBootTest(classes = ExcludeConfig2.class) 11 | public class TestConfigExclude_2_Test extends AbstractTestNGSpringContextTests { 12 | 13 | @Autowired(required = false) 14 | private TestConfig testConfig; 15 | 16 | @Test 17 | public void testPlusCount() throws Exception { 18 | assertNull(testConfig); 19 | 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/TestConfigIncludedTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testconfig.ex3; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 6 | import org.testng.annotations.Test; 7 | 8 | import static org.testng.Assert.assertNotNull; 9 | 10 | @SpringBootTest(classes = IncludeConfig.class) 11 | public class TestConfigIncludedTest extends AbstractTestNGSpringContextTests { 12 | 13 | @Autowired(required = false) 14 | private TestConfig testConfig; 15 | 16 | @Test 17 | public void testPlusCount() throws Exception { 18 | assertNotNull(testConfig); 19 | 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testps/ex1/PropertySourceConfig.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testps.ex1; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.context.annotation.PropertySource; 5 | 6 | @Configuration 7 | @PropertySource("classpath:me/chanjar/annotation/testps/ex1/property-source.properties") 8 | public class PropertySourceConfig { 9 | } 10 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testps/ex1/TestPropertyTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testps.ex1; 2 | 3 | import org.springframework.context.EnvironmentAware; 4 | import org.springframework.core.env.ConfigurableEnvironment; 5 | import org.springframework.core.env.Environment; 6 | import org.springframework.test.context.ContextConfiguration; 7 | import org.springframework.test.context.TestPropertySource; 8 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 9 | import org.testng.annotations.Test; 10 | 11 | import java.util.Map; 12 | 13 | import static java.util.stream.Collectors.toList; 14 | import static org.testng.Assert.assertEquals; 15 | 16 | @ContextConfiguration(classes = PropertySourceConfig.class) 17 | @TestPropertySource( 18 | properties = { "foo=xyz", "bar=uvw", "PATH=aaa", "java.runtime.name=bbb" }, 19 | locations = "classpath:me/chanjar/annotation/testps/ex1/test-property-source.properties" 20 | ) 21 | public class TestPropertyTest extends AbstractTestNGSpringContextTests implements EnvironmentAware { 22 | 23 | private Environment environment; 24 | 25 | @Override 26 | public void setEnvironment(Environment environment) { 27 | this.environment = environment; 28 | Map systemEnvironment = ((ConfigurableEnvironment) environment).getSystemEnvironment(); 29 | System.out.println("=== System Environment ==="); 30 | System.out.println(getMapString(systemEnvironment)); 31 | System.out.println(); 32 | 33 | System.out.println("=== Java System Properties ==="); 34 | Map systemProperties = ((ConfigurableEnvironment) environment).getSystemProperties(); 35 | System.out.println(getMapString(systemProperties)); 36 | } 37 | 38 | @Test 39 | public void testOverridePropertySource() { 40 | assertEquals(environment.getProperty("foo"), "xyz"); 41 | } 42 | 43 | @Test 44 | public void testOverrideSystemEnvironment() { 45 | assertEquals(environment.getProperty("PATH"), "aaa"); 46 | } 47 | 48 | @Test 49 | public void testOverrideJavaSystemProperties() { 50 | assertEquals(environment.getProperty("java.runtime.name"), "bbb"); 51 | } 52 | 53 | @Test 54 | public void testInlineTestPropertyOverrideResourceLocationTestProperty() { 55 | assertEquals(environment.getProperty("bar"), "uvw"); 56 | } 57 | 58 | private String getMapString(Map map) { 59 | return String.join("\n", 60 | map.keySet().stream().map(k -> k + "=" + map.get(k)).collect(toList()) 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /annotation/src/test/java/me/chanjar/annotation/testps/ex2/TestPropertyTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.annotation.testps.ex2; 2 | 3 | import me.chanjar.annotation.testps.ex1.PropertySourceConfig; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import org.springframework.context.EnvironmentAware; 6 | import org.springframework.core.env.ConfigurableEnvironment; 7 | import org.springframework.core.env.Environment; 8 | import org.springframework.test.context.TestPropertySource; 9 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 10 | import org.testng.annotations.Test; 11 | 12 | import java.util.Map; 13 | 14 | import static java.util.stream.Collectors.toList; 15 | import static org.testng.Assert.assertEquals; 16 | 17 | @SpringBootTest(classes = PropertySourceConfig.class) 18 | @TestPropertySource( 19 | properties = { "foo=xyz", "bar=uvw", "PATH=aaa", "java.runtime.name=bbb" }, 20 | locations = "classpath:me/chanjar/annotation/testps/ex1/test-property-source.properties" 21 | ) 22 | public class TestPropertyTest extends AbstractTestNGSpringContextTests implements EnvironmentAware { 23 | 24 | private Environment environment; 25 | 26 | @Override 27 | public void setEnvironment(Environment environment) { 28 | this.environment = environment; 29 | Map systemEnvironment = ((ConfigurableEnvironment) environment).getSystemEnvironment(); 30 | System.out.println("=== System Environment ==="); 31 | System.out.println(getMapString(systemEnvironment)); 32 | System.out.println(); 33 | 34 | System.out.println("=== Java System Properties ==="); 35 | Map systemProperties = ((ConfigurableEnvironment) environment).getSystemProperties(); 36 | System.out.println(getMapString(systemProperties)); 37 | } 38 | 39 | @Test 40 | public void testOverridePropertySource() { 41 | assertEquals(environment.getProperty("foo"), "xyz"); 42 | } 43 | 44 | @Test 45 | public void testOverrideSystemEnvironment() { 46 | assertEquals(environment.getProperty("PATH"), "aaa"); 47 | } 48 | 49 | @Test 50 | public void testOverrideJavaSystemProperties() { 51 | assertEquals(environment.getProperty("java.runtime.name"), "bbb"); 52 | } 53 | 54 | @Test 55 | public void testInlineTestPropertyOverrideResourceLocationTestProperty() { 56 | assertEquals(environment.getProperty("bar"), "uvw"); 57 | } 58 | 59 | private String getMapString(Map map) { 60 | return String.join("\n", 61 | map.keySet().stream().map(k -> k + "=" + map.get(k)).collect(toList()) 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /annotation/src/test/resources/me/chanjar/annotation/jsontest/ex1/expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Honda", 3 | "age": 12 4 | } 5 | -------------------------------------------------------------------------------- /annotation/src/test/resources/me/chanjar/annotation/testps/ex1/property-source.properties: -------------------------------------------------------------------------------- 1 | foo=abc 2 | -------------------------------------------------------------------------------- /annotation/src/test/resources/me/chanjar/annotation/testps/ex1/test-property-source.properties: -------------------------------------------------------------------------------- 1 | bar=def 2 | -------------------------------------------------------------------------------- /aop/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | me.chanjar 7 | spring-test-examples-aggregator 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | me.chanjar 12 | spring-test-examples-aop 13 | jar 14 | 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-aop 26 | 27 | 28 | 29 | org.slf4j 30 | slf4j-api 31 | 32 | 33 | 34 | ch.qos.logback 35 | logback-classic 36 | 37 | 38 | 39 | 40 | 41 | org.testng 42 | testng 43 | test 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter-test 49 | test 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.apache.maven.plugins 58 | maven-jar-plugin 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /aop/src/main/java/me/chanjar/aop/aspect/FooAspect.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.aop.aspect; 2 | 3 | import org.aspectj.lang.ProceedingJoinPoint; 4 | import org.aspectj.lang.annotation.Around; 5 | import org.aspectj.lang.annotation.Aspect; 6 | import org.aspectj.lang.annotation.Pointcut; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | @Aspect 11 | public class FooAspect { 12 | 13 | @Pointcut("execution(* me.chanjar.aop.service.FooServiceImpl.incrementAndGet())") 14 | public void pointcut() { 15 | } 16 | 17 | @Around("pointcut()") 18 | public int changeIncrementAndGet(ProceedingJoinPoint pjp) { 19 | return 0; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /aop/src/main/java/me/chanjar/aop/config/AopConfig.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.aop.config; 2 | 3 | import org.springframework.context.annotation.ComponentScan; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 6 | 7 | @Configuration 8 | @EnableAspectJAutoProxy(proxyTargetClass = true) 9 | @ComponentScan("me.chanjar.aop") 10 | public class AopConfig { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /aop/src/main/java/me/chanjar/aop/service/FooService.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.aop.service; 2 | 3 | public interface FooService { 4 | 5 | int incrementAndGet(); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /aop/src/main/java/me/chanjar/aop/service/FooServiceImpl.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.aop.service; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public class FooServiceImpl implements FooService { 7 | 8 | private int count; 9 | 10 | @Override 11 | public int incrementAndGet() { 12 | count++; 13 | return count; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /aop/src/test/java/me/chanjar/aop/ex1/SpringAop_1_Test.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.aop.ex1; 2 | 3 | import me.chanjar.aop.config.AopConfig; 4 | import me.chanjar.aop.service.FooService; 5 | import me.chanjar.aop.service.FooServiceImpl; 6 | import org.springframework.aop.framework.AopProxyUtils; 7 | import org.springframework.aop.support.AopUtils; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.test.context.ContextConfiguration; 10 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 11 | import org.springframework.test.util.AopTestUtils; 12 | import org.testng.annotations.Test; 13 | 14 | import static org.testng.Assert.*; 15 | 16 | @ContextConfiguration(classes = { SpringAop_1_Test.class, AopConfig.class }) 17 | public class SpringAop_1_Test extends AbstractTestNGSpringContextTests { 18 | 19 | @Autowired 20 | private FooService fooService; 21 | 22 | @Test 23 | public void testFooService() { 24 | 25 | assertNotEquals(fooService.getClass(), FooServiceImpl.class); 26 | 27 | assertTrue(AopUtils.isAopProxy(fooService)); 28 | assertTrue(AopUtils.isCglibProxy(fooService)); 29 | 30 | assertEquals(AopProxyUtils.ultimateTargetClass(fooService), FooServiceImpl.class); 31 | 32 | assertEquals(AopTestUtils.getTargetObject(fooService).getClass(), FooServiceImpl.class); 33 | assertEquals(AopTestUtils.getUltimateTargetObject(fooService).getClass(), FooServiceImpl.class); 34 | 35 | assertEquals(fooService.incrementAndGet(), 0); 36 | assertEquals(fooService.incrementAndGet(), 0); 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /aop/src/test/java/me/chanjar/aop/ex2/SpringAop_2_Test.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.aop.ex2; 2 | 3 | import me.chanjar.aop.aspect.FooAspect; 4 | import me.chanjar.aop.config.AopConfig; 5 | import me.chanjar.aop.service.FooService; 6 | import me.chanjar.aop.service.FooServiceImpl; 7 | import org.springframework.aop.framework.AopProxyUtils; 8 | import org.springframework.aop.support.AopUtils; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener; 11 | import org.springframework.boot.test.mock.mockito.SpyBean; 12 | import org.springframework.test.context.ContextConfiguration; 13 | import org.springframework.test.context.TestExecutionListeners; 14 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 15 | import org.springframework.test.util.AopTestUtils; 16 | import org.testng.annotations.Test; 17 | 18 | import static org.mockito.Matchers.any; 19 | import static org.mockito.Mockito.times; 20 | import static org.mockito.Mockito.verify; 21 | import static org.testng.Assert.assertEquals; 22 | import static org.testng.Assert.assertNotEquals; 23 | import static org.testng.Assert.assertTrue; 24 | 25 | @ContextConfiguration(classes = { SpringAop_2_Test.class, AopConfig.class }) 26 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 27 | public class SpringAop_2_Test extends AbstractTestNGSpringContextTests { 28 | 29 | @SpyBean 30 | private FooAspect fooAspect; 31 | 32 | @Autowired 33 | private FooService fooService; 34 | 35 | @Test 36 | public void testFooService() { 37 | 38 | assertNotEquals(fooService.getClass(), FooServiceImpl.class); 39 | 40 | assertTrue(AopUtils.isAopProxy(fooService)); 41 | assertTrue(AopUtils.isCglibProxy(fooService)); 42 | 43 | assertEquals(AopProxyUtils.ultimateTargetClass(fooService), FooServiceImpl.class); 44 | 45 | assertEquals(AopTestUtils.getTargetObject(fooService).getClass(), FooServiceImpl.class); 46 | assertEquals(AopTestUtils.getUltimateTargetObject(fooService).getClass(), FooServiceImpl.class); 47 | 48 | assertEquals(fooService.incrementAndGet(), 0); 49 | assertEquals(fooService.incrementAndGet(), 0); 50 | 51 | verify(fooAspect, times(2)).changeIncrementAndGet(any()); 52 | 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /aop/src/test/java/me/chanjar/aop/ex3/SpringBootAopTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.aop.ex3; 2 | 3 | import me.chanjar.aop.aspect.FooAspect; 4 | import me.chanjar.aop.config.AopConfig; 5 | import me.chanjar.aop.service.FooService; 6 | import me.chanjar.aop.service.FooServiceImpl; 7 | import org.springframework.aop.framework.AopProxyUtils; 8 | import org.springframework.aop.support.AopUtils; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener; 12 | import org.springframework.boot.test.mock.mockito.SpyBean; 13 | import org.springframework.test.context.TestExecutionListeners; 14 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 15 | import org.springframework.test.util.AopTestUtils; 16 | import org.testng.annotations.Test; 17 | 18 | import static org.mockito.Matchers.any; 19 | import static org.mockito.Mockito.times; 20 | import static org.mockito.Mockito.verify; 21 | import static org.testng.Assert.*; 22 | 23 | @SpringBootTest(classes = { SpringBootAopTest.class, AopConfig.class }) 24 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 25 | public class SpringBootAopTest extends AbstractTestNGSpringContextTests { 26 | 27 | @SpyBean 28 | private FooAspect fooAspect; 29 | 30 | @Autowired 31 | private FooService fooService; 32 | 33 | @Test 34 | public void testFooService() { 35 | 36 | assertNotEquals(fooService.getClass(), FooServiceImpl.class); 37 | 38 | assertTrue(AopUtils.isAopProxy(fooService)); 39 | assertTrue(AopUtils.isCglibProxy(fooService)); 40 | 41 | assertEquals(AopProxyUtils.ultimateTargetClass(fooService), FooServiceImpl.class); 42 | 43 | assertEquals(AopTestUtils.getTargetObject(fooService).getClass(), FooServiceImpl.class); 44 | assertEquals(AopTestUtils.getUltimateTargetObject(fooService).getClass(), FooServiceImpl.class); 45 | 46 | assertEquals(fooService.incrementAndGet(), 0); 47 | assertEquals(fooService.incrementAndGet(), 0); 48 | 49 | verify(fooAspect, times(2)).changeIncrementAndGet(any()); 50 | 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /appendix_i.md: -------------------------------------------------------------------------------- 1 | # 附录I Spring Mock Objects 2 | 3 | Spring提供的[Mock Objects][doc-spring-mock-objects]有以下这些: 4 | 5 | ## package org.springframework.mock.env 6 | 7 | 1. MockEnvironment 8 | 1. MockPropertySource 9 | 10 | ## package org.springframework.mock.jndi 11 | 12 | 1. SimpleNamingContext 13 | 1. ExpectedLookupTemplate 14 | 1. SimpleNamingContextBuilder 15 | 16 | ## package org.springframework.mock.web 17 | 18 | 1. DelegatingServletInputStream 19 | 1. DelegatingServletOutputStream 20 | 1. HeaderValueHolder 21 | 1. MockAsyncContext 22 | 1. MockBodyContent 23 | 1. MockExpressionEvaluator 24 | 1. MockFilterChain 25 | 1. MockFilterConfig 26 | 1. MockHttpServletRequest 27 | 1. MockHttpServletResponse 28 | 1. MockHttpSession 29 | 1. MockJspWriter 30 | 1. MockMultipartFile 31 | 1. MockMultipartHttpServletRequest 32 | 1. MockPageContext 33 | 1. MockRequestDispatcher 34 | 1. MockServletConfig 35 | 1. MockServletContext 36 | 1. MockSessionCookieConfig 37 | 1. PassThroughFilterChain 38 | 39 | [doc-spring-mock-objects]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/unit-testing.html#mock-objects 40 | -------------------------------------------------------------------------------- /appendix_ii.md: -------------------------------------------------------------------------------- 1 | # 附录II Spring Test Utils 2 | 3 | Spring Boot提供的[Unit Test Utils][spring-test-utils]有以下这些: 4 | 5 | 1. AopTestUtils 6 | 1. AssertionErrors 7 | 1. JsonExpectationsHelper 8 | 1. JsonPathExpectationsHelper 9 | 1. MetaAnnotationUtils 10 | 1. ReflectionTestUtils 11 | 1. XmlExpectationsHelper 12 | 1. XpathExpectationsHelper 13 | 1. ModelAndViewAssert 14 | 1. JdbcTestUtils 15 | 1. BeanFactoryAnnotationUtils,用它getBeanByQualifier特别有用 16 | 17 | Spring Boot提供的[Unit Test Utils][spring-boot-test-utils]有以下这些: 18 | 19 | 1. ConfigFileApplicationContextInitializer 20 | 1. EnvironmentTestUtils 21 | 1. OutputCapture 22 | 1. TestRestTemplate 23 | 24 | [spring-test-utils]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/unit-testing.html#unit-testing-support-classes 25 | [spring-boot-test-utils]: https://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-test-utilities -------------------------------------------------------------------------------- /basic/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | me.chanjar 7 | spring-test-examples-aggregator 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | me.chanjar 12 | spring-test-examples-basic 13 | jar 14 | 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-jdbc 26 | 27 | 28 | 29 | com.h2database 30 | h2 31 | 32 | 33 | 34 | org.slf4j 35 | slf4j-api 36 | 37 | 38 | 39 | ch.qos.logback 40 | logback-classic 41 | 42 | 43 | 44 | 45 | org.testng 46 | testng 47 | test 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-starter-test 53 | test 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-jar-plugin 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /basic/src/main/java/me/chanjar/basic/service/FooService.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.service; 2 | 3 | public interface FooService { 4 | 5 | void plusCount(); 6 | 7 | int getCount(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /basic/src/main/java/me/chanjar/basic/service/FooServiceImpl.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.service; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public class FooServiceImpl implements FooService { 7 | 8 | private int count = 0; 9 | 10 | @Override 11 | public void plusCount() { 12 | this.count++; 13 | } 14 | 15 | @Override 16 | public int getCount() { 17 | return count; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/spring/ex1/FooServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.spring.ex1; 2 | 3 | import me.chanjar.basic.service.FooService; 4 | import me.chanjar.basic.service.FooServiceImpl; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.test.context.ContextConfiguration; 7 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 8 | import org.testng.annotations.Test; 9 | 10 | import static org.testng.Assert.assertEquals; 11 | 12 | @ContextConfiguration(classes = FooServiceImpl.class) 13 | public class FooServiceImplTest extends AbstractTestNGSpringContextTests { 14 | 15 | @Autowired 16 | private FooService foo; 17 | 18 | @Test 19 | public void testPlusCount() throws Exception { 20 | assertEquals(foo.getCount(), 0); 21 | 22 | foo.plusCount(); 23 | assertEquals(foo.getCount(), 1); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/spring/ex2/FooServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.spring.ex2; 2 | 3 | import me.chanjar.basic.service.FooService; 4 | import me.chanjar.basic.service.FooServiceImpl; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.annotation.Import; 8 | import org.springframework.test.context.ContextConfiguration; 9 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 10 | import org.testng.annotations.Test; 11 | 12 | import static org.testng.Assert.assertEquals; 13 | 14 | @ContextConfiguration 15 | public class FooServiceImplTest extends AbstractTestNGSpringContextTests { 16 | 17 | @Autowired 18 | private FooService foo; 19 | 20 | @Test 21 | public void testPlusCount() throws Exception { 22 | assertEquals(foo.getCount(), 0); 23 | 24 | foo.plusCount(); 25 | assertEquals(foo.getCount(), 1); 26 | } 27 | 28 | @Configuration 29 | @Import(FooServiceImpl.class) 30 | static class Config { 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/spring/ex3/Config.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.spring.ex3; 2 | 3 | import me.chanjar.basic.service.FooServiceImpl; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.Import; 6 | 7 | @Configuration 8 | @Import(FooServiceImpl.class) 9 | public class Config { 10 | } 11 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/spring/ex3/FooServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.spring.ex3; 2 | 3 | import me.chanjar.basic.service.FooService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.test.context.ContextConfiguration; 6 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 7 | import org.testng.annotations.Test; 8 | 9 | import static org.testng.Assert.assertEquals; 10 | 11 | @ContextConfiguration(classes = Config.class) 12 | public class FooServiceImplTest extends AbstractTestNGSpringContextTests { 13 | 14 | @Autowired 15 | private FooService foo; 16 | 17 | @Test 18 | public void testPlusCount() throws Exception { 19 | assertEquals(foo.getCount(), 0); 20 | 21 | foo.plusCount(); 22 | assertEquals(foo.getCount(), 1); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/springboot/ex1/FooServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.springboot.ex1; 2 | 3 | import me.chanjar.basic.service.FooService; 4 | import me.chanjar.basic.service.FooServiceImpl; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 8 | import org.testng.annotations.Test; 9 | 10 | import static org.testng.Assert.assertEquals; 11 | 12 | @SpringBootTest(classes = FooServiceImpl.class) 13 | public class FooServiceImplTest extends AbstractTestNGSpringContextTests { 14 | 15 | @Autowired 16 | private FooService foo; 17 | 18 | @Test 19 | public void testPlusCount() throws Exception { 20 | assertEquals(foo.getCount(), 0); 21 | 22 | foo.plusCount(); 23 | assertEquals(foo.getCount(), 1); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/springboot/ex2/FooServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.springboot.ex2; 2 | 3 | import me.chanjar.basic.service.FooService; 4 | import me.chanjar.basic.service.FooServiceImpl; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.Import; 9 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 10 | import org.testng.annotations.Test; 11 | 12 | import static org.testng.Assert.assertEquals; 13 | 14 | @SpringBootTest 15 | public class FooServiceImplTest extends AbstractTestNGSpringContextTests { 16 | 17 | @Autowired 18 | private FooService foo; 19 | 20 | @Test 21 | public void testPlusCount() throws Exception { 22 | assertEquals(foo.getCount(), 0); 23 | 24 | foo.plusCount(); 25 | assertEquals(foo.getCount(), 1); 26 | } 27 | 28 | @Configuration 29 | @Import(FooServiceImpl.class) 30 | static class Config { 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/springboot/ex3/Config.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.springboot.ex3; 2 | 3 | import me.chanjar.basic.service.FooServiceImpl; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.context.annotation.Import; 6 | 7 | @Configuration 8 | @Import(FooServiceImpl.class) 9 | public class Config { 10 | } 11 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/springboot/ex3/FooServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.springboot.ex3; 2 | 3 | import me.chanjar.basic.service.FooService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 7 | import org.testng.annotations.Test; 8 | 9 | import static org.testng.Assert.assertEquals; 10 | 11 | @SpringBootTest(classes = Config.class) 12 | public class FooServiceImplTest extends AbstractTestNGSpringContextTests { 13 | 14 | @Autowired 15 | private FooService foo; 16 | 17 | @Test 18 | public void testPlusCount() throws Exception { 19 | assertEquals(foo.getCount(), 0); 20 | 21 | foo.plusCount(); 22 | assertEquals(foo.getCount(), 1); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/springboot/ex4/Config.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.springboot.ex4; 2 | 3 | import me.chanjar.basic.service.FooServiceImpl; 4 | import org.springframework.boot.SpringBootConfiguration; 5 | import org.springframework.context.annotation.Import; 6 | 7 | @SpringBootConfiguration 8 | @Import(FooServiceImpl.class) 9 | public class Config { 10 | } 11 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/springboot/ex4/FooServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.springboot.ex4; 2 | 3 | import me.chanjar.basic.service.FooService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 7 | import org.testng.annotations.Test; 8 | 9 | import static org.testng.Assert.assertEquals; 10 | 11 | @SpringBootTest 12 | public class FooServiceImplTest extends AbstractTestNGSpringContextTests { 13 | 14 | @Autowired 15 | private FooService foo; 16 | 17 | @Test 18 | public void testPlusCount() throws Exception { 19 | assertEquals(foo.getCount(), 0); 20 | 21 | foo.plusCount(); 22 | assertEquals(foo.getCount(), 1); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/springboot/ex5/Config.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.springboot.ex5; 2 | 3 | import org.springframework.boot.SpringBootConfiguration; 4 | import org.springframework.context.annotation.ComponentScan; 5 | 6 | @SpringBootConfiguration 7 | @ComponentScan(basePackages = "me.chanjar.basic.service") 8 | public class Config { 9 | } 10 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/springboot/ex5/FooServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.springboot.ex5; 2 | 3 | import me.chanjar.basic.service.FooService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 7 | import org.testng.annotations.Test; 8 | 9 | import static org.testng.Assert.assertEquals; 10 | 11 | @SpringBootTest 12 | public class FooServiceImplTest extends AbstractTestNGSpringContextTests { 13 | 14 | @Autowired 15 | private FooService foo; 16 | 17 | @Test 18 | public void testPlusCount() throws Exception { 19 | assertEquals(foo.getCount(), 0); 20 | 21 | foo.plusCount(); 22 | assertEquals(foo.getCount(), 1); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/springboot/ex6/Config.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.springboot.ex6; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | @SpringBootApplication(scanBasePackages = "me.chanjar.basic.service") 6 | public class Config { 7 | } 8 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/springboot/ex6/FooServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.springboot.ex6; 2 | 3 | import me.chanjar.basic.service.FooService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 7 | import org.testng.annotations.Test; 8 | 9 | import static org.testng.Assert.assertEquals; 10 | 11 | @SpringBootTest 12 | public class FooServiceImplTest extends AbstractTestNGSpringContextTests { 13 | 14 | @Autowired 15 | private FooService foo; 16 | 17 | @Test 18 | public void testPlusCount() throws Exception { 19 | assertEquals(foo.getCount(), 0); 20 | 21 | foo.plusCount(); 22 | assertEquals(foo.getCount(), 1); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /basic/src/test/java/me/chanjar/basic/testng/ex1/FooServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.basic.testng.ex1; 2 | 3 | import me.chanjar.basic.service.FooService; 4 | import me.chanjar.basic.service.FooServiceImpl; 5 | import org.testng.annotations.Test; 6 | 7 | import static org.testng.Assert.assertEquals; 8 | 9 | public class FooServiceImplTest { 10 | 11 | @Test 12 | public void testPlusCount() { 13 | FooService foo = new FooServiceImpl(); 14 | assertEquals(foo.getCount(), 0); 15 | 16 | foo.plusCount(); 17 | assertEquals(foo.getCount(), 1); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /chapter_0_concept.md: -------------------------------------------------------------------------------- 1 | # Chapter 0: 基本概念 2 | 3 | 在了解学习本项目提供的例子之前,先了解一下什么是单元测试(Unit Testing,简称UT)和集成测试(Integration Testing,简称IT)。 4 | 5 | 如果你之前没有深究过这两个概念,那么你可能会得出如下错误的答案: 6 | 7 | 错误答案1: 8 | 9 | > 单元测试就是对一个方法进行测试的测试 10 | 11 | 听上去很像那么回事儿,对吧?单元测试,就是测一个逻辑单元,所以就测一个方法就是单元测试,听上去很有道理是不是?但是,那么测试两个方法(这两个方法互相关联)的话叫什么呢? 12 | 13 | 错误答案2: 14 | 15 | > 集成测试是把几个方法或者几个类放在一起测试 16 | 17 | 既然前面单元测试只测一个方法,那么几个方法放在一起测就是集成测试,听上去挺有道理的。那么是不是只要测一个以上的方法就是集成测试呢? 18 | 19 | 错误答案3: 20 | 21 | > 集成测试就是和其他系统联合调试做的测试 22 | 23 | 听上去有点像SOA或者现在流行的微服务是吧。做这种测试的时候必须得各个开发团队紧密配合,一个不小心就会测试失败,然后就是各种返工,总之难度和火箭发射有的一拼。 24 | 25 | 那么正确答案是什么?其实这两个概念的解释比较冗长这里就不细讲了,只需记住UT和IT具备以下特征: 26 | 27 | 1. UT和IT必须是自动化的。 28 | 1. UT只专注于整个系统里的某一小部分,粒度没有规定,一般都比较小可以到方法级别。比如某个字符串串接方法。 29 | 1. UT不需要连接外部系统,在内存里跑跑就行了。 30 | 1. IT需要连接外部系统,比如连接数据库做CRUD测试。 31 | 1. 测试环境和生产环境是隔离的。 32 | 1. 能做UT的就不要做IT。 33 | 34 | 参考链接: 35 | 36 | * [Martin Fowler - Unit Test](https://martinfowler.com/bliki/UnitTest.html) 37 | * [Wikipedia - Unit Testing](https://en.wikipedia.org/wiki/Unit_testing) 38 | * [Wikipedia - Integration Testing](https://en.wikipedia.org/wiki/Integration_testing) 39 | -------------------------------------------------------------------------------- /chapter_1_intro.md: -------------------------------------------------------------------------------- 1 | # Chapter 1: 基本用法 - 引言 2 | 3 | 本项目所有的项目均采用Maven的标准目录结构: 4 | 5 | * `src/main/java`,程序java文件目录 6 | * `src/main/resource`,程序资源文件目录 7 | * `src/test/java`,测试代码目录 8 | * `src/test/resources`,测试资源文件目录 9 | 10 | 并且所有Maven项目都可以使用`mvn clean test`方式跑单元测试,特别需要注意,只有文件名是`*Test.java`才会被执行,一定要注意这一点哦。 11 | 12 | ## 参考文档 13 | 14 | * [Maven Standard Directory Layout][doc-maven-standard-dir-layout] 15 | 16 | [doc-maven-standard-dir-layout]: https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html 17 | -------------------------------------------------------------------------------- /chapter_1_s1_testng.md: -------------------------------------------------------------------------------- 1 | # Chapter 1: 基本用法 - 认识TestNG 2 | 3 | 先认识一下TestNG,这里有一个[FooServiceImpl][src-FooServiceImpl],里面有两个方法,一个是给计数器+1,一个是获取当前计数器的值: 4 | 5 | ```java 6 | @Component 7 | public class FooServiceImpl implements FooService { 8 | 9 | private int count = 0; 10 | 11 | @Override 12 | public void plusCount() { 13 | this.count++; 14 | } 15 | 16 | @Override 17 | public int getCount() { 18 | return count; 19 | } 20 | 21 | } 22 | ``` 23 | 24 | 然后我们针对它有一个[FooServiceImplTest][src-ex1-FooServiceImplTest]作为UT: 25 | 26 | ```java 27 | public class FooServiceImplTest { 28 | 29 | @Test 30 | public void testPlusCount() { 31 | FooService foo = new FooServiceImpl(); 32 | assertEquals(foo.getCount(), 0); 33 | 34 | foo.plusCount(); 35 | assertEquals(foo.getCount(), 1); 36 | } 37 | 38 | } 39 | ``` 40 | 41 | 注意看代码里的`assertEquals(...)`,我们利用它来判断`Foo.getCount`方法是否按照预期执行。所以,所谓的测试其实就是给定输入、执行一些方法,assert结果是否符合预期的过程。 42 | 43 | ## 参考文档 44 | 45 | * [TestNG documentation][doc-testng-doc] 46 | 47 | [doc-testng-doc]: http://testng.org/doc/documentation-main.html 48 | [src-FooServiceImpl]: basic/src/main/java/me/chanjar/basic/service/FooServiceImpl.java 49 | [src-ex1-FooServiceImplTest]: basic/src/test/java/me/chanjar/basic/testng/ex1/FooServiceImplTest.java 50 | 51 | -------------------------------------------------------------------------------- /chapter_1_s2_spring_testing.md: -------------------------------------------------------------------------------- 1 | # Chapter 1: 基本用法 - 使用Spring Testing工具 2 | 3 | 既然我们现在开发的是一个Spring项目,那么肯定会用到Spring Framework的各种特性,这些特性实在是太好用了,它能够大大提高我们的开发效率。那么自然而然,你会想在测试代码里也能够利用Spring Framework提供的特性,来提高测试代码的开发效率。这部分我们会讲如何使用Spring提供的测试工具来做测试。 4 | 5 | ## 例子1 6 | 7 | 源代码见[FooServiceImplTest][src-ex1-FooServiceImplTest]: 8 | 9 | ```java 10 | @ContextConfiguration(classes = FooServiceImpl.class) 11 | public class FooServiceImplTest extends AbstractTestNGSpringContextTests { 12 | 13 | @Autowired 14 | private FooService foo; 15 | 16 | @Test 17 | public void testPlusCount() throws Exception { 18 | assertEquals(foo.getCount(), 0); 19 | 20 | foo.plusCount(); 21 | assertEquals(foo.getCount(), 1); 22 | } 23 | 24 | } 25 | ``` 26 | 27 | 在上面的源代码里我们要注意三点: 28 | 29 | 1. 测试类继承了[AbstractTestNGSpringContextTests][javadoc-AbstractTestNGSpringContextTests],如果不这么做测试类是无法启动Spring容器的 30 | 1. 使用了[@ContextConfiguration][javadoc-ContextConfiguration]来加载被测试的Bean:`FooServiceImpl` 31 | 1. `FooServiceImpl`是`@Component` 32 | 33 | 以上三点缺一不可。 34 | 35 | ## 例子2 36 | 37 | 在这个例子里,我们将`@Configuration`作为nested static class放在测试类里,根据[@ContextConfiguration][doc-spring-ContextConfiguration]的文档,它会在默认情况下查找测试类的nested static @Configuration class,用它来导入Bean。 38 | 39 | 源代码见[FooServiceImplTest][src-ex2-FooServiceImplTest]: 40 | 41 | 42 | ```java 43 | @ContextConfiguration 44 | public class FooServiceImplTest extends AbstractTestNGSpringContextTests { 45 | 46 | @Autowired 47 | private FooService foo; 48 | 49 | @Test 50 | public void testPlusCount() throws Exception { 51 | assertEquals(foo.getCount(), 0); 52 | 53 | foo.plusCount(); 54 | assertEquals(foo.getCount(), 1); 55 | } 56 | 57 | @Configuration 58 | @Import(FooServiceImpl.class) 59 | static class Config { 60 | } 61 | 62 | } 63 | ``` 64 | 65 | 66 | ## 例子3 67 | 68 | 在这个例子里,我们将`@Configuration`放到外部,并让[@ContextConfiguration][doc-spring-ContextConfiguration]去加载。 69 | 70 | 源代码见[Config][src-ex3-Config]: 71 | 72 | ```java 73 | @Configuration 74 | @Import(FooServiceImpl.class) 75 | public class Config { 76 | } 77 | ``` 78 | 79 | [FooServiceImplTest][src-ex3-FooServiceImplTest]: 80 | 81 | ```java 82 | @ContextConfiguration(classes = Config.class) 83 | public class FooServiceImplTest extends AbstractTestNGSpringContextTests { 84 | 85 | @Autowired 86 | private FooService foo; 87 | 88 | @Test 89 | public void testPlusCount() throws Exception { 90 | assertEquals(foo.getCount(), 0); 91 | 92 | foo.plusCount(); 93 | assertEquals(foo.getCount(), 1); 94 | } 95 | 96 | } 97 | ``` 98 | 99 | 需要注意的是,如果`@Configuration`是专供某个测试类使用的话,把它放到外部并不是一个好主意,因为它有可能会被`@ComponentScan`扫描到,从而产生一些奇怪的问题。 100 | 101 | ## 参考文档 102 | 103 | * [Spring Framework Testing][doc-spring-framework-testing] 104 | * [Context configuration with annotated classes][doc-spring-ContextConfiguration] 105 | 106 | [doc-spring-ContextConfiguration]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/integration-testing.html#testcontext-ctx-management-javaconfig 107 | [doc-spring-framework-testing]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#testing 108 | [javadoc-AbstractTestNGSpringContextTests]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/test/context/testng/AbstractTestNGSpringContextTests.html 109 | [src-ex1-FooServiceImplTest]: basic/src/test/java/me/chanjar/basic/spring/ex1/FooServiceImplTest.java 110 | [src-ex2-FooServiceImplTest]: basic/src/test/java/me/chanjar/basic/spring/ex2/FooServiceImplTest.java 111 | [src-ex3-Config]: basic/src/test/java/me/chanjar/basic/spring/ex3/Config.java 112 | [src-ex3-FooServiceImplTest]: basic/src/test/java/me/chanjar/basic/spring/ex3/FooServiceImplTest.java 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /chapter_2_intro.md: -------------------------------------------------------------------------------- 1 | # Chapter 2: Annotations 2 | 3 | Spring & Spring Boot Testing工具提供了一些方便测试的Annotation,本章节会对其中的一些做一些讲解。 4 | -------------------------------------------------------------------------------- /chapter_2_s1_test_property_source.md: -------------------------------------------------------------------------------- 1 | # Chapter 2: Annotations - @TestPropertySource 2 | 3 | [@TestPropertySource][javadoc-TestPropertySource]可以用来覆盖掉来自于系统环境变量、Java系统属性、[@PropertySource][javadoc-PropertySource]的属性。 4 | 5 | 同时``@TestPropertySource(properties=...)``优先级高于``@TestPropertySource(locations=...)``。 6 | 7 | 利用它我们可以很方便的在测试代码里微调、模拟配置(比如修改操作系统目录分隔符、数据源等)。 8 | 9 | ## 例子1: 使用Spring Testing工具 10 | 11 | 我们先使用[@PropertySource][javadoc-PropertySource]将一个外部properties文件加载进来,[PropertySourceConfig][src-ex1-PropertySourceConfig]: 12 | 13 | ```java 14 | @Configuration 15 | @PropertySource("classpath:me/chanjar/annotation/testps/ex1/property-source.properties") 16 | public class PropertySourceConfig { 17 | } 18 | ``` 19 | 20 | ``` 21 | file: property-source.properties 22 | foo=abc 23 | ``` 24 | 25 | 然后我们用[@TestPropertySource][javadoc-TestPropertySource]覆盖了这个property: 26 | 27 | ```java 28 | @TestPropertySource(properties = { "foo=xyz" ... 29 | ``` 30 | 31 | 最后我们测试了是否覆盖成功(结果是成功的): 32 | 33 | ```java 34 | @Test 35 | public void testOverridePropertySource() { 36 | assertEquals(environment.getProperty("foo"), "xyz"); 37 | } 38 | ``` 39 | 40 | 同时我们还对@TestPropertySource做了一些其他的测试,具体情况你可以自己观察。为了方便你观察[@TestPropertySource][javadoc-TestPropertySource]对系统环境变量和Java系统属性的覆盖效果,我们在一开始打印出了它们的值。 41 | 42 | 源代码[TestPropertyTest][src-ex1-TestPropertyTest]: 43 | 44 | ```java 45 | @ContextConfiguration(classes = PropertySourceConfig.class) 46 | @TestPropertySource( 47 | properties = { "foo=xyz", "bar=uvw", "PATH=aaa", "java.runtime.name=bbb" }, 48 | locations = "classpath:me/chanjar/annotation/testps/ex1/test-property-source.properties" 49 | ) 50 | public class TestPropertyTest extends AbstractTestNGSpringContextTests implements EnvironmentAware { 51 | 52 | private Environment environment; 53 | 54 | @Override 55 | public void setEnvironment(Environment environment) { 56 | this.environment = environment; 57 | Map systemEnvironment = ((ConfigurableEnvironment) environment).getSystemEnvironment(); 58 | System.out.println("=== System Environment ==="); 59 | System.out.println(getMapString(systemEnvironment)); 60 | System.out.println(); 61 | 62 | System.out.println("=== Java System Properties ==="); 63 | Map systemProperties = ((ConfigurableEnvironment) environment).getSystemProperties(); 64 | System.out.println(getMapString(systemProperties)); 65 | } 66 | 67 | @Test 68 | public void testOverridePropertySource() { 69 | assertEquals(environment.getProperty("foo"), "xyz"); 70 | } 71 | 72 | @Test 73 | public void testOverrideSystemEnvironment() { 74 | assertEquals(environment.getProperty("PATH"), "aaa"); 75 | } 76 | 77 | @Test 78 | public void testOverrideJavaSystemProperties() { 79 | assertEquals(environment.getProperty("java.runtime.name"), "bbb"); 80 | } 81 | 82 | @Test 83 | public void testInlineTestPropertyOverrideResourceLocationTestProperty() { 84 | assertEquals(environment.getProperty("bar"), "uvw"); 85 | } 86 | 87 | private String getMapString(Map map) { 88 | return String.join("\n", 89 | map.keySet().stream().map(k -> k + "=" + map.get(k)).collect(toList()) 90 | ); 91 | } 92 | } 93 | ``` 94 | 95 | ## 例子2: 使用Spring Boot Testing工具 96 | 97 | [@TestPropertySource][javadoc-TestPropertySource]也可以和[@SpringBootTest][javadoc-SpringBootTest]一起使用。 98 | 99 | 源代码见[TestPropertyTest][src-ex2-TestPropertyTest]: 100 | 101 | ```java 102 | @SpringBootTest(classes = PropertySourceConfig.class) 103 | @TestPropertySource( 104 | properties = { "foo=xyz", "bar=uvw", "PATH=aaa", "java.runtime.name=bbb" }, 105 | locations = "classpath:me/chanjar/annotation/testps/ex1/test-property-source.properties" 106 | ) 107 | public class TestPropertyTest extends AbstractTestNGSpringContextTests implements EnvironmentAware { 108 | // ... 109 | } 110 | ``` 111 | 112 | 113 | 114 | ## 参考文档 115 | 116 | * [Spring Framework Testing][doc-spring-framework-testing] 117 | * [Spring Boot Testing][doc-spring-boot-testing] 118 | * [Context configuration with test property sources][doc-test-property-source] 119 | 120 | [src-ex1-TestPropertyTest]: annotation/src/test/java/me/chanjar/annotation/testps/ex1/TestPropertyTest.java 121 | [src-ex1-PropertySourceConfig]: annotation/src/test/java/me/chanjar/annotation/testps/ex1/PropertySourceConfig.java 122 | [src-ex2-TestPropertyTest]: annotation/src/test/java/me/chanjar/annotation/testps/ex2/TestPropertyTest.java 123 | [src-ex2-TestPropertyTest]: annotation/src/test/java/me/chanjar/annotation/testps/ex2/TestPropertyTest.java 124 | [javadoc-SpringBootTest]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/test/context/SpringBootTest.html 125 | [doc-spring-framework-testing]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#testing 126 | [doc-spring-boot-testing]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-testing 127 | [javadoc-TestPropertySource]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/test/context/TestPropertySource.html 128 | [javadoc-PropertySource]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/context/annotation/PropertySource.html 129 | [javadoc-JsonTest]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/test/autoconfigure/json/JsonTest.html 130 | [doc-test-property-source]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/integration-testing.html#testcontext-ctx-management-property-sources 131 | -------------------------------------------------------------------------------- /chapter_2_s2_active_profile.md: -------------------------------------------------------------------------------- 1 | # Chapter 2: Annotations - [@ActiveProfiles][doc-active-profiles] 2 | 3 | [@ActiveProfiles][doc-active-profiles]可以用来在测试的时候启用某些Profile的Bean。本章节的测试代码使用了下面的这个配置: 4 | 5 | ```java 6 | @Configuration 7 | public class Config { 8 | 9 | @Bean 10 | @Profile("dev") 11 | public Foo fooDev() { 12 | return new Foo("dev"); 13 | } 14 | 15 | @Bean 16 | @Profile("product") 17 | public Foo fooProduct() { 18 | return new Foo("product"); 19 | } 20 | 21 | @Bean 22 | @Profile("default") 23 | public Foo fooDefault() { 24 | return new Foo("default"); 25 | } 26 | 27 | @Bean 28 | public Bar bar() { 29 | return new Bar("no profile"); 30 | } 31 | 32 | } 33 | ``` 34 | 35 | ## 例子1:不使用ActiveProfiles 36 | 37 | 在没有[@ActiveProfiles][doc-active-profiles]的时候,profile=default和没有设定profile的Bean会被加载到。 38 | 39 | 源代码[ActiveProfileTest][src-ex1-ActiveProfileTest]: 40 | 41 | ```java 42 | @ContextConfiguration(classes = Config.class) 43 | public class ActiveProfileTest extends AbstractTestNGSpringContextTests { 44 | 45 | @Autowired 46 | private Foo foo; 47 | 48 | @Autowired 49 | private Bar bar; 50 | 51 | @Test 52 | public void test() { 53 | assertEquals(foo.getName(), "default"); 54 | assertEquals(bar.getName(), "no profile"); 55 | } 56 | 57 | } 58 | ``` 59 | 60 | ## 例子2:使用ActiveProfiles 61 | 62 | 当使用了[@ActiveProfiles][doc-active-profiles]的时候,profile匹配的和没有设定profile的Bean会被加载到。 63 | 64 | 源代码[ActiveProfileTest][src-ex2-ActiveProfileTest]: 65 | 66 | ```java 67 | @ContextConfiguration(classes = Config.class) 68 | [@ActiveProfiles][doc-active-profiles]("product") 69 | public class ActiveProfileTest extends AbstractTestNGSpringContextTests { 70 | 71 | @Autowired 72 | private Foo foo; 73 | 74 | @Autowired 75 | private Bar bar; 76 | 77 | @Test 78 | public void test() { 79 | assertEquals(foo.getName(), "product"); 80 | assertEquals(bar.getName(), "no profile"); 81 | } 82 | 83 | } 84 | ``` 85 | 86 | ## 总结 87 | 88 | * 在没有[@ActiveProfiles][doc-active-profiles]的时候,profile=default和没有设定profile的Bean会被加载到。 89 | * 当使用了[@ActiveProfiles][doc-active-profiles]的时候,profile匹配的和没有设定profile的Bean会被加载到。 90 | 91 | [@ActiveProfiles][doc-active-profiles]同样也可以和@SpringBootTest配合使用,这里就不举例说明了。 92 | 93 | ## 参考文档 94 | 95 | * [Spring Framework Testing][doc-spring-framework-testing] 96 | * [Spring Boot Testing][doc-spring-boot-testing] 97 | 98 | [doc-spring-framework-testing]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#testing 99 | [doc-spring-boot-testing]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-testing 100 | [doc-active-profiles]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/integration-testing.html#__activeprofiles 101 | [src-ex1-ActiveProfileTest]: annotation/src/test/java/me/chanjar/annotation/activeprofiles/ex1/ActiveProfileTest.java 102 | [src-ex2-ActiveProfileTest]: annotation/src/test/java/me/chanjar/annotation/activeprofiles/ex1/ActiveProfileTest.java 103 | -------------------------------------------------------------------------------- /chapter_2_s3_json_test.md: -------------------------------------------------------------------------------- 1 | # Chapter 2: Annotations - @JsonTest 2 | 3 | [@JsonTest][javadoc-JsonTest]是Spring Boot提供的方便测试JSON序列化反序列化的测试工具,在Spring Boot的[文档][doc-JsonTest]中有一些介绍。 4 | 5 | 需要注意的是[@JsonTest][javadoc-JsonTest]需要Jackson的``ObjectMapper``,事实上如果你的Spring Boot项目添加了`spring-web`的Maven依赖,[JacksonAutoConfiguration][javadoc-JacksonAutoConfiguration]就会自动为你配置一个: 6 | 7 | ```xml 8 | 9 | org.springframework.boot 10 | spring-boot-autoconfigure 11 | 12 | 13 | 14 | org.springframework 15 | spring-web 16 | 17 | ``` 18 | 19 | 这里没有提供关于日期时间的例子,关于这个比较复杂,可以看我的另一篇文章:[Spring Boot Jackson对于日期时间类型处理的例子][github-springboot-jackson-datetime-example]。 20 | 21 | ## 例子1:简单例子 22 | 23 | 源代码见[SimpleJsonTest][src-ex1-SimpleJsonTest]: 24 | 25 | ```java 26 | @SpringBootTest(classes = SimpleJsonTest.class) 27 | @JsonTest 28 | public class SimpleJsonTest extends AbstractTestNGSpringContextTests { 29 | 30 | @Autowired 31 | private JacksonTester json; 32 | 33 | @Test 34 | public void testSerialize() throws Exception { 35 | Foo details = new Foo("Honda", 12); 36 | // 使用通包下的json文件测试结果是否正确 37 | assertThat(this.json.write(details)).isEqualToJson("expected.json"); 38 | // 或者使用基于JSON path的校验 39 | assertThat(this.json.write(details)).hasJsonPathStringValue("@.name"); 40 | assertThat(this.json.write(details)).extractingJsonPathStringValue("@.name").isEqualTo("Honda"); 41 | assertThat(this.json.write(details)).hasJsonPathNumberValue("@.age"); 42 | assertThat(this.json.write(details)).extractingJsonPathNumberValue("@.age").isEqualTo(12); 43 | } 44 | 45 | @Test 46 | public void testDeserialize() throws Exception { 47 | String content = "{\"name\":\"Ford\",\"age\":13}"; 48 | Foo actual = this.json.parseObject(content); 49 | assertThat(actual).isEqualTo(new Foo("Ford", 13)); 50 | assertThat(actual.getName()).isEqualTo("Ford"); 51 | assertThat(actual.getAge()).isEqualTo(13); 52 | 53 | } 54 | 55 | } 56 | ``` 57 | 58 | ## 例子2: 测试@JsonComponent 59 | 60 | [@JsonTest][javadoc-JsonTest]可以用来测试[@JsonComponent][javadoc-JsonComponent]。 61 | 62 | 这个例子里使用了自定义的``@JsonComponent`` [FooJsonComponent][src-ex2-FooJsonComponent]: 63 | 64 | ```java 65 | @JsonComponent 66 | public class FooJsonComponent { 67 | 68 | public static class Serializer extends JsonSerializer { 69 | @Override 70 | public void serialize(Foo value, JsonGenerator gen, SerializerProvider serializers) 71 | throws IOException, JsonProcessingException { 72 | // ... 73 | } 74 | 75 | } 76 | 77 | public static class Deserializer extends JsonDeserializer { 78 | 79 | @Override 80 | public Foo deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 81 | // ... 82 | } 83 | 84 | } 85 | 86 | } 87 | 88 | ``` 89 | 90 | 测试代码[JsonComponentJsonTest][src-ex2-JsonComponentJsonTest]: 91 | 92 | ```java 93 | @SpringBootTest(classes = { JsonComponentJacksonTest.class, FooJsonComponent.class }) 94 | @JsonTest 95 | public class JsonComponentJacksonTest extends AbstractTestNGSpringContextTests { 96 | 97 | @Autowired 98 | private JacksonTester json; 99 | 100 | @Test 101 | public void testSerialize() throws Exception { 102 | Foo details = new Foo("Honda", 12); 103 | assertThat(this.json.write(details).getJson()).isEqualTo("\"name=Honda,age=12\""); 104 | } 105 | 106 | @Test 107 | public void testDeserialize() throws Exception { 108 | String content = "\"name=Ford,age=13\""; 109 | Foo actual = this.json.parseObject(content); 110 | assertThat(actual).isEqualTo(new Foo("Ford", 13)); 111 | assertThat(actual.getName()).isEqualTo("Ford"); 112 | assertThat(actual.getAge()).isEqualTo(13); 113 | 114 | } 115 | 116 | } 117 | ``` 118 | 119 | ## 例子3: 使用@ContextConfiguration 120 | 121 | 事实上[@JsonTest][javadoc-JsonTest]也可以配合`@ContextConfiguration`一起使用。 122 | 123 | 源代码见[ThinJsonTest][src-ex3-ThinJsonTest]: 124 | 125 | ```java 126 | @JsonTest 127 | @ContextConfiguration(classes = JsonTest.class) 128 | public class ThinJsonTest extends AbstractTestNGSpringContextTests { 129 | 130 | @Autowired 131 | private JacksonTester json; 132 | 133 | @Test 134 | public void testSerialize() throws Exception { 135 | // ... 136 | } 137 | 138 | @Test 139 | public void testDeserialize() throws Exception { 140 | // ... 141 | } 142 | 143 | } 144 | 145 | ``` 146 | 147 | ## 参考文档 148 | 149 | * [Spring Framework Testing][doc-spring-framework-testing] 150 | * [Spring Boot Testing][doc-spring-boot-testing] 151 | * [@JsonTest][doc-JsonTest] 152 | * [JsonComponent][javadoc-JsonComponent] 153 | * [JacksonAutoConfiguration][javadoc-JacksonAutoConfiguration] 154 | * [JacksonTester][javadoc-JacksonTester] 155 | * [GsonTester][javadoc-GsonTester] 156 | * [BasicJsonTester][javadoc-BasicJsonTester] 157 | 158 | [src-ex1-SimpleJsonTest]: annotation/src/test/java/me/chanjar/annotation/jsontest/ex1/SimpleJsonTest.java 159 | [src-ex2-JsonComponentJsonTest]: annotation/src/test/java/me/chanjar/annotation/jsontest/ex2/JsonComponentJacksonTest.java 160 | [src-ex2-FooJsonComponent]: annotation/src/test/java/me/chanjar/annotation/jsontest/ex2/FooJsonComponent.java 161 | [src-ex3-ThinJsonTest]: annotation/src/test/java/me/chanjar/annotation/jsontest/ex3/ThinJsonTest.java 162 | [doc-spring-framework-testing]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#testing 163 | [doc-spring-boot-testing]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-testing 164 | [doc-JsonTest]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-json-tests 165 | [javadoc-JsonTest]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/test/autoconfigure/json/JsonTest.html 166 | [javadoc-JsonTest]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/test/autoconfigure/json/JsonTest.html 167 | [javadoc-JacksonTester]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/test/json/JacksonTester.html 168 | [javadoc-GsonTester]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/test/json/GsonTester.html 169 | [javadoc-BasicJsonTester]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/test/json/BasicJsonTester.html 170 | [javadoc-JacksonAutoConfiguration]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.html 171 | [javadoc-JsonComponent]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/jackson/JsonComponent.html 172 | [github-springboot-jackson-datetime-example]: https://github.com/chanjarster/springboot-jackson-datetime-example 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /chapter_2_s4_override_auto_configuration.md: -------------------------------------------------------------------------------- 1 | # Chapter 2: Annotations - @OverrideAutoConfiguration 2 | 3 | 在[Chapter 1: 基本用法 - 使用Spring Boot Testing工具][chapter_1_s3_spring_boot_testing.md]里提到: 4 | 5 | > 除了单元测试(不需要初始化ApplicationContext的测试)外,尽量将测试配置和生产配置保持一致。比如如果生产配置里启用了AutoConfiguration,那么测试配置也应该启用。因为只有这样才能够在测试环境下发现生产环境的问题,也避免出现一些因为配置不同导致的奇怪问题。 6 | 7 | 那么当我们想在测试代码里关闭Auto Configuration如何处理? 8 | 9 | 1. 方法1:提供另一套测试配置 10 | 1. 方法2:使用`@OverrideAutoConfiguration` 11 | 12 | 方法1虽然能够很好的解决问题,但是比较麻烦。而方法2则能够不改变原有配置、不提供新的配置的情况下,就能够关闭Auto Configuration。 13 | 14 | 在本章节的例子里,我们自己做了一个Auto Configuration类,[AutoConfigurationEnableLogger][src-AutoConfigurationEnableLogger]: 15 | 16 | ```java 17 | @Configuration 18 | public class AutoConfigurationEnableLogger { 19 | 20 | private static final Logger LOGGER = LoggerFactory.getLogger(AutoConfigurationEnableLogger.class); 21 | 22 | public AutoConfigurationEnableLogger() { 23 | LOGGER.info("Auto Configuration Enabled"); 24 | } 25 | 26 | } 27 | ``` 28 | 29 | 并且在`META-INF/spring.factories`里注册了它: 30 | 31 | ``` 32 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 33 | me.chanjar.annotation.overrideac.AutoConfigurationEnableLogger 34 | ``` 35 | 36 | 这样一来,只要Spring Boot启动了Auto Configuration就会打印出日志: 37 | 38 | ``` 39 | 2017-08-24 16:44:52.789 INFO 13212 --- [ main] m.c.a.o.AutoConfigurationEnableLogger : Auto Configuration Enabled 40 | ``` 41 | 42 | ## 例子1:未关闭Auto Configuration 43 | 44 | 源代码见[BootTest][src-ex1-BootTest]: 45 | 46 | ```java 47 | @SpringBootTest 48 | @SpringBootApplication 49 | public class BootTest extends AbstractTestNGSpringContextTests { 50 | 51 | @Test 52 | public void testName() throws Exception { 53 | 54 | } 55 | } 56 | ``` 57 | 58 | 查看输出的日志,会发现Auto Configuration已经启用。 59 | 60 | ## 例子2:关闭Auto Configuration 61 | 62 | 然后我们用[@OverrideAutoConfiguration][javadoc-OverrideAutoConfiguration]关闭了Auto Configuration。 63 | 64 | 源代码见[BootTest][src-ex2-BootTest]: 65 | 66 | ```java 67 | @SpringBootTest 68 | @OverrideAutoConfiguration(enabled = false) 69 | @SpringBootApplication 70 | public class BootTest extends AbstractTestNGSpringContextTests { 71 | 72 | @Test 73 | public void testName() throws Exception { 74 | 75 | } 76 | } 77 | ``` 78 | 79 | 再查看输出的日志,就会发现Auto Configuration已经关闭。 80 | 81 | 82 | ## 参考文档 83 | 84 | * [Spring Framework Testing][doc-spring-framework-testing] 85 | * [Spring Boot Testing][doc-spring-boot-testing] 86 | * [Context configuration with test property sources][doc-test-property-source] 87 | 88 | [chapter_1_s3_spring_boot_testing.md]: chapter_1_s3_spring_boot_testing.md 89 | [javadoc-OverrideAutoConfiguration]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/test/autoconfigure/OverrideAutoConfiguration.html 90 | 91 | [doc-spring-framework-testing]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#testing 92 | [doc-spring-boot-testing]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-testing 93 | [src-AutoConfigurationEnableLogger]: annotation/src/main/java/me/chanjar/annotation/overrideac/AutoConfigurationEnableLogger.java 94 | [src-ex1-BootTest]: annotation/src/test/java/me/chanjar/annotation/overrideac/ex1/BootTest.java 95 | [src-ex2-BootTest]: annotation/src/test/java/me/chanjar/annotation/overrideac/ex2/BootTest.java 96 | -------------------------------------------------------------------------------- /chapter_2_s5_test_configuration.md: -------------------------------------------------------------------------------- 1 | # Chapter 2: Annotations - @TestConfiguration 2 | 3 | [@TestConfiguration][javadoc-test-configuration]是Spring Boot Test提供的一种工具,用它我们可以在一般的@Configuration之外补充测试专门用的Bean或者自定义的配置。 4 | 5 | [@TestConfiguration][javadoc-test-configuration]实际上是一种[@TestComponent][javadoc-test-component],[@TestComponent][javadoc-test-component]是另一种@Component,在语义上用来指定某个Bean是专门用于测试的。 6 | 7 | 需要特别注意,你应该使用一切办法避免在生产代码中自动扫描到[@TestComponent][javadoc-test-component]。 8 | 如果你使用`@SpringBootApplication`启动测试或者生产代码,[@TestComponent][javadoc-test-component]会自动被排除掉,如果不是则需要像`@SpringBootApplication`一样添加`TypeExcludeFilter`: 9 | 10 | ```java 11 | //... 12 | @ComponentScan(excludeFilters = { 13 | @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), 14 | // ...}) 15 | public @interface SpringBootApplication 16 | ``` 17 | 18 | ## 例子1:作为内部类 19 | 20 | [@TestConfiguration][javadoc-test-configuration]和@Configuration不同,它不会阻止@SpringBootTest去查找机制(在[Chapter 1: 基本用法 - 使用Spring Boot Testing工具 - 例子4][chapter_1_s3_spring_boot_testing]提到过),正如[@TestConfiguration][javadoc-test-configuration]的javadoc所说,它只是对既有配置的一个补充。 21 | 22 | 所以我们在测试代码上添加@SpringBootConfiguration,用`@SpringBootTest(classes=...)`或者在同package里添加@SpringBootConfiguration类都是可以的。 23 | 24 | 而且[@TestConfiguration][javadoc-test-configuration]作为内部类的时候它是会被@SpringBootTest扫描掉的,这点和@Configuration一样。 25 | 26 | 测试代码[TestConfigurationTest][src-ex1-TestConfigurationTest]: 27 | 28 | ```java 29 | @SpringBootTest 30 | @SpringBootConfiguration 31 | public class TestConfigurationTest extends AbstractTestNGSpringContextTests { 32 | 33 | @Autowired 34 | private Foo foo; 35 | 36 | @Test 37 | public void testPlusCount() throws Exception { 38 | assertEquals(foo.getName(), "from test config"); 39 | } 40 | 41 | @TestConfiguration 42 | public class TestConfig { 43 | 44 | @Bean 45 | public Foo foo() { 46 | return new Foo("from test config"); 47 | } 48 | 49 | } 50 | } 51 | ``` 52 | 53 | 54 | 55 | ## 例子2:对@Configuration的补充和覆盖 56 | 57 | [@TestConfiguration][javadoc-test-configuration]能够: 58 | 59 | 1. 补充额外的Bean 60 | 1. 覆盖已存在的Bean 61 | 62 | 要特别注意第二点,[@TestConfiguration][javadoc-test-configuration]能够直接覆盖已存在的Bean,这一点正常的@Configuration是做不到的。 63 | 64 | 我们先提供了一个正常的@Configuration([Config][src-ex2-Config]): 65 | 66 | ```java 67 | @Configuration 68 | public class Config { 69 | 70 | @Bean 71 | public Foo foo() { 72 | return new Foo("from config"); 73 | } 74 | } 75 | ``` 76 | 77 | 又提供了一个@TestConfiguration,在里面覆盖了`foo` Bean,并且提供了`foo2` Bean([TestConfig][src-ex2-TestConfig]): 78 | 79 | ```java 80 | @TestConfiguration 81 | public class TestConfig { 82 | 83 | // 这里不需要@Primary之类的机制,直接就能够覆盖 84 | @Bean 85 | public Foo foo() { 86 | return new Foo("from test config"); 87 | } 88 | 89 | @Bean 90 | public Foo foo2() { 91 | return new Foo("from test config2"); 92 | } 93 | } 94 | ``` 95 | 96 | 测试代码[TestConfigurationTest][src-ex2-TestConfigurationTest]: 97 | 98 | ```java 99 | @SpringBootTest(classes = { Config.class, TestConfig.class }) 100 | public class TestConfigurationTest extends AbstractTestNGSpringContextTests { 101 | 102 | @Qualifier("foo") 103 | @Autowired 104 | private Foo foo; 105 | 106 | @Qualifier("foo2") 107 | @Autowired 108 | private Foo foo2; 109 | 110 | @Test 111 | public void testPlusCount() throws Exception { 112 | assertEquals(foo.getName(), "from test config"); 113 | assertEquals(foo2.getName(), "from test config2"); 114 | 115 | } 116 | 117 | } 118 | ``` 119 | 120 | 再查看输出的日志,就会发现Auto Configuration已经关闭。 121 | 122 | ## 例子3:避免@TestConfiguration被扫描到 123 | 124 | 在上面的这个例子里的[TestConfig][src-ex2-TestConfig]是会被@ComponentScan扫描到的,如果要避免被扫描到,在本文开头已经提到过了。 125 | 126 | 先来看一下没有做任何过滤的情形,我们先提供了一个@SpringBootConfiguration([IncludeConfig][src-ex3-IncludeConfig]): 127 | 128 | ```java 129 | @SpringBootConfiguration 130 | @ComponentScan 131 | public interface IncludeConfig { 132 | } 133 | ``` 134 | 135 | 然后有个测试代码引用了它([TestConfigIncludedTest][src-ex3-TestConfigIncludedTest]): 136 | 137 | ```java 138 | @SpringBootTest(classes = IncludeConfig.class) 139 | public class TestConfigIncludedTest extends AbstractTestNGSpringContextTests { 140 | 141 | @Autowired(required = false) 142 | private TestConfig testConfig; 143 | 144 | @Test 145 | public void testPlusCount() throws Exception { 146 | assertNotNull(testConfig); 147 | 148 | } 149 | 150 | } 151 | ``` 152 | 153 | 从这段代码可以看到`TestConfig`被加载了。 154 | 155 | 现在我们使用TypeExcludeFilter来过滤@TestConfiguration([ExcludeConfig1][src-ex3-ExcludeConfig1]): 156 | 157 | ```java 158 | @SpringBootConfiguration 159 | @ComponentScan(excludeFilters = { 160 | @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class) 161 | }) 162 | public interface ExcludeConfig1 { 163 | } 164 | ``` 165 | 166 | 再来看看结果([TestConfigExclude_1_Test][src-ex3-TestConfigExclude_1_Test]): 167 | 168 | ```java 169 | @SpringBootTest(classes = ExcludeConfig1.class) 170 | public class TestConfigExclude_1_Test extends AbstractTestNGSpringContextTests { 171 | 172 | @Autowired(required = false) 173 | private TestConfig testConfig; 174 | 175 | @Test 176 | public void test() throws Exception { 177 | assertNull(testConfig); 178 | 179 | } 180 | 181 | } 182 | ``` 183 | 184 | 还可以用@SpringBootApplication来排除`TestConfig`([ExcludeConfig2][src-ex3-ExcludeConfig2]): 185 | 186 | ```java 187 | @SpringBootApplication 188 | public interface ExcludeConfig2 { 189 | } 190 | ``` 191 | 192 | 看看结果([TestConfigExclude_2_Test][src-ex3-TestConfigExclude_2_Test]): 193 | 194 | ```java 195 | @SpringBootTest(classes = ExcludeConfig2.class) 196 | public class TestConfigExclude_2_Test extends AbstractTestNGSpringContextTests { 197 | 198 | @Autowired(required = false) 199 | private TestConfig testConfig; 200 | 201 | @Test 202 | public void testPlusCount() throws Exception { 203 | assertNull(testConfig); 204 | 205 | } 206 | 207 | } 208 | ``` 209 | 210 | ## 参考文档 211 | 212 | * [Spring Framework Testing][doc-spring-framework-testing] 213 | * [Spring Boot Testing][doc-spring-boot-testing] 214 | * [Detecting test configuration][doc-spring-boot-detecting-test-configuration] 215 | * [Excluding test configuration][doc-spring-boot-excluding-test-configuration] 216 | 217 | [chapter_1_s3_spring_boot_testing]: chapter_1_s3_spring_boot_testing.md 218 | 219 | [javadoc-test-configuration]: https://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/test/context/TestConfiguration.html 220 | [javadoc-test-component]: https://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/test/context/TestComponent.html 221 | [src-ex1-TestConfigurationTest]: annotation/src/test/java/me/chanjar/annotation/testconfig/ex1/TestConfigurationTest.java 222 | [src-ex2-Config]: annotation/src/test/java/me/chanjar/annotation/testconfig/ex2/Config.java 223 | [src-ex2-TestConfig]: annotation/src/test/java/me/chanjar/annotation/testconfig/ex2/TestConfig.java 224 | [src-ex2-TestConfigurationTest]: annotation/src/test/java/me/chanjar/annotation/testconfig/ex2/TestConfigurationTest.java 225 | [src-ex3-ExcludeConfig1]: annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/ExcludeConfig1.java 226 | [src-ex3-ExcludeConfig2]: annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/ExcludeConfig2.java 227 | [src-ex3-IncludeConfig]: annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/IncludeConfig.java 228 | [src-ex3-TestConfig]: annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/TestConfig.java 229 | [src-ex3-TestConfigExclude_1_Test]: annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/TestConfigExclude_1_Test.java 230 | [src-ex3-TestConfigExclude_2_Test]: annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/TestConfigExclude_2_Test.java 231 | [src-ex3-TestConfigIncludedTest]: annotation/src/test/java/me/chanjar/annotation/testconfig/ex3/TestConfigIncludedTest.java 232 | 233 | [doc-spring-framework-testing]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#testing 234 | [doc-spring-boot-testing]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-testing 235 | [doc-spring-boot-detecting-test-configuration]: https://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-detecting-config 236 | [doc-spring-boot-excluding-test-configuration]: https://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-excluding-config 237 | -------------------------------------------------------------------------------- /chapter_3_mockito.md: -------------------------------------------------------------------------------- 1 | # Chapter 3: 使用Mockito 2 | 3 | Mock测试技术能够避免你为了测试一个方法,却需要自行构建整个依赖关系的工作,并且能够让你专注于当前被测试对象的逻辑,而不是其依赖的其他对象的逻辑。 4 | 5 | 举例来说,比如你需要测试`Foo.methodA`,而这个方法依赖了`Bar.methodB`,又传递依赖到了`Zoo.methodC`,于是它们的依赖关系就是`Foo->Bar->Zoo`,所以在测试代码里你必须自行new Bar和Zoo。 6 | 7 | 有人会说:"我直接用Spring的DI机制不就行了吗?"的确,你可以用Spring的DI机制,不过解决不了测试代码耦合度过高的问题: 8 | 9 | 因为Foo方法内部调用了Bar和Zoo的方法,所以你对其做单元测试的时候,必须完全了解Bar和Zoo方法的内部逻辑,并且谨慎的传参和assert结果,一旦Bar和Zoo的代码修改了,你的Foo测试代码很可能就会运行失败。 10 | 11 | 所以这个时候我们需要一种机制,能过让我们在测试Foo的时候不依赖于Bar和Zoo的具体实现,即不关心其内部逻辑,只关注Foo内部的逻辑,从而将Foo的每个逻辑分支都测试到。 12 | 13 | 所以业界就产生了Mock技术,它可以让我们做一个假的Bar(不需要Zoo,因为只有真的Bar才需要Zoo),然后控制这个假的Bar的行为(让它返回什么就返回什么),以此来测试Foo的每个逻辑分支。 14 | 15 | 你肯定会问,这样的测试有意义吗?在真实环境里Foo用的是真的Bar而不是假的Bar,你用假的Bar测试成功能代表真实环境不出问题? 16 | 17 | 其实假Bar代表的是一个行为正确的Bar,用它来测试就能验证"在Bar行为正确的情况下Foo的行为是否正确",而真Bar的行为是否正确会由它自己的测试代码来验证。 18 | 19 | Mock技术的另一个好处是能够让你尽量避免集成测试,比如我们可以Mock一个Repository(数据库操作类),让我们尽量多写单元测试,提高测试代码执行效率。 20 | 21 | `spring-boot-starter-test`依赖了[Mockito][site-mockito],所以我们会在本章里使用Mockito来讲解。 22 | 23 | ## 被测试类 24 | 25 | 先介绍一下接下来要被我们测试的类[Foo][src-Foo]、[Bar][src-Bar]俩兄弟。 26 | 27 | ```java 28 | public interface Foo { 29 | 30 | boolean checkCodeDuplicate(String code); 31 | 32 | } 33 | 34 | public interface Bar { 35 | 36 | Set getAllCodes(); 37 | 38 | } 39 | 40 | @Component 41 | public class FooImpl implements Foo { 42 | 43 | private Bar bar; 44 | 45 | @Override 46 | public boolean checkCodeDuplicate(String code) { 47 | return bar.getAllCodes().contains(code); 48 | } 49 | 50 | @Autowired 51 | public void setBar(Bar bar) { 52 | this.bar = bar; 53 | } 54 | 55 | } 56 | ``` 57 | 58 | 59 | ## 例子1: 不使用Mock技术 60 | 61 | 源代码[NoMockTest][src-NoMockTest]: 62 | 63 | ```java 64 | public class NoMockTest { 65 | 66 | @Test 67 | public void testCheckCodeDuplicate1() throws Exception { 68 | 69 | FooImpl foo = new FooImpl(); 70 | foo.setBar(new Bar() { 71 | @Override 72 | public Set getAllCodes() { 73 | return Collections.singleton("123"); 74 | } 75 | }); 76 | assertEquals(foo.checkCodeDuplicate("123"), true); 77 | 78 | } 79 | 80 | @Test 81 | public void testCheckCodeDuplicate2() throws Exception { 82 | 83 | FooImpl foo = new FooImpl(); 84 | foo.setBar(new FakeBar(Collections.singleton("123"))); 85 | assertEquals(foo.checkCodeDuplicate("123"), true); 86 | 87 | } 88 | 89 | public class FakeBar implements Bar { 90 | 91 | private final Set codes; 92 | 93 | public FakeBar(Set codes) { 94 | this.codes = codes; 95 | } 96 | 97 | @Override 98 | public Set getAllCodes() { 99 | return codes; 100 | } 101 | 102 | } 103 | 104 | } 105 | ``` 106 | 107 | 这个测试代码里用到了两种方法来做假的Bar: 108 | 109 | 1. 匿名内部类 110 | 1. 做了一个``FakeBar`` 111 | 112 | 这两种方式都不是很优雅,看下面使用Mockito的例子。 113 | 114 | ## 例子2:使用Mockito 115 | 116 | 源代码[MockitoTest][src-MockitoTest]: 117 | 118 | ```java 119 | public class MockitoTest { 120 | 121 | @Mock 122 | private Bar bar; 123 | 124 | @InjectMocks 125 | private FooImpl foo; 126 | 127 | @BeforeMethod(alwaysRun = true) 128 | public void initMock() { 129 | MockitoAnnotations.initMocks(this); 130 | } 131 | 132 | @Test 133 | public void testCheckCodeDuplicate() throws Exception { 134 | 135 | when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); 136 | assertEquals(foo.checkCodeDuplicate("123"), true); 137 | 138 | } 139 | 140 | } 141 | ``` 142 | 143 | 1. 我们先给了一个Bar的Mock实现:`@Mock private Bar bar;` 144 | 1. 然后又规定了`getAllCodes`方法的返回值:`when(bar.getAllCodes()).thenReturn(Collections.singleton("123"))`。这样就把一个假的Bar定义好了。 145 | 1. 最后利用Mockito把Bar注入到Foo里面,`@InjectMocks private FooImpl foo;`、`MockitoAnnotations.initMocks(this);` 146 | 147 | 148 | ## 例子3:配合Spring Test 149 | 150 | 源代码[Spring_1_Test][src-Spring_1_Test]: 151 | 152 | ```java 153 | @ContextConfiguration(classes = FooImpl.class) 154 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 155 | public class Spring_1_Test extends AbstractTestNGSpringContextTests { 156 | 157 | @MockBean 158 | private Bar bar; 159 | 160 | @Autowired 161 | private Foo foo; 162 | 163 | @Test 164 | public void testCheckCodeDuplicate() throws Exception { 165 | 166 | when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); 167 | assertEquals(foo.checkCodeDuplicate("123"), true); 168 | 169 | } 170 | 171 | } 172 | ``` 173 | 174 | 要注意,如果要启用Spring和Mockito,必须添加这么一行:`@TestExecutionListeners(listeners = MockitoTestExecutionListener.class)`。 175 | 176 | 177 | ## 例子4:配合Spring Test(多层依赖) 178 | 179 | 当Bean存在这种依赖关系当时候:`LooImpl -> FooImpl -> Bar`,我们应该怎么测试呢? 180 | 181 | 按照Mock测试的原则,这个时候我们应该mock一个`Foo`对象,把这个注入到`LooImpl`对象里,就像例子3里的一样。 182 | 183 | 不过如果你不想mock `Foo`而是想mock `Bar`的时候,其实做法和前面也差不多,Spring会自动将mock Bar注入到`FooImpl`中,然后将`FooImpl`注入到`LooImpl`中。 184 | 185 | 源代码[Spring_2_Test][src-Spring_2_Test]: 186 | 187 | ```java 188 | @ContextConfiguration(classes = { FooImpl.class, LooImpl.class }) 189 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 190 | public class Spring_2_Test extends AbstractTestNGSpringContextTests { 191 | 192 | @MockBean 193 | private Bar bar; 194 | 195 | @Autowired 196 | private Loo loo; 197 | 198 | @Test 199 | public void testCheckCodeDuplicate() throws Exception { 200 | 201 | when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); 202 | assertEquals(loo.checkCodeDuplicate("123"), true); 203 | 204 | } 205 | 206 | } 207 | ``` 208 | 209 | 也就是说,得益于Spring Test Framework,我们能够很方便地对依赖关系中任意层级的任意Bean做mock。 210 | 211 | ## 例子5:配合Spring Boot Test 212 | 213 | 源代码[Boot_1_Test][src-Boot_1_Test]: 214 | 215 | ```java 216 | @SpringBoot_1_Test(classes = { FooImpl.class }) 217 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 218 | public class Boot_1_Test extends AbstractTestNGSpringContextTests { 219 | 220 | @MockBean 221 | private Bar bar; 222 | 223 | @Autowired 224 | private Foo foo; 225 | 226 | @Test 227 | public void testCheckCodeDuplicate() throws Exception { 228 | 229 | when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); 230 | assertEquals(foo.checkCodeDuplicate("123"), true); 231 | 232 | } 233 | 234 | } 235 | ``` 236 | 237 | 238 | ## 参考文档 239 | 240 | * [Spring Framework Testing][doc-spring-framework-testing] 241 | * [Spring Boot Testing][doc-spring-boot-testing] 242 | * [Mockito][site-mockito] 243 | 244 | [doc-spring-framework-testing]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#testing 245 | [doc-spring-boot-testing]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-testing 246 | [site-mockito]: http://site.mockito.org 247 | [src-Bar]: mock/src/main/java/me/chanjar/common/Bar.java 248 | [src-Foo]: mock/src/main/java/me/chanjar/common/Foo.java 249 | [src-FooController]: mock/src/main/java/me/chanjar/common/FooController.java 250 | [src-FooImpl]: mock/src/main/java/me/chanjar/common/FooImpl.java 251 | [src-FakeBar]: mock/src/test/java/me/chanjar/no_mock/FakeBar.java 252 | [src-NoMockTest]: mock/src/test/java/me/chanjar/no_mock/NoMockTest.java 253 | [src-Spring_1_Test]: mock/src/test/java/me/chanjar/spring1/Spring_1_Test.java 254 | [src-Spring_2_Test]: mock/src/test/java/me/chanjar/spring2/Spring_2_Test.java 255 | [src-Boot_1_Test]: mock/src/test/java/me/chanjar/springboot1/Boot_1_Test.java 256 | -------------------------------------------------------------------------------- /chapter_4_s1_basic.md: -------------------------------------------------------------------------------- 1 | # Chapter 4: 测试关系型数据库 - 基本做法 2 | 3 | [Spring Test Framework][doc-spring-framework-testing]提供了对[JDBC的支持][doc-spring-testing-jdbc],能够让我们很方便对关系型数据库做集成测试。 4 | 5 | 同时Spring Boot提供了和[Flyway][site-flyway]的[集成][doc-spring-boot-flyway]支持,能够方便的管理开发过程中产生的SQL文件,配合Spring已经提供的工具能够更方便地在测试之前初始化数据库以及测试之后清空数据库。 6 | 7 | 本章节为了方便起见,本章节使用了H2作为测试数据库。 8 | 9 | 注意:**在真实的开发环境中,集成测试用数据库应该和最终的生产数据库保持一致**,这是因为不同数据库的对于SQL不是完全相互兼容的,如果不注意这一点,很有可能出现集成测试通过,但是上了生产环境却报错的问题。 10 | 11 | 因为是集成测试,所以我们使用了``maven-failsafe-plugin``来跑,它和``maven-surefire-plugin``的差别在于,``maven-failsafe-plugin``只会搜索``*IT.java``来跑测试,而``maven-surefire-plugin``只会搜索``*Test.java``来跑测试。 12 | 13 | 如果想要在maven打包的时候跳过集成测试,只需要``mvn clean install -DskipITs``。 14 | 15 | ## 被测试类 16 | 17 | 先介绍一下被测试的类。 18 | 19 | [Foo.java][src-Foo.java]: 20 | 21 | ```java 22 | public class Foo { 23 | 24 | private String name; 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | } 34 | ``` 35 | 36 | [FooRepositoryImpl.java][src-FooRepositoryImpl.java]: 37 | 38 | ```java 39 | @Repository 40 | public class FooRepositoryImpl implements FooRepository { 41 | 42 | private JdbcTemplate jdbcTemplate; 43 | 44 | @Override 45 | public void save(Foo foo) { 46 | jdbcTemplate.update("INSERT INTO FOO(name) VALUES (?)", foo.getName()); 47 | } 48 | 49 | @Override 50 | public void delete(String name) { 51 | jdbcTemplate.update("DELETE FROM FOO WHERE NAME = ?", name); 52 | } 53 | 54 | @Autowired 55 | public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 56 | this.jdbcTemplate = jdbcTemplate; 57 | } 58 | 59 | } 60 | 61 | ``` 62 | 63 | ## 例子1:不使用Spring Testing提供的工具 64 | 65 | [Spring_1_IT_Configuration.java][src-Spring_1_IT_Configuration.java]: 66 | 67 | ```java 68 | @Configuration 69 | @ComponentScan(basePackageClasses = FooRepository.class) 70 | public class Spring_1_IT_Configuration { 71 | 72 | @Bean(destroyMethod = "shutdown") 73 | public DataSource dataSource() { 74 | 75 | return new EmbeddedDatabaseBuilder() 76 | .generateUniqueName(true) 77 | .setType(EmbeddedDatabaseType.H2) 78 | .setScriptEncoding("UTF-8") 79 | .ignoreFailedDrops(true) 80 | .addScript("classpath:me/chanjar/domain/foo-ddl.sql") 81 | .build(); 82 | } 83 | 84 | @Bean 85 | public JdbcTemplate jdbcTemplate() { 86 | 87 | return new JdbcTemplate(dataSource()); 88 | 89 | } 90 | } 91 | ``` 92 | 93 | 在``Spring_1_IT_Configuration``中,我们定义了一个H2的DataSource Bean,并且构建了JdbcTemplate Bean。 94 | 95 | 注意看``addScript("classpath:me/chanjar/domain/foo-ddl.sql")``这句代码,我们让``EmbeddedDatabase``执行[foo-ddl.sql][src-foo-ddl.sql]脚本来建表: 96 | 97 | ```sql 98 | CREATE TABLE FOO ( 99 | name VARCHAR2(100) 100 | ); 101 | ``` 102 | 103 | [Spring_1_IT.java][src-Spring_1_IT.java]: 104 | 105 | ```java 106 | @ContextConfiguration(classes = Spring_1_IT_Configuration.class) 107 | public class Spring_1_IT extends AbstractTestNGSpringContextTests { 108 | 109 | @Autowired 110 | private FooRepository fooRepository; 111 | 112 | @Autowired 113 | private JdbcTemplate jdbcTemplate; 114 | 115 | @Test 116 | public void testSave() { 117 | 118 | Foo foo = new Foo(); 119 | foo.setName("Bob"); 120 | fooRepository.save(foo); 121 | 122 | assertEquals( 123 | jdbcTemplate.queryForObject("SELECT count(*) FROM FOO", Integer.class), 124 | Integer.valueOf(1) 125 | ); 126 | 127 | } 128 | 129 | @Test(dependsOnMethods = "testSave") 130 | public void testDelete() { 131 | 132 | assertEquals( 133 | jdbcTemplate.queryForObject("SELECT count(*) FROM FOO", Integer.class), 134 | Integer.valueOf(1) 135 | ); 136 | 137 | Foo foo = new Foo(); 138 | foo.setName("Bob"); 139 | fooRepository.save(foo); 140 | 141 | fooRepository.delete(foo.getName()); 142 | assertEquals( 143 | jdbcTemplate.queryForObject("SELECT count(*) FROM FOO", Integer.class), 144 | Integer.valueOf(0) 145 | ); 146 | } 147 | 148 | } 149 | ``` 150 | 151 | 在这段测试代码里可以看到,我们分别测试了``FooRepository``的``save``和``delete``方法,并且利用``JdbcTemplate``来验证数据库中的结果。 152 | 153 | 154 | ## 例子2:使用Spring Testing提供的工具 155 | 156 | 在这个例子里,我们会使用[JdbcTestUtils][doc-spring-testing-jdbc]来辅助测试。 157 | 158 | [Spring_2_IT_Configuration.java][src-Spring_2_IT_Configuration.java]: 159 | 160 | ```java 161 | @Configuration 162 | @ComponentScan(basePackageClasses = FooRepository.class) 163 | public class Spring_2_IT_Configuration { 164 | 165 | @Bean 166 | public DataSource dataSource() { 167 | 168 | EmbeddedDatabase db = new EmbeddedDatabaseBuilder() 169 | .generateUniqueName(true) 170 | .setType(EmbeddedDatabaseType.H2) 171 | .setScriptEncoding("UTF-8") 172 | .ignoreFailedDrops(true) 173 | .addScript("classpath:me/chanjar/domain/foo-ddl.sql") 174 | .build(); 175 | return db; 176 | } 177 | 178 | @Bean 179 | public JdbcTemplate jdbcTemplate() { 180 | 181 | return new JdbcTemplate(dataSource()); 182 | 183 | } 184 | 185 | @Bean 186 | public PlatformTransactionManager transactionManager() { 187 | return new DataSourceTransactionManager(dataSource()); 188 | } 189 | 190 | } 191 | ``` 192 | 193 | 这里和例子1的区别在于,我们提供了一个``PlatformTransactionManager`` Bean,这是因为在下面的测试代码里的``AbstractTransactionalTestNGSpringContextTests``需要它。 194 | 195 | [Spring_2_IT.java][src-Spring_2_IT.java]: 196 | 197 | ```java 198 | @ContextConfiguration(classes = Spring_2_IT_Configuration.class) 199 | public class Spring_2_IT extends AbstractTransactionalTestNGSpringContextTests { 200 | 201 | @Autowired 202 | private FooRepository fooRepository; 203 | 204 | @Test 205 | public void testSave() { 206 | 207 | Foo foo = new Foo(); 208 | foo.setName("Bob"); 209 | fooRepository.save(foo); 210 | 211 | assertEquals(countRowsInTable("FOO"), 1); 212 | countRowsInTableWhere("FOO", "name = 'Bob'"); 213 | } 214 | 215 | @Test(dependsOnMethods = "testSave") 216 | public void testDelete() { 217 | 218 | assertEquals(countRowsInTable("FOO"), 0); 219 | 220 | Foo foo = new Foo(); 221 | foo.setName("Bob"); 222 | fooRepository.save(foo); 223 | 224 | fooRepository.delete(foo.getName()); 225 | assertEquals(countRowsInTable("FOO"), 0); 226 | 227 | } 228 | 229 | } 230 | ``` 231 | 232 | 在这里我们使用``countRowsInTable("FOO")``来验证数据库结果,这个方法是``AbstractTransactionalTestNGSpringContextTests``对``JdbcTestUtils``的代理。 233 | 234 | 而且要注意的是,每个测试方法在执行完毕后,会自动rollback,所以在``testDelete``的第一行里,我们``assertEquals(countRowsInTable("FOO"), 0)``,这一点和例子1里是不同的。 235 | 236 | 更多关于Spring Testing Framework与Transaction相关的信息,可以见Spring官方文档 [Transaction management][doc-spring-testing-tx]。 237 | 238 | ## 例子3:使用Spring Boot 239 | 240 | [Boot_1_IT.java][src-Boot_1_IT.java]: 241 | 242 | ```java 243 | @SpringBootTest 244 | @SpringBootApplication(scanBasePackageClasses = FooRepository.class) 245 | public class Boot_1_IT extends AbstractTransactionalTestNGSpringContextTests { 246 | 247 | @Autowired 248 | private FooRepository fooRepository; 249 | 250 | @Test 251 | public void testSave() { 252 | 253 | Foo foo = new Foo(); 254 | foo.setName("Bob"); 255 | fooRepository.save(foo); 256 | 257 | assertEquals(countRowsInTable("FOO"), 1); 258 | countRowsInTableWhere("FOO", "name = 'Bob'"); 259 | } 260 | 261 | @Test(dependsOnMethods = "testSave") 262 | public void testDelete() { 263 | 264 | assertEquals(countRowsInTable("FOO"), 0); 265 | 266 | Foo foo = new Foo(); 267 | foo.setName("Bob"); 268 | fooRepository.save(foo); 269 | 270 | fooRepository.delete(foo.getName()); 271 | assertEquals(countRowsInTable("FOO"), 0); 272 | 273 | } 274 | 275 | @AfterTest 276 | public void cleanDb() { 277 | flyway.clean(); 278 | } 279 | 280 | } 281 | ``` 282 | 283 | 因为使用了Spring Boot来做集成测试,得益于其AutoConfiguration机制,不需要自己构建``DataSource`` 、``JdbcTemplate``和``PlatformTransactionManager``的Bean。 284 | 285 | 并且因为我们已经将``flyway-core``添加到了maven依赖中,Spring Boot会利用flyway来帮助我们初始化数据库,我们需要做的仅仅是将sql文件放到classpath的``db/migration``目录下: 286 | 287 | ``V1.0.0__foo-ddl.sql``: 288 | 289 | ```sql 290 | 291 | CREATE TABLE FOO ( 292 | name VARCHAR2(100) 293 | ); 294 | ``` 295 | 296 | 而且在测试最后,我们利用flyway清空了数据库: 297 | 298 | ```java 299 | @AfterTest 300 | public void cleanDb() { 301 | flyway.clean(); 302 | } 303 | ``` 304 | 305 | 使用flyway有很多好处: 306 | 307 | 1. 每个sql文件名都规定了版本号 308 | 1. flyway按照版本号顺序执行 309 | 1. 在开发期间,只需要将sql文件放到db/migration目录下就可以了,不需要写类似``EmbeddedDatabaseBuilder.addScript()``这样的代码 310 | 1. 基于以上三点,就能够将数据库初始化SQL语句也纳入到集成测试中来,保证代码配套的SQL语句的正确性 311 | 1. 可以帮助你清空数据库,这在你使用非内存数据库的时候非常有用,因为不管测试前还是测试后,你都需要一个干净的数据库 312 | 313 | ## 参考文档 314 | 315 | 本章节涉及到的Spring Testing Framework JDBC、SQL相关的工具: 316 | 317 | * [Transaction management][doc-spring-testing-tx] 318 | * [Executing SQL scripts][doc-spring-testing-sql] 319 | 320 | 和flyway相关的: 321 | 322 | * [flyway的官方文档][doc-flyway] 323 | * [flway和spring boot的集成][doc-spring-boot-flyway] 324 | 325 | [doc-spring-boot-flyway]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#howto-execute-flyway-database-migrations-on-startup 326 | [doc-spring-framework-testing]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#testing 327 | [doc-spring-boot-testing]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-testing 328 | [doc-spring-testing-jdbc]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#integration-testing-support-jdbc 329 | [doc-spring-boot-flyway]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#howto-execute-flyway-database-migrations-on-startup 330 | [site-flyway]: https://flywaydb.org/ 331 | [doc-flyway]: https://flywaydb.org/documentation/ 332 | [doc-spring-testing-tx]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#testcontext-tx 333 | [doc-spring-testing-sql]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#testcontext-executing-sql 334 | [src-Foo.java]: rdbs/src/main/java/me/chanjar/domain/Foo.java 335 | [src-FooRepositoryImpl.java]: rdbs/src/main/java/me/chanjar/domain/FooRepositoryImpl.java 336 | [src-foo-ddl.sql]: rdbs/src/main/resources/me/chanjar/domain/foo-ddl.sql 337 | [src-Spring_1_IT.java]: rdbs/src/test/java/me/chanjar/spring1/Spring_1_IT.java 338 | [src-Spring_1_IT_Configuration.java]: rdbs/src/test/java/me/chanjar/spring1/Spring_1_IT_Configuration.java 339 | [src-Spring_2_IT.java]: rdbs/src/test/java/me/chanjar/spring2/Spring_2_IT.java 340 | [src-Spring_2_IT_Configuration.java]: rdbs/src/test/java/me/chanjar/spring2/Spring_2_IT_Configuration.java 341 | [src-Boot_1_IT.java]: rdbs/src/test/java/me/chanjar/springboot1/Boot_1_IT.java 342 | -------------------------------------------------------------------------------- /chapter_4_s2_using_docker.md: -------------------------------------------------------------------------------- 1 | # Chapter 4: 测试关系型数据库 - 使用Docker创建临时数据库 2 | 3 | 在[Chapter 4: 测试关系型数据库 - 基本做法][chapter_4_s1_basic]里我们使用的是H2数据库,这是为了让你免去你去安装/配置一个数据库的工作,能够尽快的了解到集成测试的过程。 4 | 5 | 在文章里也说了: 6 | 7 | > 在真实的开发环境中,集成测试用数据库应该和最终的生产数据库保持一致 8 | 9 | 那么很容易就能想到两种解决方案: 10 | 11 | 1. 开发团队使用共用同一个数据库。这样做的问题在于:当有多个集成测试同时在跑时,会产生错误的测试结果。 12 | 2. 每个人使用自己的数据库。这样做的问题在于让开发人员维护MySQL数据库挺麻烦的。 13 | 14 | 那么做到能否这样呢? 15 | 16 | 1. 测试启动前,创建一个MySQL数据库 17 | 2. 测试过程中连接到这个数据库 18 | 3. 测试结束后,删除这个MySQL数据库 19 | 20 | So, Docker comes to the rescue。 21 | 22 | 我们还是会以[Chapter 4: 测试关系型数据库 - 基本做法][chapter_4_s1_basic]里的`FooRepositoryImpl`来做集成测试([代码在这里][code-rdbs-docker])。下面来讲解具体步骤: 23 | 24 | ## 安装Docker 25 | 26 | 请查阅官方文档。并且掌握Docker的基本概念。 27 | 28 | ## 配置properties 29 | 30 | 先定义几个properties: 31 | 32 | ```xml 33 | 34 | 0.28.0 35 | 2.22.0 36 | 2.19 37 | 38 | false 39 | 40 | ${skipTests} 41 | 42 | ${skipTests} 43 | 44 | ``` 45 | 46 | ## 配置fabric8 docker-maven-plugin 47 | 48 | [farbic8 docker-maven-plugin][fabric8-dmp]顾名思义就是一个能够使用docker的maven plugin。它主要功能有二: 49 | 50 | 1. [创建Docker image][fabric8-dmp-build] 51 | 2. [启动Docker container][fabric8-dmp-start] 52 | 53 | 我们这里使用**启动Docker container**的功能。 54 | 55 | 大致配置如下 56 | 57 | ```xml 58 | 59 | io.fabric8 60 | docker-maven-plugin 61 | ${fabric8.dmp.version} 62 | 63 | 64 | 65 | ${skipITs} 66 | 67 | 68 | 69 | mysql:8 70 | 71 | 72 | 73 | 75 | docker-mysql.port:3306 76 | 77 | 78 | 79 | 123456 80 | test 81 | foo 82 | bar 83 | 84 | 85 | 86 | 87 | MySQL init process done. Ready for start up. 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | start 99 | pre-integration-test 100 | 101 | start 102 | 103 | 104 | 105 | 106 | stop 107 | post-integration-test 108 | 109 | stop 110 | 111 | 112 | 113 | 114 | ``` 115 | 116 | ## 配置maven-failsafe-plugin 117 | 118 | maven-failsafe-plugin用来执行集成测试`*IT.java`。 119 | 120 | ```xml 121 | 122 | org.apache.maven.plugins 123 | maven-failsafe-plugin 124 | ${maven-failsafe-plugin.version} 125 | 126 | 127 | integration-test 128 | 129 | integration-test 130 | 131 | 132 | 133 | verify 134 | 135 | verify 136 | 137 | 138 | 139 | 140 | 141 | ${skipTests} 142 | 143 | ${skipITs} 144 | 147 | 148 | jdbc:mysql://localhost:${docker-mysql.port}/test 149 | foo 150 | bar 151 | 152 | 153 | 154 | ``` 155 | 156 | ## 配置maven-surefire-plugin 157 | 158 | maven-surefire-plugin用来执行单元测试`*Test.java`。 159 | 160 | ```xml 161 | 162 | org.apache.maven.plugins 163 | maven-surefire-plugin 164 | ${maven-surefire-plugin.version} 165 | 166 | ${skipUTs} 167 | 168 | 169 | ``` 170 | 171 | ## 执行 172 | 173 | 三种常见用法: 174 | 175 | * `mvn clean integration-test`,会启动docker container、运行集成测试。这个很有用,如果集成测试失败,那么你还可以连接到MySQL数据库查看情况。 176 | * `mvn clean verify`,会执行`mvn integration-test`、删除docker container。 177 | * `mvn clean install`,会执`mvn verify`,并将包安装到本地maven 仓库。 178 | 179 | 配合`-DskipTests`、`-DskipITs`、`-DskipUTs`可以控制是否跳过所有测试、紧跳过集成测试、仅跳过单元测试。 180 | 181 | 下面是`mvn clean verify`的日志: 182 | 183 | ```txt 184 | ... 185 | [INFO] --- docker-maven-plugin:0.28.0:start (start) @ spring-test-examples-rdbs-docker --- 186 | [INFO] DOCKER> [mysql:8]: Start container f683aadfe8ba 187 | [INFO] DOCKER> Pattern 'MySQL init process done. Ready for start up.' matched for container f683aadfe8ba 188 | [INFO] DOCKER> [mysql:8]: Waited on log out 'MySQL init process done. Ready for start up.' 13717 ms 189 | [INFO] 190 | [INFO] --- maven-failsafe-plugin:2.22.1:integration-test (integration-test) @ spring-test-examples-rdbs-docker --- 191 | [INFO] 192 | [INFO] ------------------------------------------------------- 193 | [INFO] T E S T S 194 | [INFO] ------------------------------------------------------- 195 | ... 196 | [INFO] 197 | [INFO] Results: 198 | [INFO] 199 | [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 200 | [INFO] 201 | [INFO] 202 | [INFO] --- docker-maven-plugin:0.28.0:stop (stop) @ spring-test-examples-rdbs-docker --- 203 | [INFO] DOCKER> [mysql:8]: Stop and removed container f683aadfe8ba after 0 ms 204 | [INFO] 205 | [INFO] --- maven-failsafe-plugin:2.22.1:verify (verify) @ spring-test-examples-rdbs-docker --- 206 | [INFO] ------------------------------------------------------------------------ 207 | [INFO] BUILD SUCCESS 208 | [INFO] ------------------------------------------------------------------------ 209 | ... 210 | ``` 211 | 212 | 可以看到fabric8 dmp在集成测试前后start和stop容器的相关日志,且测试成功。 213 | 214 | 如何找到MySQL的端口开在哪一个呢?运行`docker ps`查看端口(注意下面的`0.0.0.0:32798->3306/tcp`): 215 | 216 | ```txt 217 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 218 | a1f4b51d7c75 mysql:8 ... ... Up 19... 33060/tcp, 0.0.0.0:32798->3306/tcp mysql-1 219 | ``` 220 | 221 | ## 在Intellij IDEA中运行JUnit的问题 222 | 223 | 本文中用到的pom.xml同样可适用于JUnit编写的单元、集成测试。 224 | 225 | 在Intellij IDEA中直接执行JUnit测试的时候会读取maven-failsafe-plugin的`systemPropertyVariables`,这样会产生问题: 226 | 你想用一个临时的application.properties来执行JUnit测试,但是IDEA的JUnit插件读取了maven-failsafe-plugin的`systemPropertyVariables`, 227 | 而这些东西覆盖了application.properties里的内容,并且它们的值还可能是错的。 228 | 229 | 解决办法是将maven-failsafe-plugin和docker-maven-plugin放到profile中: 230 | 231 | ```xml 232 | 233 | 234 | integration-test 235 | 236 | 237 | 238 | org.apache.maven.plugins 239 | maven-failsafe-plugin 240 | ${maven-failsafe-plugin.version} 241 | ... 242 | 243 | 244 | 245 | io.fabric8 246 | docker-maven-plugin 247 | ${fabric8.dmp.version} 248 | ... 249 | 250 | 251 | 252 | 253 | 254 | 255 | ``` 256 | 257 | 然后使用`mvn clean install -Pintegration-test`来启用它。 258 | 259 | ## 参考文档 260 | 261 | * [Fabric8 dmp][fabric8-dmp] 262 | * [Spring boot - Externalized Configuration][doc-spring-boot-ext-config] 263 | 264 | 265 | [chapter_4_s1_basic]: chapter_4_s1_basic.md 266 | [code-rdbs-docker]:rdbs-docker 267 | [fabric8-dmp]: https://dmp.fabric8.io/ 268 | 269 | [fabric8-dmp-build]: https://dmp.fabric8.io/#docker:build 270 | [fabric8-dmp-start]: https://dmp.fabric8.io/#docker:start 271 | 272 | [doc-spring-boot-ext-config]: https://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/html/boot-features-external-config.html 273 | -------------------------------------------------------------------------------- /chapter_5_mvc.md: -------------------------------------------------------------------------------- 1 | # Chapter 5: 测试Spring MVC 2 | 3 | [Spring Testing Framework][doc-spring-framework-testing]提供了[Spring MVC Test Framework][doc-spring-mvc-test-framework],能够很方便的来测试Controller。同时Spring Boot也提供了[Auto-configured Spring MVC tests][doc-auto-configured-spring-mvc-tests]更进一步简化了测试需要的配置工作。 4 | 5 | 本章节将分别举例说明在不使用Spring Boot和使用Spring Boot下如何对Spring MVC进行测试。 6 | 7 | ## 例子1:Spring 8 | 9 | 测试Spring MVC的关键是使用`MockMvc`对象,利用它我们能够在不需启动Servlet容器的情况下测试Controller的行为。 10 | 11 | 源代码[SpringMvc_1_Test.java][src-SpringMvc_1_Test.java]: 12 | 13 | ```java 14 | @EnableWebMvc 15 | @WebAppConfiguration 16 | @ContextConfiguration(classes = { FooController.class, FooImpl.class }) 17 | public class SpringMvc_1_Test extends AbstractTestNGSpringContextTests { 18 | 19 | @Autowired 20 | private WebApplicationContext wac; 21 | 22 | private MockMvc mvc; 23 | 24 | @BeforeMethod 25 | public void prepareMockMvc() { 26 | this.mvc = webAppContextSetup(wac).build(); 27 | } 28 | 29 | @Test 30 | public void testController() throws Exception { 31 | 32 | this.mvc.perform(get("/foo/check-code-dup").param("code", "123")) 33 | .andDo(print()) 34 | .andExpect(status().isOk()) 35 | .andExpect(content().string("true")); 36 | 37 | } 38 | 39 | } 40 | ``` 41 | 42 | 在这段代码里,主要有三个步骤: 43 | 44 | 1. 将测试类标记为`@WebAppConfiguration` 45 | 1. 通过`webAppContextSetup(wac).build()`构建`MockMvc` 46 | 1. 利用`MockMvc`对结果进行判断 47 | 48 | ## 例子2:Spring + Mock 49 | 50 | 在例子1里,`FooController`使用了一个实体`FooImpl`的Bean,实际上我们也可以提供一个`Foo`的mock bean来做测试,这样就能够更多的控制测试过程。如果你还不知道Mock那么请看[Chapter 3: 使用Mockito][chapter_3_mockito]。 51 | 52 | 源代码[SpringMvc_2_Test.java][src-SpringMvc_2_Test.java]: 53 | 54 | ```java 55 | @EnableWebMvc 56 | @WebAppConfiguration 57 | @ContextConfiguration(classes = { FooController.class }) 58 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 59 | public class SpringMvc_2_Test extends AbstractTestNGSpringContextTests { 60 | 61 | @Autowired 62 | private WebApplicationContext wac; 63 | 64 | @MockBean 65 | private Foo foo; 66 | 67 | private MockMvc mvc; 68 | 69 | @BeforeMethod 70 | public void prepareMockMvc() { 71 | this.mvc = webAppContextSetup(wac).build(); 72 | } 73 | 74 | @Test 75 | public void testController() throws Exception { 76 | 77 | when(foo.checkCodeDuplicate(anyString())).thenReturn(true); 78 | 79 | this.mvc.perform(get("/foo/check-code-dup").param("code", "123")) 80 | .andDo(print()) 81 | .andExpect(status().isOk()) 82 | .andExpect(content().string("true")); 83 | 84 | } 85 | 86 | } 87 | ``` 88 | 89 | 90 | ## 例子3:Spring Boot 91 | 92 | Spring Boot提供了`@WebMvcTest`更进一步简化了对于Spring MVC的测试,我们提供了对应例子1的Spring Boot版本。 93 | 94 | 源代码[BootMvc_1_Test.java][src-BootMvc_1_Test.java]: 95 | 96 | ```java 97 | @WebMvcTest 98 | @ContextConfiguration(classes = { FooController.class, FooImpl.class }) 99 | public class BootMvc_1_Test extends AbstractTestNGSpringContextTests { 100 | 101 | @Autowired 102 | private MockMvc mvc; 103 | 104 | @Test 105 | public void testController() throws Exception { 106 | 107 | this.mvc.perform(get("/foo/check-code-dup").param("code", "123")) 108 | .andDo(print()) 109 | .andExpect(status().isOk()) 110 | .andExpect(content().string("true")); 111 | 112 | } 113 | 114 | } 115 | ``` 116 | 117 | 在这里,我们不需要自己构建`MockMvc`,直接使用`@Autowired`注入就行了,是不是很方便? 118 | 119 | ## 例子4:Spring Boot + Mock 120 | 121 | 这个是对应例子2的Spring Boot版本,源代码[BootMvc_2_Test.java][src-BootMvc_2_Test.java]: 122 | 123 | ```java 124 | @WebMvcTest 125 | @ContextConfiguration(classes = { FooController.class }) 126 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 127 | public class BootMvc_2_Test extends AbstractTestNGSpringContextTests { 128 | 129 | @Autowired 130 | private MockMvc mvc; 131 | 132 | @MockBean 133 | private Foo foo; 134 | 135 | @Test 136 | public void testController() throws Exception { 137 | 138 | when(foo.checkCodeDuplicate(anyString())).thenReturn(true); 139 | 140 | this.mvc.perform(get("/foo/check-code-dup").param("code", "123")) 141 | .andDo(print()) 142 | .andExpect(status().isOk()) 143 | .andExpect(content().string("true")); 144 | 145 | } 146 | 147 | } 148 | ``` 149 | 150 | 151 | ## 参考文档 152 | 153 | * [Loading a WebApplicationContext][doc-spring-WebApplicationContext] 154 | * [Spring MVC Test Framework][doc-spring-mvc-test-framework] 155 | * [Spring MVC Official Sample Tests][gh-spring-mvc-official-sample-tests] 156 | * [Spring MVC showcase - with full mvc test][gh-spring-mvc-showcase] 157 | * [Auto-configured Spring MVC tests][doc-auto-configured-spring-mvc-tests] 158 | * [Spring Framework Testing][doc-spring-framework-testing] 159 | * [Spring Boot Testing][doc-spring-boot-testing] 160 | * [Spring Guides - Testing the Web Layer][guide-testing-the-web-layer] 161 | 162 | [chapter_3_mockito]: chapter_3_mockito.md 163 | [guide-testing-the-web-layer]: https://spring.io/guides/gs/testing-web/ 164 | [doc-spring-framework-testing]: http://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#testing 165 | [doc-spring-WebApplicationContext]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/integration-testing.html#testcontext-ctx-management-web 166 | [doc-spring-boot-testing]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-testing 167 | [javadoc-AutoConfigureMockMvc]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/api/org/springframework/boot/test/autoconfigure/web/servlet/AutoConfigureMockMvc.html 168 | [doc-auto-configured-spring-mvc-tests]: http://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/reference/htmlsingle/#boot-features-testing-spring-boot-applications-testing-autoconfigured-mvc-tests 169 | [doc-spring-mvc-test-framework]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/htmlsingle/#spring-mvc-test-framework 170 | [gh-spring-mvc-official-sample-tests]: https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/test/java/org/springframework/test/web/servlet/samples 171 | [gh-spring-mvc-showcase]: https://github.com/spring-projects/spring-mvc-showcase 172 | [src-SpringMvc_1_Test.java]: mvc/src/test/java/me/chanjar/spring1/SpringMvc_1_Test.java 173 | [src-SpringMvc_2_Test.java]: mvc/src/test/java/me/chanjar/spring2/SpringMvc_2_Test.java 174 | [src-BootMvc_1_Test.java]: mvc/src/test/java/me/chanjar/springboot1/BootMvc_1_Test.java 175 | [src-BootMvc_2_Test.java]: mvc/src/test/java/me/chanjar/springboot2/BootMvc_2_Test.java 176 | -------------------------------------------------------------------------------- /chapter_6_aop.md: -------------------------------------------------------------------------------- 1 | # Chapter 6: 测试AOP 2 | 3 | Spring提供了一套[AOP工具][doc-spring-aop],但是当你把各种Aspect写完之后,如何确定这些Aspect都正确的应用到目标Bean上了呢?本章将举例说明如何对Spring AOP做测试。 4 | 5 | 首先先来看我们事先定义的Bean以及Aspect。 6 | 7 | [FooServiceImpl][src-FooServiceImpl]: 8 | 9 | ```java 10 | @Component 11 | public class FooServiceImpl implements FooService { 12 | 13 | private int count; 14 | 15 | @Override 16 | public int incrementAndGet() { 17 | count++; 18 | return count; 19 | } 20 | 21 | } 22 | ``` 23 | 24 | [FooAspect][src-FooAspect]: 25 | 26 | ```java 27 | @Component 28 | @Aspect 29 | public class FooAspect { 30 | 31 | @Pointcut("execution(* me.chanjar.aop.service.FooServiceImpl.incrementAndGet())") 32 | public void pointcut() { 33 | } 34 | 35 | @Around("pointcut()") 36 | public int changeIncrementAndGet(ProceedingJoinPoint pjp) { 37 | return 0; 38 | } 39 | 40 | } 41 | ``` 42 | 43 | 可以看到`FooAspect`会修改`FooServiceImpl.incrementAndGet`方法的返回值,使其返回0。 44 | 45 | ## 例子1:测试FooService的行为 46 | 47 | 最简单的测试方法就是直接调用`FooServiceImpl.incrementAndGet`,看看它是否使用返回0。 48 | 49 | [SpringAop_1_Test][src-ex1-SpringAop_1_Test]: 50 | 51 | ```java 52 | @ContextConfiguration(classes = { SpringAopTest.class, AopConfig.class }) 53 | public class SpringAop_1_Test extends AbstractTestNGSpringContextTests { 54 | 55 | @Autowired 56 | private FooService fooService; 57 | 58 | @Test 59 | public void testFooService() { 60 | 61 | assertNotEquals(fooService.getClass(), FooServiceImpl.class); 62 | 63 | assertTrue(AopUtils.isAopProxy(fooService)); 64 | assertTrue(AopUtils.isCglibProxy(fooService)); 65 | 66 | assertEquals(AopProxyUtils.ultimateTargetClass(fooService), FooServiceImpl.class); 67 | 68 | assertEquals(AopTestUtils.getTargetObject(fooService).getClass(), FooServiceImpl.class); 69 | assertEquals(AopTestUtils.getUltimateTargetObject(fooService).getClass(), FooServiceImpl.class); 70 | 71 | assertEquals(fooService.incrementAndGet(), 0); 72 | assertEquals(fooService.incrementAndGet(), 0); 73 | 74 | } 75 | 76 | } 77 | ``` 78 | 79 | 先看这段代码: 80 | 81 | ```java 82 | assertNotEquals(fooService.getClass(), FooServiceImpl.class); 83 | 84 | assertTrue(AopUtils.isAopProxy(fooService)); 85 | assertTrue(AopUtils.isCglibProxy(fooService)); 86 | 87 | assertEquals(AopProxyUtils.ultimateTargetClass(fooService), FooServiceImpl.class); 88 | 89 | assertEquals(AopTestUtils.getTargetObject(fooService).getClass(), FooServiceImpl.class); 90 | assertEquals(AopTestUtils.getUltimateTargetObject(fooService).getClass(), FooServiceImpl.class); 91 | ``` 92 | 93 | 这些是利用Spring提供的[AopUtils][javadoc-spring-AopUtils]、[AopTestUtils][javadoc-spring-AopTestUtils]和[AopProxyUtils][javadoc-spring-AopProxyUtils]来判断`FooServiceImpl` Bean是否被代理了(Spring AOP的实现是通过[动态代理][javadoc-spring-aop-proxying]来做的)。 94 | 95 | 但是证明`FooServiceImpl` Bean被代理并不意味着`FooAspect`生效了(假设此时有多个`@Aspect`),那么我们还需要验证`FooServiceImpl.incrementAndGet`的行为: 96 | 97 | ```java 98 | assertEquals(fooService.incrementAndGet(), 0); 99 | assertEquals(fooService.incrementAndGet(), 0); 100 | ``` 101 | 102 | ## 例子2:测试FooAspect的行为 103 | 104 | 但是总有一些时候我们是无法通过例子1的方法来测试Bean是否被正确的advised的: 105 | 106 | 1. advised方法没有返回值 107 | 1. Aspect不会修改advised方法的返回值(比如:做日志) 108 | 109 | 那么这个时候怎么测试呢?此时我们就需要用到Mockito的Spy方法结合Spring Testing工具来测试。 110 | 111 | [SpringAop_2_Test][src-ex2-SpringAop_2_Test]: 112 | 113 | ```java 114 | @ContextConfiguration(classes = { SpringAop_2_Test.class, AopConfig.class }) 115 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 116 | public class SpringAop_2_Test extends AbstractTestNGSpringContextTests { 117 | 118 | @SpyBean 119 | private FooAspect fooAspect; 120 | 121 | @Autowired 122 | private FooService fooService; 123 | 124 | @Test 125 | public void testFooService() { 126 | 127 | // ... 128 | verify(fooAspect, times(2)).changeIncrementAndGet(any()); 129 | 130 | } 131 | 132 | } 133 | ``` 134 | 135 | 这段代码和例子1有三点区别: 136 | 137 | 1. 启用了``MockitoTestExecutionListener``,这样能够开启Mockito的支持(回顾一下[Chapter 3: 使用Mockito][chapter_3_mockito.md]) 138 | 1. ``@SpyBean private FooAspect fooAspect``,这样能够声明一个被Mockito.spy过的Bean 139 | 1. ``verify(fooAspect, times(2)).changeIncrementAndGet(any())``,使用Mockito测试``FooAspect.changeIncrementAndGet``是否被调用了两次 140 | 141 | 上面的测试代码测试的是``FooAspect``的行为,而不是``FooServiceImpl``的行为,这种测试方法更为通用。 142 | 143 | ## 例子3:Spring Boot的例子 144 | 145 | 上面两个例子使用的是Spring Testing工具,下面举例Spring Boot Testing工具如何测AOP(其实大同小异): 146 | 147 | [SpringBootAopTest][src-ex3-SpringBootAopTest]: 148 | 149 | ```java 150 | @SpringBootTest(classes = { SpringBootAopTest.class, AopConfig.class }) 151 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 152 | public class SpringBootAopTest extends AbstractTestNGSpringContextTests { 153 | 154 | @SpyBean 155 | private FooAspect fooAspect; 156 | 157 | @Autowired 158 | private FooService fooService; 159 | 160 | @Test 161 | public void testFooService() { 162 | 163 | // ... 164 | verify(fooAspect, times(2)).changeIncrementAndGet(any()); 165 | 166 | } 167 | 168 | } 169 | ``` 170 | 171 | ## 参考文档 172 | 173 | * [Aspect Oriented Programming with Spring][doc-spring-aop] 174 | * [AopUtils][javadoc-spring-AopUtils] 175 | * [AopTestUtils][javadoc-spring-AopTestUtils] 176 | * [AopProxyUtils][javadoc-spring-AopProxyUtils] 177 | * [spring源码EnableAspectJAutoProxyTests][src-gh-EnableAspectJAutoProxyTests] 178 | * [spring源码AbstractAspectJAdvisorFactoryTests][src-gh-AbstractAspectJAdvisorFactoryTests] 179 | 180 | [chapter_3_mockito.md]: chapter_3_mockito.md 181 | [doc-spring-aop]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/aop.html 182 | [javadoc-spring-AopUtils]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/aop/support/AopUtils.html 183 | [javadoc-spring-AopTestUtils]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/test/util/AopTestUtils.html 184 | [javadoc-spring-AopProxyUtils]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/javadoc-api/org/springframework/aop/framework/AopProxyUtils.html 185 | [javadoc-spring-aop-proxying]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/aop.html#aop-proxying 186 | 187 | [src-FooServiceImpl]: aop/src/main/java/me/chanjar/aop/service/FooServiceImpl.java 188 | [src-FooAspect]: aop/src/main/java/me/chanjar/aop/aspect/FooAspect.java 189 | [src-ex1-SpringAop_1_Test]: aop/src/test/java/me/chanjar/aop/ex1/SpringAop_1_Test.java 190 | [src-ex2-SpringAop_2_Test]: aop/src/test/java/me/chanjar/aop/ex2/SpringAop_2_Test.java 191 | [src-ex3-SpringBootAopTest]: aop/src/test/java/me/chanjar/aop/ex3/SpringBootAopTest.java 192 | [src-gh-EnableAspectJAutoProxyTests]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-context/src/test/java/org/springframework/context/annotation/EnableAspectJAutoProxyTests.java 193 | [src-gh-AbstractAspectJAdvisorFactoryTests]: https://github.com/spring-projects/spring-framework/blob/v4.3.9.RELEASE/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java 194 | -------------------------------------------------------------------------------- /chapter_8_share_test_config.md: -------------------------------------------------------------------------------- 1 | # Chapter 8: 共享测试配置 2 | 3 | 在[使用Spring Boot Testing工具][chapter_1_s3_spring_boot_testing]中提到: 4 | 5 | > 在测试代码之间尽量做到配置共用。 6 | > ... 7 | > 能够有效利用Spring TestContext Framework的[缓存机制][doc-context-caching],ApplicationContext只会创建一次,后面的测试会直接用已创建的那个,加快测试代码运行速度。 8 | 9 | 本章将列举几种共享测试配置的方法 10 | 11 | ## @Configuration 12 | 13 | 我们可以将测试配置放在一个@Configuration里,然后在测试@SpringBootTest或ContextConfiguration中引用它。 14 | 15 | 16 | [PlainConfiguration][src-ex1-PlainConfiguration.java]: 17 | 18 | ```java 19 | @SpringBootApplication(scanBasePackages = "me.chanjar.shareconfig") 20 | public class PlainConfiguration { 21 | } 22 | ``` 23 | 24 | [FooRepositoryIT][src-ex1-FooRepositoryIT.java]: 25 | 26 | ```java 27 | @SpringBootTest(classes = PlainConfiguration.class) 28 | public class FooRepositoryIT extends ... 29 | ``` 30 | 31 | ## @Configuration on interface 32 | 33 | 也可以把@Configuration放到一个interface上。 34 | 35 | [PlainConfiguration][src-ex2-InterfaceConfiguration.java]: 36 | 37 | ```java 38 | @SpringBootApplication(scanBasePackages = "me.chanjar.shareconfig") 39 | public interface InterfaceConfiguration { 40 | } 41 | ``` 42 | 43 | [FooRepositoryIT][src-ex2-FooRepositoryIT.java]: 44 | 45 | ```java 46 | @SpringBootTest(classes = InterfaceConfiguration.class) 47 | public class FooRepositoryIT extends ... 48 | ``` 49 | ## Annotation 50 | 51 | 也可以利用Spring的[Meta-annotations][doc-spring-meta-annotations]及[自定义机制][doc-spring-annotation-programming-model],提供自己的Annotation用在测试配置上。 52 | 53 | [PlainConfiguration][src-ex3-AnnotationConfiguration.java]: 54 | 55 | ```java 56 | @Target(ElementType.TYPE) 57 | @Retention(RetentionPolicy.RUNTIME) 58 | @SpringBootApplication(scanBasePackages = "me.chanjar.shareconfig") 59 | public @interface AnnotationConfiguration { 60 | } 61 | ``` 62 | 63 | [FooRepositoryIT][src-ex3-FooRepositoryIT.java]: 64 | 65 | ```java 66 | @SpringBootTest(classes = FooRepositoryIT.class) 67 | @AnnotationConfiguration 68 | public class FooRepositoryIT extends ... 69 | ``` 70 | ## 参考文档 71 | 72 | * [Meta-annotations][doc-spring-meta-annotations] 73 | * [Meta-Annotation Support for Testing][doc-spring-meta-annotations-for-testing] 74 | * [Spring Annotation Programming Model][doc-spring-annotation-programming-model] 75 | 76 | [chapter_1_s3_spring_boot_testing]: chapter_1_s3_spring_boot_testing.md 77 | [doc-context-caching]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/integration-testing.html#testcontext-ctx-management-caching 78 | [doc-spring-meta-annotations-for-testing]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/integration-testing.html#integration-testing-annotations-meta 79 | [doc-spring-meta-annotations]: https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/beans.html#beans-meta-annotations 80 | [doc-spring-annotation-programming-model]: https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model 81 | [src-ex1-PlainConfiguration.java]: share-config/src/test/java/me/chanjar/shareconfig/testconfig/PlainConfiguration.java 82 | [src-ex1-FooRepositoryIT.java]: share-config/src/test/java/me/chanjar/shareconfig/configuration/FooRepositoryIT.java 83 | [src-ex2-InterfaceConfiguration.java]: share-config/src/test/java/me/chanjar/shareconfig/testconfig/InterfaceConfiguration.java 84 | [src-ex2-FooRepositoryIT.java]: share-config/src/test/java/me/chanjar/shareconfig/inter/FooRepositoryIT.java 85 | [src-ex3-AnnotationConfiguration.java]: share-config/src/test/java/me/chanjar/shareconfig/annotation/FooRepositoryIT.java 86 | [src-ex3-FooRepositoryIT.java]: share-config/src/test/java/me/chanjar/shareconfig/testconfig/AnnotationConfiguration.java 87 | -------------------------------------------------------------------------------- /configuration/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | me.chanjar 7 | spring-test-examples-aggregator 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | me.chanjar 12 | spring-test-examples-configuration 13 | jar 14 | 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot 21 | 22 | 23 | 24 | org.slf4j 25 | slf4j-api 26 | 27 | 28 | 29 | ch.qos.logback 30 | logback-classic 31 | 32 | 33 | 34 | 35 | org.testng 36 | testng 37 | test 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-jar-plugin 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /configuration/src/main/java/me/chanjar/configuration/service/Bar.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.configuration.service; 2 | 3 | public class Bar { 4 | 5 | private final String name; 6 | 7 | public Bar(String name) { 8 | this.name = name; 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /configuration/src/main/java/me/chanjar/configuration/service/Foo.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.configuration.service; 2 | 3 | public class Foo { 4 | } 5 | -------------------------------------------------------------------------------- /configuration/src/test/java/me/chanjar/configuration/ex1/FooConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.configuration.ex1; 2 | 3 | import me.chanjar.configuration.service.Foo; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | public class FooConfiguration { 9 | 10 | @Bean 11 | public Foo foo() { 12 | return new Foo(); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /configuration/src/test/java/me/chanjar/configuration/ex1/FooConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.configuration.ex1; 2 | 3 | import me.chanjar.configuration.service.Foo; 4 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 5 | import org.testng.annotations.AfterMethod; 6 | import org.testng.annotations.BeforeMethod; 7 | import org.testng.annotations.Test; 8 | 9 | import static org.testng.Assert.assertNotNull; 10 | 11 | public class FooConfigurationTest { 12 | 13 | private AnnotationConfigApplicationContext context; 14 | 15 | @BeforeMethod 16 | public void init() { 17 | context = new AnnotationConfigApplicationContext(); 18 | } 19 | 20 | @AfterMethod(alwaysRun = true) 21 | public void reset() { 22 | context.close(); 23 | } 24 | 25 | @Test 26 | public void testFooCreation() { 27 | context.register(FooConfiguration.class); 28 | context.refresh(); 29 | assertNotNull(context.getBean(Foo.class)); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /configuration/src/test/java/me/chanjar/configuration/ex2/FooConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.configuration.ex2; 2 | 3 | import me.chanjar.configuration.service.Foo; 4 | import org.springframework.context.annotation.*; 5 | import org.springframework.core.type.AnnotatedTypeMetadata; 6 | 7 | @Configuration 8 | public class FooConfiguration { 9 | 10 | @Bean 11 | @Conditional(FooCondition.class) 12 | public Foo foo() { 13 | return new Foo(); 14 | } 15 | 16 | public static class FooCondition implements Condition { 17 | 18 | @Override 19 | public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 20 | if (context.getEnvironment() != null) { 21 | Boolean property = context.getEnvironment().getProperty("foo.create", Boolean.class); 22 | return Boolean.TRUE.equals(property); 23 | } 24 | return false; 25 | } 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /configuration/src/test/java/me/chanjar/configuration/ex2/FooConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.configuration.ex2; 2 | 3 | import me.chanjar.configuration.service.Foo; 4 | import org.springframework.beans.factory.NoSuchBeanDefinitionException; 5 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 6 | import org.springframework.core.env.MapPropertySource; 7 | import org.testng.annotations.AfterMethod; 8 | import org.testng.annotations.BeforeMethod; 9 | import org.testng.annotations.Test; 10 | 11 | import java.util.Collections; 12 | 13 | import static org.testng.Assert.assertNotNull; 14 | 15 | public class FooConfigurationTest { 16 | 17 | private AnnotationConfigApplicationContext context; 18 | 19 | @BeforeMethod 20 | public void init() { 21 | context = new AnnotationConfigApplicationContext(); 22 | } 23 | 24 | @AfterMethod(alwaysRun = true) 25 | public void reset() { 26 | context.close(); 27 | } 28 | 29 | @Test(expectedExceptions = NoSuchBeanDefinitionException.class) 30 | public void testFooCreatePropertyNull() { 31 | context.register(FooConfiguration.class); 32 | context.refresh(); 33 | context.getBean(Foo.class); 34 | } 35 | 36 | @Test 37 | public void testFooCreatePropertyTrue() { 38 | context.getEnvironment().getPropertySources().addLast( 39 | new MapPropertySource("test", Collections.singletonMap("foo.create", "true")) 40 | ); 41 | context.register(FooConfiguration.class); 42 | context.refresh(); 43 | assertNotNull(context.getBean(Foo.class)); 44 | } 45 | 46 | @Test(expectedExceptions = NoSuchBeanDefinitionException.class) 47 | public void testFooCreatePropertyFalse() { 48 | context.getEnvironment().getPropertySources().addLast( 49 | new MapPropertySource("test", Collections.singletonMap("foo.create", "false")) 50 | ); 51 | context.register(FooConfiguration.class); 52 | context.refresh(); 53 | assertNotNull(context.getBean(Foo.class)); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /configuration/src/test/java/me/chanjar/configuration/ex3/FooConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.configuration.ex3; 2 | 3 | import me.chanjar.configuration.service.Foo; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class FooConfiguration { 10 | 11 | @Bean 12 | @ConditionalOnProperty(prefix = "foo", name = "create", havingValue = "true") 13 | public Foo foo() { 14 | return new Foo(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /configuration/src/test/java/me/chanjar/configuration/ex3/FooConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.configuration.ex3; 2 | 3 | import me.chanjar.configuration.service.Foo; 4 | import org.springframework.beans.factory.NoSuchBeanDefinitionException; 5 | import org.springframework.boot.test.util.EnvironmentTestUtils; 6 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 7 | import org.testng.annotations.AfterMethod; 8 | import org.testng.annotations.BeforeMethod; 9 | import org.testng.annotations.Test; 10 | 11 | import static org.testng.Assert.assertNotNull; 12 | 13 | public class FooConfigurationTest { 14 | 15 | private AnnotationConfigApplicationContext context; 16 | 17 | @BeforeMethod 18 | public void init() { 19 | context = new AnnotationConfigApplicationContext(); 20 | } 21 | 22 | @AfterMethod(alwaysRun = true) 23 | public void reset() { 24 | context.close(); 25 | } 26 | 27 | @Test(expectedExceptions = NoSuchBeanDefinitionException.class) 28 | public void testFooCreatePropertyNull() { 29 | context.register(FooConfiguration.class); 30 | context.refresh(); 31 | context.getBean(Foo.class); 32 | } 33 | 34 | @Test 35 | public void testFooCreatePropertyTrue() { 36 | EnvironmentTestUtils.addEnvironment(context, "foo.create=true"); 37 | context.register(FooConfiguration.class); 38 | context.refresh(); 39 | assertNotNull(context.getBean(Foo.class)); 40 | } 41 | 42 | @Test(expectedExceptions = NoSuchBeanDefinitionException.class) 43 | public void testFooCreatePropertyFalse() { 44 | EnvironmentTestUtils.addEnvironment(context, "foo.create=false"); 45 | context.register(FooConfiguration.class); 46 | context.refresh(); 47 | assertNotNull(context.getBean(Foo.class)); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /configuration/src/test/java/me/chanjar/configuration/ex4/BarConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.configuration.ex4; 2 | 3 | import me.chanjar.configuration.service.Bar; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | @EnableConfigurationProperties(BarConfiguration.BarProperties.class) 12 | public class BarConfiguration { 13 | 14 | @Autowired 15 | private BarProperties barProperties; 16 | 17 | @Bean 18 | public Bar bar() { 19 | return new Bar(barProperties.getName()); 20 | } 21 | 22 | @ConfigurationProperties("bar") 23 | public static class BarProperties { 24 | 25 | private String name; 26 | 27 | public String getName() { 28 | return name; 29 | } 30 | 31 | public void setName(String name) { 32 | this.name = name; 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /configuration/src/test/java/me/chanjar/configuration/ex4/BarConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.configuration.ex4; 2 | 3 | import me.chanjar.configuration.service.Bar; 4 | import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; 5 | import org.springframework.boot.test.util.EnvironmentTestUtils; 6 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 7 | import org.testng.annotations.AfterMethod; 8 | import org.testng.annotations.BeforeMethod; 9 | import org.testng.annotations.Test; 10 | 11 | import static org.testng.Assert.assertEquals; 12 | 13 | public class BarConfigurationTest { 14 | 15 | private AnnotationConfigApplicationContext context; 16 | 17 | @BeforeMethod 18 | public void init() { 19 | context = new AnnotationConfigApplicationContext(); 20 | } 21 | 22 | @AfterMethod(alwaysRun = true) 23 | public void reset() { 24 | context.close(); 25 | } 26 | 27 | @Test 28 | public void testBarCreation() { 29 | EnvironmentTestUtils.addEnvironment(context, "bar.name=test"); 30 | context.register(BarConfiguration.class, PropertyPlaceholderAutoConfiguration.class); 31 | context.refresh(); 32 | assertEquals(context.getBean(Bar.class).getName(), "test"); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /mock/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | me.chanjar 7 | spring-test-examples-aggregator 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | me.chanjar 12 | spring-test-examples-mock 13 | jar 14 | 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot 21 | 22 | 23 | 24 | org.slf4j 25 | slf4j-api 26 | 27 | 28 | 29 | ch.qos.logback 30 | logback-classic 31 | 32 | 33 | 34 | 35 | org.testng 36 | testng 37 | test 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-jar-plugin 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /mock/src/main/java/me/chanjar/common/Bar.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.common; 2 | 3 | import java.util.Set; 4 | 5 | public interface Bar { 6 | 7 | Set getAllCodes(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /mock/src/main/java/me/chanjar/common/Foo.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.common; 2 | 3 | public interface Foo { 4 | 5 | boolean checkCodeDuplicate(String code); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /mock/src/main/java/me/chanjar/common/FooImpl.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.common; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Component; 5 | 6 | @Component 7 | public class FooImpl implements Foo { 8 | 9 | private Bar bar; 10 | 11 | @Override 12 | public boolean checkCodeDuplicate(String code) { 13 | return bar.getAllCodes().contains(code); 14 | } 15 | 16 | @Autowired 17 | public void setBar(Bar bar) { 18 | this.bar = bar; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /mock/src/test/java/me/chanjar/mockito/MockitoTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.mockito; 2 | 3 | import me.chanjar.common.Bar; 4 | import me.chanjar.common.FooImpl; 5 | import org.mockito.InjectMocks; 6 | import org.mockito.Mock; 7 | import org.mockito.MockitoAnnotations; 8 | import org.testng.annotations.BeforeMethod; 9 | import org.testng.annotations.Test; 10 | 11 | import java.util.Collections; 12 | 13 | import static org.mockito.Mockito.when; 14 | import static org.testng.Assert.assertEquals; 15 | 16 | public class MockitoTest { 17 | 18 | @Mock 19 | private Bar bar; 20 | 21 | @InjectMocks 22 | private FooImpl foo; 23 | 24 | @BeforeMethod(alwaysRun = true) 25 | public void initMock() { 26 | MockitoAnnotations.initMocks(this); 27 | } 28 | 29 | @Test 30 | public void testCheckCodeDuplicate() throws Exception { 31 | 32 | when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); 33 | assertEquals(foo.checkCodeDuplicate("123"), true); 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /mock/src/test/java/me/chanjar/no_mock/NoMockTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.no_mock; 2 | 3 | import me.chanjar.common.Bar; 4 | import me.chanjar.common.FooImpl; 5 | import org.testng.annotations.Test; 6 | 7 | import java.util.Collections; 8 | import java.util.Set; 9 | 10 | import static org.testng.Assert.assertEquals; 11 | 12 | public class NoMockTest { 13 | 14 | @Test 15 | public void testCheckCodeDuplicate1() throws Exception { 16 | 17 | FooImpl foo = new FooImpl(); 18 | foo.setBar(new Bar() { 19 | @Override 20 | public Set getAllCodes() { 21 | return Collections.singleton("123"); 22 | } 23 | }); 24 | assertEquals(foo.checkCodeDuplicate("123"), true); 25 | 26 | } 27 | 28 | @Test 29 | public void testCheckCodeDuplicate2() throws Exception { 30 | 31 | FooImpl foo = new FooImpl(); 32 | foo.setBar(new FakeBar(Collections.singleton("123"))); 33 | assertEquals(foo.checkCodeDuplicate("123"), true); 34 | 35 | } 36 | 37 | public class FakeBar implements Bar { 38 | 39 | private final Set codes; 40 | 41 | public FakeBar(Set codes) { 42 | this.codes = codes; 43 | } 44 | 45 | @Override 46 | public Set getAllCodes() { 47 | return codes; 48 | } 49 | 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /mock/src/test/java/me/chanjar/spring1/Spring_1_Test.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.spring1; 2 | 3 | import me.chanjar.common.Bar; 4 | import me.chanjar.common.Foo; 5 | import me.chanjar.common.FooImpl; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.mock.mockito.MockBean; 8 | import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener; 9 | import org.springframework.test.context.ContextConfiguration; 10 | import org.springframework.test.context.TestExecutionListeners; 11 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 12 | import org.testng.annotations.Test; 13 | 14 | import java.util.Collections; 15 | 16 | import static org.mockito.Mockito.when; 17 | import static org.testng.Assert.assertEquals; 18 | 19 | @ContextConfiguration(classes = FooImpl.class) 20 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 21 | public class Spring_1_Test extends AbstractTestNGSpringContextTests { 22 | 23 | @MockBean 24 | private Bar bar; 25 | 26 | @Autowired 27 | private Foo foo; 28 | 29 | @Test 30 | public void testCheckCodeDuplicate() throws Exception { 31 | 32 | when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); 33 | assertEquals(foo.checkCodeDuplicate("123"), true); 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /mock/src/test/java/me/chanjar/spring2/Loo.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.spring2; 2 | 3 | public interface Loo { 4 | boolean checkCodeDuplicate(String code); 5 | } 6 | -------------------------------------------------------------------------------- /mock/src/test/java/me/chanjar/spring2/LooImpl.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.spring2; 2 | 3 | import me.chanjar.common.Foo; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class LooImpl implements Loo { 9 | 10 | private Foo foo; 11 | 12 | @Override 13 | public boolean checkCodeDuplicate(String code) { 14 | return foo.checkCodeDuplicate(code); 15 | } 16 | 17 | @Autowired 18 | public void setFoo(Foo foo) { 19 | this.foo = foo; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /mock/src/test/java/me/chanjar/spring2/Spring_2_Test.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.spring2; 2 | 3 | import me.chanjar.common.Bar; 4 | import me.chanjar.common.FooImpl; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.mock.mockito.MockBean; 7 | import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener; 8 | import org.springframework.test.context.ContextConfiguration; 9 | import org.springframework.test.context.TestExecutionListeners; 10 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 11 | import org.testng.annotations.Test; 12 | 13 | import java.util.Collections; 14 | 15 | import static org.mockito.Mockito.when; 16 | import static org.testng.Assert.assertEquals; 17 | 18 | @ContextConfiguration(classes = { FooImpl.class, LooImpl.class }) 19 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 20 | public class Spring_2_Test extends AbstractTestNGSpringContextTests { 21 | 22 | @MockBean 23 | private Bar bar; 24 | 25 | @Autowired 26 | private Loo loo; 27 | 28 | @Test 29 | public void testCheckCodeDuplicate() throws Exception { 30 | 31 | when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); 32 | assertEquals(loo.checkCodeDuplicate("123"), true); 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /mock/src/test/java/me/chanjar/springboot1/Boot_1_Test.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.springboot1; 2 | 3 | import me.chanjar.common.Bar; 4 | import me.chanjar.common.Foo; 5 | import me.chanjar.common.FooImpl; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.boot.test.mock.mockito.MockBean; 9 | import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener; 10 | import org.springframework.test.context.TestExecutionListeners; 11 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 12 | import org.testng.annotations.Test; 13 | 14 | import java.util.Collections; 15 | 16 | import static org.mockito.Mockito.when; 17 | import static org.testng.Assert.assertEquals; 18 | 19 | @SpringBootTest(classes = { FooImpl.class }) 20 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 21 | public class Boot_1_Test extends AbstractTestNGSpringContextTests { 22 | 23 | @MockBean 24 | private Bar bar; 25 | 26 | @Autowired 27 | private Foo foo; 28 | 29 | @Test 30 | public void testCheckCodeDuplicate() throws Exception { 31 | 32 | when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); 33 | assertEquals(foo.checkCodeDuplicate("123"), true); 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /mvc/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | me.chanjar 7 | spring-test-examples-aggregator 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | me.chanjar 12 | spring-test-examples-mvc 13 | jar 14 | 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-web 26 | 27 | 28 | 29 | org.slf4j 30 | slf4j-api 31 | 32 | 33 | 34 | ch.qos.logback 35 | logback-classic 36 | 37 | 38 | 39 | 40 | org.testng 41 | testng 42 | test 43 | 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-jar-plugin 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /mvc/src/main/java/me/chanjar/web/Foo.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.web; 2 | 3 | public interface Foo { 4 | 5 | boolean checkCodeDuplicate(String code); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /mvc/src/main/java/me/chanjar/web/FooController.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.web; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestMethod; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | 11 | @Controller 12 | public class FooController { 13 | 14 | @Autowired 15 | private Foo foo; 16 | 17 | @RequestMapping(path = "/foo/check-code-dup", method = RequestMethod.GET) 18 | public ResponseEntity checkCodeDuplicate(@RequestParam String code) { 19 | 20 | return new ResponseEntity<>( 21 | Boolean.valueOf(foo.checkCodeDuplicate(code)), 22 | HttpStatus.OK 23 | ); 24 | 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /mvc/src/main/java/me/chanjar/web/FooImpl.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.web; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public class FooImpl implements Foo { 7 | 8 | @Override 9 | public boolean checkCodeDuplicate(String code) { 10 | return true; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /mvc/src/test/java/me/chanjar/spring1/SpringMvc_1_Test.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.spring1; 2 | 3 | import me.chanjar.web.FooController; 4 | import me.chanjar.web.FooImpl; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.test.context.ContextConfiguration; 7 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 8 | import org.springframework.test.context.web.WebAppConfiguration; 9 | import org.springframework.test.web.servlet.MockMvc; 10 | import org.springframework.web.context.WebApplicationContext; 11 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 12 | import org.testng.annotations.BeforeMethod; 13 | import org.testng.annotations.Test; 14 | 15 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 16 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 17 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 18 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 19 | import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; 20 | 21 | @EnableWebMvc 22 | @WebAppConfiguration 23 | @ContextConfiguration(classes = { FooController.class, FooImpl.class }) 24 | public class SpringMvc_1_Test extends AbstractTestNGSpringContextTests { 25 | 26 | @Autowired 27 | private WebApplicationContext wac; 28 | 29 | private MockMvc mvc; 30 | 31 | @BeforeMethod 32 | public void prepareMockMvc() { 33 | this.mvc = webAppContextSetup(wac).build(); 34 | } 35 | 36 | @Test 37 | public void testController() throws Exception { 38 | 39 | this.mvc.perform(get("/foo/check-code-dup").param("code", "123")) 40 | .andDo(print()) 41 | .andExpect(status().isOk()) 42 | .andExpect(content().string("true")); 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /mvc/src/test/java/me/chanjar/spring2/SpringMvc_2_Test.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.spring2; 2 | 3 | import me.chanjar.web.Foo; 4 | import me.chanjar.web.FooController; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.mock.mockito.MockBean; 7 | import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener; 8 | import org.springframework.test.context.ContextConfiguration; 9 | import org.springframework.test.context.TestExecutionListeners; 10 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 11 | import org.springframework.test.context.web.WebAppConfiguration; 12 | import org.springframework.test.web.servlet.MockMvc; 13 | import org.springframework.web.context.WebApplicationContext; 14 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 15 | import org.testng.annotations.BeforeMethod; 16 | import org.testng.annotations.Test; 17 | 18 | import static org.mockito.Matchers.anyString; 19 | import static org.mockito.Mockito.when; 20 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 21 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 22 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 23 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 24 | import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; 25 | 26 | @EnableWebMvc 27 | @WebAppConfiguration 28 | @ContextConfiguration(classes = { FooController.class }) 29 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 30 | public class SpringMvc_2_Test extends AbstractTestNGSpringContextTests { 31 | 32 | @Autowired 33 | private WebApplicationContext wac; 34 | 35 | @MockBean 36 | private Foo foo; 37 | 38 | private MockMvc mvc; 39 | 40 | @BeforeMethod 41 | public void prepareMockMvc() { 42 | this.mvc = webAppContextSetup(wac).build(); 43 | } 44 | 45 | @Test 46 | public void testController() throws Exception { 47 | 48 | when(foo.checkCodeDuplicate(anyString())).thenReturn(true); 49 | 50 | this.mvc.perform(get("/foo/check-code-dup").param("code", "123")) 51 | .andDo(print()) 52 | .andExpect(status().isOk()) 53 | .andExpect(content().string("true")); 54 | 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /mvc/src/test/java/me/chanjar/springboot1/BootMvc_1_Test.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.springboot1; 2 | 3 | import me.chanjar.web.FooController; 4 | import me.chanjar.web.FooImpl; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 7 | import org.springframework.test.context.ContextConfiguration; 8 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 9 | import org.springframework.test.web.servlet.MockMvc; 10 | import org.testng.annotations.Test; 11 | 12 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 13 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 14 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 15 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 16 | 17 | @WebMvcTest 18 | @ContextConfiguration(classes = { FooController.class, FooImpl.class }) 19 | public class BootMvc_1_Test extends AbstractTestNGSpringContextTests { 20 | 21 | @Autowired 22 | private MockMvc mvc; 23 | 24 | @Test 25 | public void testController() throws Exception { 26 | 27 | this.mvc.perform(get("/foo/check-code-dup").param("code", "123")) 28 | .andDo(print()) 29 | .andExpect(status().isOk()) 30 | .andExpect(content().string("true")); 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /mvc/src/test/java/me/chanjar/springboot2/BootMvc_2_Test.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.springboot2; 2 | 3 | import me.chanjar.web.Foo; 4 | import me.chanjar.web.FooController; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 7 | import org.springframework.boot.test.mock.mockito.MockBean; 8 | import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener; 9 | import org.springframework.test.context.ContextConfiguration; 10 | import org.springframework.test.context.TestExecutionListeners; 11 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 12 | import org.springframework.test.web.servlet.MockMvc; 13 | import org.testng.annotations.Test; 14 | 15 | import static org.mockito.Matchers.anyString; 16 | import static org.mockito.Mockito.when; 17 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 18 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; 19 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 20 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 21 | 22 | @WebMvcTest 23 | @ContextConfiguration(classes = { FooController.class }) 24 | @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) 25 | public class BootMvc_2_Test extends AbstractTestNGSpringContextTests { 26 | 27 | @Autowired 28 | private MockMvc mvc; 29 | 30 | @MockBean 31 | private Foo foo; 32 | 33 | @Test 34 | public void testController() throws Exception { 35 | 36 | when(foo.checkCodeDuplicate(anyString())).thenReturn(true); 37 | 38 | this.mvc.perform(get("/foo/check-code-dup").param("code", "123")) 39 | .andDo(print()) 40 | .andExpect(status().isOk()) 41 | .andExpect(content().string("true")); 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | me.chanjar 7 | spring-test-examples-aggregator 8 | 1.0.0-SNAPSHOT 9 | pom 10 | 11 | 12 | basic 13 | annotation 14 | mock 15 | rdbs 16 | rdbs-docker 17 | mvc 18 | configuration 19 | share-config 20 | aop 21 | 22 | 23 | 24 | 25 | 1.8 26 | UTF-8 27 | true 28 | true 29 | 30 | 1.5.4.RELEASE 31 | 1.9.5 32 | 6.8.7 33 | 3.5 34 | 4.1 35 | 36 | 2.6 37 | 2.22.1 38 | 3.3 39 | 0.7.9 40 | 3.2.1 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-dependencies 50 | ${spring-boot.version} 51 | pom 52 | import 53 | 54 | 55 | 56 | org.testng 57 | testng 58 | ${testng.version} 59 | 60 | 61 | 62 | org.apache.commons 63 | commons-lang3 64 | ${commons-lang3.version} 65 | 66 | 67 | 68 | org.apache.commons 69 | commons-collections4 70 | ${commons-collections4.version} 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | false 81 | 82 | central 83 | http://repo.maven.apache.org/maven2 84 | 85 | 86 | 87 | false 88 | 89 | spring-releases 90 | https://repo.spring.io/libs-release 91 | 92 | 93 | 94 | 95 | 96 | spring-releases 97 | https://repo.spring.io/libs-release 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.apache.maven.plugins 105 | maven-compiler-plugin 106 | ${maven.compiler.plugin.version} 107 | 108 | ${java.version} 109 | ${java.version} 110 | ${project.build.sourceEncoding} 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 121 | 122 | org.apache.maven.plugins 123 | maven-jar-plugin 124 | ${maven.jar.plugin.version} 125 | 126 | 127 | 128 | test-jar 129 | 130 | 131 | true 132 | 133 | 134 | 135 | 136 | 137 | 140 | 141 | org.apache.maven.plugins 142 | maven-failsafe-plugin 143 | ${maven.failsafe.plugin.version} 144 | 145 | 146 | 147 | org.flywaydb 148 | flyway-maven-plugin 149 | ${flyway.maven.plugin.version} 150 | 151 | ${skipITs} 152 | 153 | 154 | 155 | clean-db 156 | post-integration-test 157 | 158 | 159 | clean 160 | 161 | 162 | 163 | 164 | 165 | 166 | org.jacoco 167 | jacoco-maven-plugin 168 | ${jacoco.maven.plugin.version} 169 | 170 | 171 | 172 | default-prepare-agent 173 | 174 | prepare-agent 175 | 176 | 177 | 178 | default-prepare-agent-integration 179 | 180 | prepare-agent-integration 181 | 182 | 183 | 184 | default-report 185 | test 186 | 187 | report 188 | 189 | 190 | 191 | default-report-integration 192 | integration-test 193 | 194 | report-integration 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /rdbs-docker/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | me.chanjar 7 | spring-test-examples-aggregator 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | me.chanjar 12 | spring-test-examples-rdbs-docker 13 | jar 14 | 15 | 16 | 0.28.0 17 | 2.22.0 18 | 2.19 19 | 20 | 21 | false 22 | 23 | ${skipTests} 24 | 25 | ${skipTests} 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-jdbc 39 | 40 | 41 | 42 | org.flywaydb 43 | flyway-core 44 | 45 | 46 | 47 | org.slf4j 48 | slf4j-api 49 | 50 | 51 | 52 | ch.qos.logback 53 | logback-classic 54 | 55 | 56 | 57 | mysql 58 | mysql-connector-java 59 | 8.0.12 60 | 61 | 62 | 63 | org.testng 64 | testng 65 | test 66 | 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-starter-test 71 | test 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | org.springframework.boot 80 | spring-boot-maven-plugin 81 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | org.apache.maven.plugins 90 | maven-jar-plugin 91 | 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-surefire-plugin 96 | ${maven-surefire-plugin.version} 97 | 98 | ${skipUTs} 99 | 100 | 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-failsafe-plugin 105 | ${maven-failsafe-plugin.version} 106 | 107 | 108 | integration-test 109 | 110 | integration-test 111 | 112 | 113 | 114 | verify 115 | 116 | verify 117 | 118 | 119 | 120 | 121 | 122 | ${skipTests} 123 | 124 | ${skipITs} 125 | 128 | 129 | jdbc:mysql://localhost:${docker-mysql.port}/test 130 | foo 131 | bar 132 | 133 | 134 | 135 | 136 | 137 | io.fabric8 138 | docker-maven-plugin 139 | ${fabric8.dmp.version} 140 | 141 | 142 | ${skipITs} 143 | 144 | 145 | 146 | mysql:8 147 | 148 | 149 | 150 | 152 | docker-mysql.port:3306 153 | 154 | 155 | 156 | 123456 157 | test 158 | foo 159 | bar 160 | 161 | 162 | 163 | 164 | MySQL init process done. Ready for start up. 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | start 176 | pre-integration-test 177 | 178 | start 179 | 180 | 181 | 182 | 183 | stop 184 | post-integration-test 185 | 186 | stop 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /rdbs-docker/src/main/java/me/chanjar/domain/Foo.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.domain; 2 | 3 | public class Foo { 4 | 5 | private String name; 6 | 7 | public String getName() { 8 | return name; 9 | } 10 | 11 | public void setName(String name) { 12 | this.name = name; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /rdbs-docker/src/main/java/me/chanjar/domain/FooRepository.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.domain; 2 | 3 | public interface FooRepository { 4 | 5 | void save(Foo foo); 6 | 7 | void delete(String name); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /rdbs-docker/src/main/java/me/chanjar/domain/FooRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.domain; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.jdbc.core.JdbcTemplate; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public class FooRepositoryImpl implements FooRepository { 9 | 10 | private JdbcTemplate jdbcTemplate; 11 | 12 | @Override 13 | public void save(Foo foo) { 14 | jdbcTemplate.update("INSERT INTO FOO(name) VALUES (?)", foo.getName()); 15 | } 16 | 17 | @Override 18 | public void delete(String name) { 19 | jdbcTemplate.update("DELETE FROM FOO WHERE NAME = ?", name); 20 | } 21 | 22 | @Autowired 23 | public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 24 | this.jdbcTemplate = jdbcTemplate; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /rdbs-docker/src/main/resources/db/migration/V1.0.0__foo-ddl.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE FOO ( 2 | name VARCHAR(100) 3 | ); 4 | -------------------------------------------------------------------------------- /rdbs-docker/src/test/java/me/chanjar/domain/FooRepositoryImplIT.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.domain; 2 | 3 | import org.flywaydb.core.Flyway; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.context.EnvironmentAware; 8 | import org.springframework.core.env.Environment; 9 | import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests; 10 | import org.testng.annotations.AfterTest; 11 | import org.testng.annotations.Test; 12 | 13 | import static org.testng.Assert.*; 14 | 15 | @SpringBootTest(properties = "spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver") 16 | @SpringBootApplication 17 | public class FooRepositoryImplIT extends AbstractTransactionalTestNGSpringContextTests implements EnvironmentAware { 18 | 19 | @Autowired 20 | private FooRepository fooRepository; 21 | 22 | @Autowired 23 | private Flyway flyway; 24 | 25 | @Test 26 | public void testSave() { 27 | 28 | Foo foo = new Foo(); 29 | foo.setName("Bob"); 30 | fooRepository.save(foo); 31 | 32 | assertEquals(countRowsInTable("FOO"), 1); 33 | countRowsInTableWhere("FOO", "name = 'Bob'"); 34 | } 35 | 36 | @Test(dependsOnMethods = "testSave") 37 | public void testDelete() { 38 | 39 | assertEquals(countRowsInTable("FOO"), 0); 40 | 41 | Foo foo = new Foo(); 42 | foo.setName("Bob"); 43 | fooRepository.save(foo); 44 | 45 | fooRepository.delete(foo.getName()); 46 | assertEquals(countRowsInTable("FOO"), 0); 47 | 48 | } 49 | 50 | @AfterTest 51 | public void cleanDb() { 52 | flyway.clean(); 53 | } 54 | 55 | @Override 56 | public void setEnvironment(Environment environment) { 57 | System.out.println("==================================="); 58 | System.out.println(environment.getProperty("SPRING_DATASOURCE_URL")); 59 | System.out.println("==================================="); 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /rdbs-docker/src/test/java/me/chanjar/domain/FooTest.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.domain; 2 | 3 | import org.testng.annotations.Test; 4 | 5 | public class FooTest { 6 | 7 | @Test 8 | public void testDummy() { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /rdbs/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | me.chanjar 7 | spring-test-examples-aggregator 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | me.chanjar 12 | spring-test-examples-rdbs 13 | jar 14 | 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot 21 | 22 | 23 | 24 | com.h2database 25 | h2 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-jdbc 31 | 32 | 33 | 34 | org.flywaydb 35 | flyway-core 36 | 37 | 38 | 39 | org.slf4j 40 | slf4j-api 41 | 42 | 43 | 44 | ch.qos.logback 45 | logback-classic 46 | 47 | 48 | 49 | 50 | org.testng 51 | testng 52 | test 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-test 58 | test 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-jar-plugin 68 | 69 | 70 | org.apache.maven.plugins 71 | maven-failsafe-plugin 72 | 73 | 74 | integration-test 75 | 76 | integration-test 77 | 78 | 79 | 80 | verify 81 | 82 | verify 83 | 84 | 85 | 86 | 87 | 88 | 98 | ${spring.config.location} 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /rdbs/src/main/java/me/chanjar/domain/Foo.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.domain; 2 | 3 | public class Foo { 4 | 5 | private String name; 6 | 7 | public String getName() { 8 | return name; 9 | } 10 | 11 | public void setName(String name) { 12 | this.name = name; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /rdbs/src/main/java/me/chanjar/domain/FooRepository.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.domain; 2 | 3 | public interface FooRepository { 4 | 5 | void save(Foo foo); 6 | 7 | void delete(String name); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /rdbs/src/main/java/me/chanjar/domain/FooRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.domain; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.jdbc.core.JdbcTemplate; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public class FooRepositoryImpl implements FooRepository { 9 | 10 | private JdbcTemplate jdbcTemplate; 11 | 12 | @Override 13 | public void save(Foo foo) { 14 | jdbcTemplate.update("INSERT INTO FOO(name) VALUES (?)", foo.getName()); 15 | } 16 | 17 | @Override 18 | public void delete(String name) { 19 | jdbcTemplate.update("DELETE FROM FOO WHERE NAME = ?", name); 20 | } 21 | 22 | @Autowired 23 | public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 24 | this.jdbcTemplate = jdbcTemplate; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /rdbs/src/main/resources/db/migration/V1.0.0__foo-ddl.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE FOO ( 2 | name VARCHAR2(100) 3 | ); 4 | -------------------------------------------------------------------------------- /rdbs/src/main/resources/me/chanjar/domain/foo-ddl.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE FOO ( 2 | name VARCHAR2(100) 3 | ); 4 | -------------------------------------------------------------------------------- /rdbs/src/test/java/me/chanjar/spring1/Spring_1_IT.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.spring1; 2 | 3 | import me.chanjar.domain.Foo; 4 | import me.chanjar.domain.FooRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.jdbc.core.JdbcTemplate; 7 | import org.springframework.test.context.ContextConfiguration; 8 | import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; 9 | import org.testng.annotations.Test; 10 | 11 | import static org.testng.Assert.assertEquals; 12 | 13 | @ContextConfiguration(classes = Spring_1_IT_Configuration.class) 14 | public class Spring_1_IT extends AbstractTestNGSpringContextTests { 15 | 16 | @Autowired 17 | private FooRepository fooRepository; 18 | 19 | @Autowired 20 | private JdbcTemplate jdbcTemplate; 21 | 22 | @Test 23 | public void testSave() { 24 | 25 | Foo foo = new Foo(); 26 | foo.setName("Bob"); 27 | fooRepository.save(foo); 28 | 29 | assertEquals( 30 | jdbcTemplate.queryForObject("SELECT count(*) FROM FOO", Integer.class), 31 | Integer.valueOf(1) 32 | ); 33 | 34 | } 35 | 36 | @Test(dependsOnMethods = "testSave") 37 | public void testDelete() { 38 | 39 | assertEquals( 40 | jdbcTemplate.queryForObject("SELECT count(*) FROM FOO", Integer.class), 41 | Integer.valueOf(1) 42 | ); 43 | 44 | Foo foo = new Foo(); 45 | foo.setName("Bob"); 46 | fooRepository.save(foo); 47 | 48 | fooRepository.delete(foo.getName()); 49 | assertEquals( 50 | jdbcTemplate.queryForObject("SELECT count(*) FROM FOO", Integer.class), 51 | Integer.valueOf(0) 52 | ); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /rdbs/src/test/java/me/chanjar/spring1/Spring_1_IT_Configuration.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.spring1; 2 | 3 | import me.chanjar.domain.FooRepository; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.ComponentScan; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.jdbc.core.JdbcTemplate; 8 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 9 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 10 | 11 | import javax.sql.DataSource; 12 | 13 | @Configuration 14 | @ComponentScan(basePackageClasses = FooRepository.class) 15 | public class Spring_1_IT_Configuration { 16 | 17 | @Bean(destroyMethod = "shutdown") 18 | public DataSource dataSource() { 19 | 20 | return new EmbeddedDatabaseBuilder() 21 | .generateUniqueName(true) 22 | .setType(EmbeddedDatabaseType.H2) 23 | .setScriptEncoding("UTF-8") 24 | .ignoreFailedDrops(true) 25 | .addScript("classpath:me/chanjar/domain/foo-ddl.sql") 26 | .build(); 27 | } 28 | 29 | @Bean 30 | public JdbcTemplate jdbcTemplate() { 31 | 32 | return new JdbcTemplate(dataSource()); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /rdbs/src/test/java/me/chanjar/spring1/index.jsp: -------------------------------------------------------------------------------- 1 |

Hello world

2 | -------------------------------------------------------------------------------- /rdbs/src/test/java/me/chanjar/spring2/Spring_2_IT.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.spring2; 2 | 3 | import me.chanjar.domain.Foo; 4 | import me.chanjar.domain.FooRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.test.context.ContextConfiguration; 7 | import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests; 8 | import org.testng.annotations.Test; 9 | 10 | import static org.testng.Assert.assertEquals; 11 | 12 | @ContextConfiguration(classes = Spring_2_IT_Configuration.class) 13 | public class Spring_2_IT extends AbstractTransactionalTestNGSpringContextTests { 14 | 15 | @Autowired 16 | private FooRepository fooRepository; 17 | 18 | @Test 19 | public void testSave() { 20 | 21 | Foo foo = new Foo(); 22 | foo.setName("Bob"); 23 | fooRepository.save(foo); 24 | 25 | assertEquals(countRowsInTable("FOO"), 1); 26 | countRowsInTableWhere("FOO", "name = 'Bob'"); 27 | } 28 | 29 | @Test(dependsOnMethods = "testSave") 30 | public void testDelete() { 31 | 32 | assertEquals(countRowsInTable("FOO"), 0); 33 | 34 | Foo foo = new Foo(); 35 | foo.setName("Bob"); 36 | fooRepository.save(foo); 37 | 38 | fooRepository.delete(foo.getName()); 39 | assertEquals(countRowsInTable("FOO"), 0); 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /rdbs/src/test/java/me/chanjar/spring2/Spring_2_IT_Configuration.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.spring2; 2 | 3 | import me.chanjar.domain.FooRepository; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.ComponentScan; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.jdbc.core.JdbcTemplate; 8 | import org.springframework.jdbc.datasource.DataSourceTransactionManager; 9 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; 10 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; 11 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; 12 | import org.springframework.transaction.PlatformTransactionManager; 13 | 14 | import javax.sql.DataSource; 15 | 16 | @Configuration 17 | @ComponentScan(basePackageClasses = FooRepository.class) 18 | public class Spring_2_IT_Configuration { 19 | 20 | @Bean 21 | public DataSource dataSource() { 22 | 23 | EmbeddedDatabase db = new EmbeddedDatabaseBuilder() 24 | .generateUniqueName(true) 25 | .setType(EmbeddedDatabaseType.H2) 26 | .setScriptEncoding("UTF-8") 27 | .ignoreFailedDrops(true) 28 | .addScript("classpath:me/chanjar/domain/foo-ddl.sql") 29 | .build(); 30 | return db; 31 | } 32 | 33 | @Bean 34 | public JdbcTemplate jdbcTemplate() { 35 | 36 | return new JdbcTemplate(dataSource()); 37 | 38 | } 39 | 40 | @Bean 41 | public PlatformTransactionManager transactionManager() { 42 | return new DataSourceTransactionManager(dataSource()); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /rdbs/src/test/java/me/chanjar/springboot1/Boot_1_IT.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.springboot1; 2 | 3 | import me.chanjar.domain.Foo; 4 | import me.chanjar.domain.FooRepository; 5 | import org.flywaydb.core.Flyway; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests; 10 | import org.testng.annotations.AfterTest; 11 | import org.testng.annotations.Test; 12 | 13 | import static org.testng.Assert.assertEquals; 14 | 15 | @SpringBootTest 16 | @SpringBootApplication(scanBasePackageClasses = FooRepository.class) 17 | public class Boot_1_IT extends AbstractTransactionalTestNGSpringContextTests { 18 | 19 | @Autowired 20 | private FooRepository fooRepository; 21 | 22 | @Autowired 23 | private Flyway flyway; 24 | 25 | @Test 26 | public void testSave() { 27 | 28 | Foo foo = new Foo(); 29 | foo.setName("Bob"); 30 | fooRepository.save(foo); 31 | 32 | assertEquals(countRowsInTable("FOO"), 1); 33 | countRowsInTableWhere("FOO", "name = 'Bob'"); 34 | } 35 | 36 | @Test(dependsOnMethods = "testSave") 37 | public void testDelete() { 38 | 39 | assertEquals(countRowsInTable("FOO"), 0); 40 | 41 | Foo foo = new Foo(); 42 | foo.setName("Bob"); 43 | fooRepository.save(foo); 44 | 45 | fooRepository.delete(foo.getName()); 46 | assertEquals(countRowsInTable("FOO"), 0); 47 | 48 | } 49 | 50 | @AfterTest 51 | public void cleanDb() { 52 | flyway.clean(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /share-config/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | me.chanjar 7 | spring-test-examples-aggregator 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | me.chanjar 12 | spring-test-examples-share-config 13 | jar 14 | 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-jdbc 26 | 27 | 28 | 29 | org.flywaydb 30 | flyway-core 31 | 32 | 33 | 34 | com.h2database 35 | h2 36 | 37 | 38 | 39 | org.slf4j 40 | slf4j-api 41 | 42 | 43 | 44 | ch.qos.logback 45 | logback-classic 46 | 47 | 48 | 49 | 50 | org.testng 51 | testng 52 | test 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-test 58 | test 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-jar-plugin 68 | 69 | 70 | org.apache.maven.plugins 71 | maven-failsafe-plugin 72 | 73 | 74 | integration-test 75 | 76 | integration-test 77 | 78 | 79 | 80 | verify 81 | 82 | verify 83 | 84 | 85 | 86 | 87 | 88 | 98 | ${spring.config.location} 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /share-config/src/main/java/me/chanjar/shareconfig/config/FooRepositoryConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.shareconfig.config; 2 | 3 | import me.chanjar.shareconfig.service.FooRepository; 4 | import me.chanjar.shareconfig.service.FooRepositoryImpl; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.jdbc.core.JdbcTemplate; 9 | 10 | @Configuration 11 | public class FooRepositoryConfiguration { 12 | 13 | @Autowired 14 | private JdbcTemplate jdbcTemplate; 15 | 16 | @Bean 17 | public FooRepository fooRepository() { 18 | FooRepositoryImpl repository = new FooRepositoryImpl(); 19 | repository.setJdbcTemplate(jdbcTemplate); 20 | return repository; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /share-config/src/main/java/me/chanjar/shareconfig/service/Foo.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.shareconfig.service; 2 | 3 | public class Foo { 4 | 5 | private String name; 6 | 7 | public String getName() { 8 | return name; 9 | } 10 | 11 | public void setName(String name) { 12 | this.name = name; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /share-config/src/main/java/me/chanjar/shareconfig/service/FooRepository.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.shareconfig.service; 2 | 3 | public interface FooRepository { 4 | void save(Foo foo); 5 | 6 | void delete(String name); 7 | } 8 | -------------------------------------------------------------------------------- /share-config/src/main/java/me/chanjar/shareconfig/service/FooRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.shareconfig.service; 2 | 3 | import org.springframework.jdbc.core.JdbcTemplate; 4 | 5 | public class FooRepositoryImpl implements FooRepository { 6 | 7 | private JdbcTemplate jdbcTemplate; 8 | 9 | @Override 10 | public void save(Foo foo) { 11 | jdbcTemplate.update("INSERT INTO FOO(name) VALUES (?)", foo.getName()); 12 | } 13 | 14 | @Override 15 | public void delete(String name) { 16 | jdbcTemplate.update("DELETE FROM FOO WHERE NAME = ?", name); 17 | } 18 | 19 | public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 20 | this.jdbcTemplate = jdbcTemplate; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /share-config/src/main/resources/db/migration/V1.0.0__foo-ddl.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE FOO ( 2 | name VARCHAR2(100) 3 | ); 4 | -------------------------------------------------------------------------------- /share-config/src/test/java/me/chanjar/shareconfig/annotation/FooRepositoryIT.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.shareconfig.annotation; 2 | 3 | import me.chanjar.shareconfig.service.FooRepositoryTestBase; 4 | import me.chanjar.shareconfig.testconfig.AnnotationConfiguration; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest(classes = FooRepositoryIT.class) 8 | @AnnotationConfiguration 9 | public class FooRepositoryIT extends FooRepositoryTestBase { 10 | 11 | } 12 | 13 | -------------------------------------------------------------------------------- /share-config/src/test/java/me/chanjar/shareconfig/configuration/FooRepositoryIT.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.shareconfig.configuration; 2 | 3 | import me.chanjar.shareconfig.service.FooRepositoryTestBase; 4 | import me.chanjar.shareconfig.testconfig.PlainConfiguration; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest(classes = PlainConfiguration.class) 8 | public class FooRepositoryIT extends FooRepositoryTestBase { 9 | 10 | } 11 | 12 | -------------------------------------------------------------------------------- /share-config/src/test/java/me/chanjar/shareconfig/inter/FooRepositoryIT.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.shareconfig.inter; 2 | 3 | import me.chanjar.shareconfig.service.FooRepositoryTestBase; 4 | import me.chanjar.shareconfig.testconfig.InterfaceConfiguration; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest(classes = InterfaceConfiguration.class) 8 | public class FooRepositoryIT extends FooRepositoryTestBase { 9 | 10 | } 11 | 12 | -------------------------------------------------------------------------------- /share-config/src/test/java/me/chanjar/shareconfig/service/FooRepositoryTestBase.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.shareconfig.service; 2 | 3 | import org.flywaydb.core.Flyway; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests; 6 | import org.testng.annotations.AfterTest; 7 | import org.testng.annotations.Test; 8 | 9 | import static org.testng.Assert.assertEquals; 10 | 11 | public class FooRepositoryTestBase extends AbstractTransactionalTestNGSpringContextTests { 12 | 13 | @Autowired 14 | private FooRepository fooRepository; 15 | 16 | @Autowired 17 | private Flyway flyway; 18 | 19 | @Test 20 | public void testSave() { 21 | 22 | Foo foo = new Foo(); 23 | foo.setName("Bob"); 24 | fooRepository.save(foo); 25 | 26 | assertEquals(countRowsInTable("FOO"), 1); 27 | countRowsInTableWhere("FOO", "name = 'Bob'"); 28 | } 29 | 30 | @Test(dependsOnMethods = "testSave") 31 | public void testDelete() { 32 | 33 | assertEquals(countRowsInTable("FOO"), 0); 34 | 35 | Foo foo = new Foo(); 36 | foo.setName("Bob"); 37 | fooRepository.save(foo); 38 | 39 | fooRepository.delete(foo.getName()); 40 | assertEquals(countRowsInTable("FOO"), 0); 41 | 42 | } 43 | 44 | @AfterTest 45 | public void cleanDb() { 46 | flyway.clean(); 47 | } 48 | 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /share-config/src/test/java/me/chanjar/shareconfig/testconfig/AnnotationConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.shareconfig.testconfig; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | @Target(ElementType.TYPE) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @SpringBootApplication(scanBasePackages = "me.chanjar.shareconfig") 13 | public @interface AnnotationConfiguration { 14 | } 15 | -------------------------------------------------------------------------------- /share-config/src/test/java/me/chanjar/shareconfig/testconfig/InterfaceConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.shareconfig.testconfig; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | @SpringBootApplication(scanBasePackages = "me.chanjar.shareconfig") 6 | public interface InterfaceConfiguration { 7 | } 8 | -------------------------------------------------------------------------------- /share-config/src/test/java/me/chanjar/shareconfig/testconfig/PlainConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.chanjar.shareconfig.testconfig; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | @SpringBootApplication(scanBasePackages = "me.chanjar.shareconfig") 6 | public class PlainConfiguration { 7 | } 8 | --------------------------------------------------------------------------------