Curse of Spring Boot Test [VRN]

Curse of Spring Boot Test [VRN]

Особенности тестирования Spring Boot приложения. Нововведения с версии spring-boot 1.4.+
В программе:
* Старые подходы
** @ContextConfiguration
** @ContextHierarchy && @DirtiesContext
** @ActiveProfiles
* Что нового нам приготовил Spring Boot?
** @SpringBootTest
** @TestConfiguration
** @SpringBootConfiguration и его связь с @SpringBootApplicatoin
** @MockBean && @SpyBean && @*Beans
** @DataJpaTest
** @WebMvcTest
* Кэширование spring контекстов
* Шкала тестов
* Порядок сканирвоания контекста test+main. Подводные камни этого процесса

Слайды с доклада "Проклятие Spring Boot Test" на JUG в рамках РИФ Воронеж

40951719c6ca509831d5c38b764661c9?s=128

Kirill Tolkachev

October 09, 2017
Tweet

Transcript

  1. Тестируем Вместе со Spring Boot Test

  2. @tolkv @lavcraft

  3. @jekaborisov @jeka1978

  4. В программе Тестирование живого приложения • Старые подходы ◦ @ContextConfiguration

    ◦ @ContextHierarchy && @DirtiesContext ◦ @ActiveProfiles • Что нового нам приготовил Spring Boot? ◦ @SpringBootTest ◦ @TestConfiguration ◦ @MockBean && @SpyBean && @*Beans ◦ @DataJpaTest ◦ @WebMvcTest • Кэширование spring контекстов • Шкала тестов
  5. Шкала Тестов Unit Component Test Microservice Test System Test ➯

    ➯ ➯ ➯
  6. Дано Чат поддержки разработчиков assistant yegor256-assistant jbaruch-assistant Queue мы web

    rest rest rest Default Answers Database
  7. Эксперты

  8. Дано Чат поддержки разработчиков assistant yegor256-assistant jbaruch-assistant Queue мы web

    rest rest rest Default Answers Database
  9. None
  10. None
  11. None
  12. Demo

  13. А давайте тестировать

  14. А давайте тестировать router yegor256-assistant jbaruch-assistant Default Answers Database Yegor256

    Resolver $tokens.yegor256 JBaruch Resolver ... $tokens.jbaruch
  15. А давайте тестировать. Тест #1 1. Пишем Egor256WordsFrequencyResolverTest.

  16. Demo

  17. Кого тестируем @Component public class Yegor256WordsFrequencyResolver extends AbstractWordsFreqResolver { @Value("${tokens.yegor256}")

    private String answers; public Yegor256WordsFrequencyResolver(WordsComposer wordsComposer) { super(wordsComposer); } @Override public QuestionType getQuestionType() { return YEGOR256; } }
  18. Тест №1.5 public class Yegor256WordsFrequencyResolverTest { @Test public void name()

    throws Exception { Yegor256WordsFrequencyResolver yegor256WordsFrequencyResolver = new Yegor256WordsFrequencyResolver( ... ) ); yegor256WordsFrequencyResolver.setAnswers( "objects"); int match = yegor256WordsFrequencyResolver.match( Question. builder().body("objects ...").build()); assertThat(match, equalTo(1)); } }
  19. Тест №1.5 public class Yegor256WordsFrequencyResolverTest { @Test public void name()

    throws Exception { Yegor256WordsFrequencyResolver yegor256WordsFrequencyResolver = new Yegor256WordsFrequencyResolver( new WordsComposer( ... ) ); yegor256WordsFrequencyResolver.setAnswers( "objects"); int match = yegor256WordsFrequencyResolver.match( Question. builder().body("objects ...").build()); assertThat(match, equalTo(1)); } }
  20. Тест №1.5 public class Yegor256WordsFrequencyResolverTest { @Test public void name()

    throws Exception { Yegor256WordsFrequencyResolver yegor256WordsFrequencyResolver = new Yegor256WordsFrequencyResolver( new WordsComposer( new GarbageProperties() ) ); yegor256WordsFrequencyResolver.setAnswers( "objects"); int match = yegor256WordsFrequencyResolver.match( Question. builder().body("objects ...").build()); assertThat(match, equalTo(1)); } }
  21. Тест №1 public class Yegor256WordsFrequencyResolverTest { @Test public void name()

    throws Exception { Yegor256WordsFrequencyResolver yegor256WordsFrequencyResolver = new Yegor256WordsFrequencyResolver( new WordsComposer( new GarbageProperties() ) ); yegor256WordsFrequencyResolver.setAnswers( "objects"); int match = yegor256WordsFrequencyResolver.match( Question. builder().body("objects ...").build()); assertThat(match, equalTo(1)); } }
  22. Тест №1 public class Yegor256WordsFrequencyResolverTest { @Test public void name()

    throws Exception { Yegor256WordsFrequencyResolver yegor256WordsFrequencyResolver = new Yegor256WordsFrequencyResolver( new WordsComposer( new GarbageProperties() ) ); yegor256WordsFrequencyResolver.setAnswers( "objects"); int match = yegor256WordsFrequencyResolver.match( Question. builder().body("objects ...").build()); assertThat(match, equalTo(1)); } }
  23. Результат java.lang.NullPointerException at … .(WordsComposer.java:48)

  24. Not Passed

  25. WordsComposer:48 garbageProperties.getGarbage() .contains(s.toLowerCase()) NullPointerException

  26. WordsComposer:48 @Value("${garbage}") void setGarbage(String[] garbage) {

  27. А давайте тестировать. Тест #1 1. Пишем Egor256WordsFrequencyResolverTest. 2. Как

    ни крути, но нужен более “интеграционный тест”
  28. Шкала Тестов Unit ➯

  29. Шкала Тестов Unit Component Test ➯ ➯

  30. Demo

  31. Тест №1.5 @RunWith(SpringRunner. class) @ContextConfiguration (classes = Yegor256WordsFrequencyResolverTestConfig. class) public

    class Yegor256WordsFrequencyResolverTest { @Autowired Yegor256WordsFrequencyResolver yegor256WordsFrequencyResolver; @Test public void name() throws Exception { yegor256WordsFrequencyResolver.setAnswers("objects"); int match = yegor256WordsFrequencyResolver.match( Question. builder().body("objects ...").build()); assertThat(match, equalTo(1)); } }
  32. Тест №1.5 @Configuration public class Yegor256WordsFrequencyResolverTestConfig { @Bean public Yegor256WordsFrequencyResolver

    yegor256WordsFrequencyResolver( WordsComposer wordsComposer) { return new Yegor256WordsFrequencyResolver(wordsComposer); } }
  33. Тест №1.5 @Configuration public class Yegor256WordsFrequencyResolverTestConfig { @Bean public Yegor256WordsFrequencyResolver

    yegor256WordsFrequencyResolver( WordsComposer wordsComposer) { return new Yegor256WordsFrequencyResolver(wordsComposer); } }
  34. Тест №1.5 @Configuration @ComponentScan("com.conference.spring.test.common") public class Yegor256WordsFrequencyResolverTestConfig { @Bean public

    Yegor256WordsFrequencyResolver yegor256WordsFrequencyResolver( WordsComposer wordsComposer) { return new Yegor256WordsFrequencyResolver(wordsComposer); } }
  35. Тест №1.5 @Configuration @ComponentScan("com.conference.spring.test.common") public class Yegor256WordsFrequencyResolverTestConfig { @Bean public

    Yegor256WordsFrequencyResolver yegor256WordsFrequencyResolver( WordsComposer wordsComposer) { return new Yegor256WordsFrequencyResolver(wordsComposer); } }
  36. Тест №1.5 @RunWith(SpringRunner. class) @ContextConfiguration (classes = Yegor256WordsFrequencyResolverTestConfig. class) public

    class Yegor256WordsFrequencyResolverTest { @Autowired Yegor256WordsFrequencyResolver yegor256WordsFrequencyResolver; @Test public void name() throws Exception { yegor256WordsFrequencyResolver.setAnswers("objects"); int match = yegor256WordsFrequencyResolver.match( Question. builder().body("objects ...").build()); assertThat(match, equalTo(1)); } }
  37. Passed

  38. SpringRunner /** * @author Sam Brannen * @since 4.3 *

    @see SpringJUnit4ClassRunner */ public final class SpringRunner extends SpringJUnit4ClassRunner
  39. SpringRunner & SpringJUnit4ClassRunner /** * @author Sam Brannen * @author

    Juergen Hoeller * ... */ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner
  40. 1. Пишем TextBasedQuestionTypeResolverTest А давайте тестировать. Тест #2

  41. Шкала Тестов Unit Component Test ➯ ➯

  42. 1. Пишем TextBasedQuestionTypeResolverTest 2. Вручную создаем три бина для тестирования

    TextBasedQuestionTypeResolver на примере Барух vs Егор кейса А давайте тестировать. Тест #2
  43. Demo

  44. @RunWith(SpringRunner. class) @ContextConfiguration (classes = TextBasedQuestionTypeResolverTestConfig. class) public class TextBasedQuestionTypeResolverTest

    { @Autowired TextBasedQuestionTypeResolver questionResolver; @Test public void name() throws Exception { QuestionType groovy = questionResolver.resolveType(new Question("groovy")); QuestionType objects = questionResolver.resolveType(new Question("objects")); assertThat(groovy, equalTo(JBARUCH)); assertThat(objects, equalTo(YEGOR256)); } } Тест #2
  45. @Configuration public class TextBasedQuestionTypeResolverTestConfig { @Bean public TextBasedQuestionTypeResolver textBasedQuestionTypeResolver( List<WordsFrequencyResolver>

    c) { return new TextBasedQuestionTypeResolver(c); } } Тест #2
  46. @Configuration public class TextBasedQuestionTypeResolverTestConfig { @Bean public TextBasedQuestionTypeResolver textBasedQuestionTypeResolver( List<WordsFrequencyResolver>

    c) { return new TextBasedQuestionTypeResolver(c); } @Bean public Yegor256WordsFrequencyResolver … { … } @Bean public JBaruchWordsFrequencyResolver … { … } } Тест #2
  47. @Configuration public class TextBasedQuestionTypeResolverTestConfig { @Bean public TextBasedQuestionTypeResolver textBasedQuestionTypeResolver( List<WordsFrequencyResolver>

    c) { return new TextBasedQuestionTypeResolver(c); } @Bean public Yegor256WordsFrequencyResolver … { … } @Bean public JBaruchWordsFrequencyResolver … { … } } Тест #2 Для них нужен WordsComposer @ComponentScan("com.conference.spring.test.common") ?
  48. @Configuration public class TextBasedQuestionTypeResolverTestConfig { @Bean public TextBasedQuestionTypeResolver textBasedQuestionTypeResolver( List<WordsFrequencyResolver>

    c) { return new TextBasedQuestionTypeResolver(c); } @Bean public Yegor256WordsFrequencyResolver … { … } @Bean public JBaruchWordsFrequencyResolver … { … } } Тест #2 Для них нужен WordsComposer @ComponentScan("com.conference.spring.test.common") ?
  49. @Configuration @Import(CommonConfig. class) public class TextBasedQuestionTypeResolverTestConfig { @Bean public TextBasedQuestionTypeResolver

    textBasedQuestionTypeResolver( List<WordsFrequencyResolver> c) { return new TextBasedQuestionTypeResolver(c); } @Bean public Yegor256WordsFrequencyResolver … { … } @Bean public JBaruchWordsFrequencyResolver … { … } } Тест #2
  50. @Configuration @ComponentScan("com.conference.spring.test.common") public class CommonConfig { } Тест #2

  51. Not Passed

  52. class Yegor256WordsFrequencyResolver @Value("${tokens.yegor256}") private String answers; class JBaruchWordsFrequencyResolver @Value("${tokens.jbaruch}") private

    String answers; Кто считывает? Что случилось
  53. Что случилось class Yegor256WordsFrequencyResolver @Value("${tokens.yegor256}") private String answers; class JBaruchWordsFrequencyResolver

    @Value("${tokens.jbaruch}") private String answers; application.yml: tokens: jbaruch: npm leftpad artifactory groovy object *** yegor256: objects Кто считывает? Отсюда считываем
  54. @Configuration @Import(CommonConfig. class) @PropertySource ("classpath*:application.yml") public class TextBasedQuestionTypeResolverTestConfig { @Bean

    public TextBasedQuestionTypeResolver textBasedQuestionTypeResolver( List<WordsFrequencyResolver> c) { return new TextBasedQuestionTypeResolver(c); } @Bean public Yegor256WordsFrequencyResolver … { … } @Bean public JBaruchWordsFrequencyResolver … { … } } Тест #2
  55. @Configuration @Import(CommonConfig. class) @PropertySource ("classpath*:application.yml") public class TextBasedQuestionTypeResolverTestConfig { @Bean

    public TextBasedQuestionTypeResolver textBasedQuestionTypeResolver( List<WordsFrequencyResolver> c) { return new TextBasedQuestionTypeResolver(c); } @Bean public Yegor256WordsFrequencyResolver … { … } @Bean public JBaruchWordsFrequencyResolver … { … } } Тест #2
  56. Not Passed

  57. 1. Пишем TextBasedQuestionTypeResolverTest 2. Вручную создаем три бина для тестирования

    TextBasedQuestionTypeResolver на примере Барух vs Егор кейса 3. Все падает потому что не подтягивается application.yml 4. @PropertySource … А давайте тестировать. Тест #2
  58. @ContextConfiguration(classes = ....class, initializers = YamlFileApplicationContextInitializer.class) public class OurTest {

    @Test public test(){ ... } } А давайте тестировать. Тест #2
  59. Spring Boot обновки 1. @SpringBootTest 2. @TestConfiguration 3. @MockBean &&

    @SpyBean 4. @DataJpaTest 5. @MockMvcTest
  60. Углубляемся в Spring. Тест #2 1. Применяем @SpringBootTest

  61. Demo

  62. @RunWith(SpringRunner. class) @ContextConfiguration (classes = TextBasedQuestionTypeResolverTestConfig. class) public class TextBasedQuestionTypeResolverTest

    { @Autowired TextBasedQuestionTypeResolver questionResolver; @Test public void name() throws Exception { QuestionType groovy = questionResolver.resolveType(new Question("groovy")); QuestionType objects = questionResolver.resolveType(new Question("objects")); assertThat(groovy, equalTo(JBARUCH)); assertThat(objects, equalTo(YEGOR256)); } } Тест #2
  63. @RunWith(SpringRunner. class) @SpringBootTest public class TextBasedQuestionTypeResolverTest { @Autowired TextBasedQuestionTypeResolver questionResolver;

    @Test public void name() throws Exception { QuestionType groovy = questionResolver.resolveType(new Question("groovy")); QuestionType objects = questionResolver.resolveType(new Question("objects")); assertThat(groovy, equalTo(JBARUCH)); assertThat(objects, equalTo(YEGOR256)); } } Тест #2
  64. Not Passed

  65. @RunWith(SpringRunner. class) @SpringBootTest @ActiveProfiles ("yegor_vs_jbaruch") public class TextBasedQuestionTypeResolverTest { @Autowired

    TextBasedQuestionTypeResolver questionResolver; @Test public void name() throws Exception { QuestionType groovy = questionResolver.resolveType(new Question("groovy")); QuestionType objects = questionResolver.resolveType(new Question("objects")); assertThat(groovy, equalTo(JBARUCH)); assertThat(objects, equalTo(YEGOR256)); } } Тест #2 Для подгрузки application-yegor_vs_jbaruch.yml
  66. Passed

  67. Углубляемся в Spring. Тест #2 1. Применяем @SpringBootTest 2. Долго…

    3. @SpringBootTest(classes = ...class)
  68. Углубляемся в Spring. Тест #2 1. Применяем @SpringBootTest 2. Долго…

    3. @SpringBootTest(classes = ...class) 4. Стало быстрее
  69. Demo - но можно лучше

  70. @Configuration @ComponentScan("com.conference.spring.test.common") public class CommonConfig { @PostConstruct public void init()

    { System.out.println("Only once " + CommonConfig.class); } } Тест #2
  71. Запустим тест №1 и №2 за раз

  72. Only once … only once … only once

  73. Only once … only once … only once Дважды...

  74. Углубляемся в Spring. Тест #2 1. Применяем @SpringBootTest 2. Долго…

    3. @SpringBootTest(classes = ...class) 4. Стало быстрее 5. С кэшированием конфигураций – еще быстрее
  75. Углубляемся в Spring. Тест #2 @ContextHierarchy({ @ContextConfiguration(classes=WordsCommonConfiguration.class), @ContextConfiguration(classes= ...class) })

  76. Demo

  77. @SpringBootTest @ContextHierarchy({ @ContextConfiguration(classes = TextBasedQuestionTypeResolverTestConfig. class), @ContextConfiguration(classes = CommonConfig. class)

    }) @ActiveProfiles("yegor_vs_jbaruch") @RunWith(SpringRunner.class) public class TextBasedQuestionTypeResolverTest { ...
  78. Запустим тест №1 и №2 за раз

  79. Only once … only once … only once … only

    once … only once Четыре раза...
  80. None
  81. @Configuration @Import(CommonConfig.class) public class Yegor256WordsFrequencyResolverTestConfig {

  82. @Configuration @Import(CommonConfig.class) public class TextBasedQuestionTypeResolverTestConfig {

  83. Убираем @Import(CommonConfig.class)

  84. Not Passed

  85. None
  86. Не найден spring bean WordsComposer

  87. Углубляемся в Spring. Тест #2 @ContextHierarchy({ @ContextConfiguration(classes=WordsCommonConfiguration.class), @ContextConfiguration(classes= ...class) })

    Порядок важен! Т.к другая конфигурация использует бины из WordsCommonConfiguration
  88. Меняем порядок в @ContextHierarchy @SpringBootTest @ContextHierarchy({ @ContextConfiguration (classes = CommonConfig.

    class), @ContextConfiguration(classes = TextBasedQuestionTypeResolverTestConfig. class) }) @ActiveProfiles("yegor_vs_jbaruch") @RunWith(SpringRunner.class) public class TextBasedQuestionTypeResolverTest { ... CommonConfig теперь первый
  89. Passed

  90. Only once … only once … only once Дважды...

  91. Сделали круг

  92. Опять не закешировалось. Тест #2

  93. Правила кэширования контекстов. Тест #2 @SpringBootTest – должен быть везде

  94. Правила кэширования контекстов. Тест #2 @SpringBootTest – должен быть везде

    @Import – должен быть нигде
  95. Правила кэширования контекстов. Тест #2 @SpringBootTest – должен быть везде

    @Import – должен быть нигде @ActiveProfiles – один на всех
  96. Правила кэширования контекстов. Тест #2 @SpringBootTest – должен быть везде

    @Import – должен быть нигде @ActiveProfiles – один на всех SpringBootTest.properties – должны быть одинаковые
  97. Правила кэширования контекстов. Тест #2 @SpringBootTest – должен быть везде

    @Import – должен быть нигде @ActiveProfiles – один на всех SpringBootTest.properties – должны быть одинаковые Порядок важен! Любая перестановка – cache miss
  98. Правила кэширования контекстов. Тест #2 @SpringBootTest(properties={"a=b","b=a"}) @SpringBootTest(properties={"b=a","a=b"})

  99. Правила кэширования контекстов. Тест #2 @SpringBootTest(properties={"a=b","b=a"}) @SpringBootTest(properties={"b=a","a=b"}) Кэш не сработает

  100. Правила кэширования контекстов. Тест #2 @SpringBootTest – должен быть везде

    @Import – должен быть нигде @ActiveProfiles – один на всех SpringBootTest.properties – должны быть одинаковые
  101. Хрупкий кэш Все может привести к потере кэша

  102. Пользуемся силой logging.level.org.springframework.test.context.cache=debug

  103. Б – безопасность @SpringBootTest @ActiveProfiles("yegor_vs_jbaruch") public abstract class ResolversAbstractCommonConfiguration {

    }
  104. Only once … only once Один!...

  105. А если наоборот? (как не кэшировать)

  106. А если наоборот? (как не кэшировать) @DirtiesContext(...)

  107. А если наоборот? (как не кэшировать) @DirtiesContext(...) methodMode() default MethodMode.AFTER_METHOD

    classMode() default ClassMode.AFTER_CLASS ...
  108. Проверим наши знания. Тест #3 1. протестируем AnswerCacheServiceJPABackend

  109. Demo

  110. Что тестируем @Service @RequiredArgsConstructor public class AnswerCacheServiceJPABackend implements AnswerCacheService {

    private final QuestionRepository questionRepository; private final AnswersRepository answersRepository; @Override public Answer find(Question question) { … } … }
  111. Что тестируем @Service @RequiredArgsConstructor public class AnswerCacheServiceJPABackend implements AnswerCacheService {

    private final QuestionRepository questionRepository; private final AnswersRepository answersRepository; @Override public Answer find(Question question) { … } … }
  112. Что тестируем @Service @RequiredArgsConstructor public class AnswerCacheServiceJPABackend implements AnswerCacheService {

    private final QuestionRepository questionRepository; private final AnswersRepository answersRepository; @Override public Answer find(Question question) { … } … }
  113. Spring Boot обновки 1. @SpringBootTest 2. @MockBean && @SpyBean 3.

    @TestConfiguration 4. @DataJpaTest 5. @MockMvcTest
  114. Как тестируем @RunWith(SpringRunner. class) @SpringBootTest (classes = AnswerCacheServiceJPABackendTestConfig. class) public

    class AnswerCacheServiceJPABackendTest { @Autowired AnswerCacheService answerCacheService; @MockBean AnswersRepository answersRepository; @MockBean QuestionRepository questionRepository; @Test public void should_not_fail() throws Exception { … test … } }
  115. Как тестируем @RunWith(SpringRunner. class) @SpringBootTest (classes = AnswerCacheServiceJPABackendTestConfig. class) public

    class AnswerCacheServiceJPABackendTest { @Autowired AnswerCacheService answerCacheService; @MockBean AnswersRepository answersRepository; @MockBean QuestionRepository questionRepository; @Test public void should_not_fail() throws Exception { … test … } }
  116. Как тестируем @RunWith(SpringRunner. class) @SpringBootTest (classes = AnswerCacheServiceJPABackendTestConfig. class) public

    class AnswerCacheServiceJPABackendTest { @Autowired AnswerCacheService answerCacheService; @MockBean AnswersRepository answersRepository; @MockBean QuestionRepository questionRepository; @Test public void should_not_fail() throws Exception { … test … } }
  117. Как тестируем – Конфигурация @Configuration public class AnswerCacheServiceJPABackendTestConfig { @Bean

    public AnswerCacheServiceJPABackend answerCacheServiceJpaBackend( QuestionRepository qR, AnswersRepository aR) { return new AnswerCacheServiceJPABackend(qR, aR); } }
  118. Как тестируем – сам тест @Test public void should_not_fail() throws

    Exception { Mockito.doThrow(new RuntimeException( "Database is down")) .when(questionRepository) .findFirstByText(Matchers. anyString()); Answer answer = answerCacheService.find(Question. builder().build()); assertNull(answer); } } Наш @MockBean
  119. Passed

  120. Синергия с Mockito 1. @MockBean/@SpyBean 2. @PostConstruct для настройки 3.

    @Bean для настройки конкретных моков
  121. 1. Запустим все тесты Все ли хорошо?

  122. Not Passed

  123. 1. Запустим все тесты 2. DeveloperAssistantApplicationTests.contextLoad падает Все ли хорошо?

    Стандартный тест на запуск контекст (см start.spring.io)
  124. Почему упал Два бина одного типа в контексте • answerCacheServiceJPABackend

    • answerCacheServiceJpaBackend
  125. Почему упал Два бина одного типа в контексте • answerCacheServiceJPABackend

    • answerCacheServiceJpaBackend
  126. Как Spring называет бины? почему имена бинов разные

  127. 1. Запустим все тесты 2. DeveloperAssistantApplicationTests.contextLoad падает 3. Загрузил бины

    из другого теста! Все ли хорошо?
  128. None
  129. Spring Boot обновки 1. @SpringBootTest 2. @MockBean && @SpyBean 3.

    @TestConfiguration 4. @DataJpaTest 5. @MockMvcTest
  130. 1. Запустим все тесты 2. DeveloperAssistantApplicationTests.contextLoad падает 3. Загрузил бины

    из другого теста! 4. @TestConfiguration! Все ли хорошо?
  131. 1. Не сканируется @SpringBootTest 2. Не сканируется другими конфигурациями и

    тестами 3. Не прерывает процесс сканирования @SpringBootTest @TestConfiguration
  132. 1. Запустим все тесты 2. DeveloperAssistantApplicationTests.contextLoad падает 3. Загрузил бины

    из другого теста! 4. @TestConfiguration! 5. DeveloperAssistantApplicationTests.contextLoad работает Все ли хорошо?
  133. Почему упал Два бина одного типа в контексте • answerCacheServiceJPABackend

    • answerCacheServiceJpaBackend
  134. Почему упал Два бина одного типа в контексте • answerCacheServiceJPABackend

    • answerCacheServiceJpaBackend Опять двадцать пять!
  135. 1. Запустим все тесты 2. DeveloperAssistantApplicationTests.contextLoad падает 3. Загрузил бины

    из другого теста! 4. @TestConfiguration! 5. DeveloperAssistantApplicationTests.contextLoad работает 6. А AnswerCacheServiceJPABackendTest перестал 7. Загрузил бины из другого теста! Все ли хорошо?
  136. Spring Заговор

  137. 1. Как @SpringBootTest сканирует пакеты

  138. Два процесса сканирования 1. @SpringBootTest сканирование 2. @SpringBootApplication (@ComponentScan)

  139. Два процесса сканирования 1. @SpringBootTest сканирование 2. @SpringBootApplication (@ComponentScan) Вверх

  140. Два процесса сканирования 1. @SpringBootTest сканирование 2. @SpringBootApplication (@ComponentScan) Вниз

    Вверх
  141. @SpringBootTest Два процесса сканирования

  142. @SpringBootTest Два процесса сканирования

  143. @SpringBootTest Два процесса сканирования

  144. Два процесса сканирования @SpringBootTest

  145. Два процесса сканирования @SpringBootTest test classpath extends main classpath

  146. None
  147. Два процесса сканирования @SpringBootTest @SpringBootApplication src/main будет так же просканирован*

    test classpath extends main classpath
  148. Два процесса сканирования @SpringBootTest @SpringBootApplication src/main будет так же просканирован*

    test classpath extends main classpath
  149. Два процесса сканирования @SpringBootTest @SpringBootApplication src/main будет так же просканирован*

    test classpath extends main classpath
  150. Два процесса сканирования @SpringBootTest @SpringBootApplication src/main будет так же просканирован*

    test classpath extends main classpath
  151. Два процесса сканирования @SpringBootTest @SpringBootApplication src/main будет так же просканирован*

    test classpath extends main classpath
  152. Тоже и с src/main/**

  153. Как чинить @SpringBootApplication @EnableFeignClients @EnableConfigurationProperties (AssistantProperties. class) public class DeveloperAssistantApplication

    { public static void main(String[] args) { SpringApplication. run(DeveloperAssistantApplication. class, args); } }
  154. Как чинить @SpringBootApplication @EnableFeignClients @EnableConfigurationProperties (AssistantProperties. class) public class DeveloperAssistantApplication

    { public static void main(String[] args) { SpringApplication. run(DeveloperAssistantApplication. class, args); } }
  155. Как чинить @Target(ElementType.TYPE) @Retention(RetentionPolicy. RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan

    (excludeFilters = { @Filter(type = FilterType. CUSTOM, classes = TypeExcludeFilter. class), @Filter(type = FilterType. CUSTOM, classes = AutoConfigurationExcludeFilter. class) }) public @interface SpringBootApplication {
  156. Как чинить @Target(ElementType.TYPE) @Retention(RetentionPolicy. RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan

    (excludeFilters = { @Filter(type = FilterType. CUSTOM, classes = TypeExcludeFilter. class), @Filter(type = FilterType. CUSTOM, classes = AutoConfigurationExcludeFilter. class) }) public @interface SpringBootApplication {
  157. Как чинить /** * @author Phillip Webb * @since 1.4.0

    */ @Target(ElementType.TYPE) @Retention(RetentionPolicy. RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { }
  158. 1. Запустим все тесты 2. DeveloperAssistantApplicationTests.contextLoad падает 3. Загрузил бины

    из другого теста! 4. @TestConfiguration! 5. DeveloperAssistantApplicationTests.contextLoad работает 6. А AnswerCacheServiceJPABackendTest перестал 7. Загрузил бины из другого теста! 8. @SpringBootConfiguration остановит сканирование Все ли хорошо?
  159. @SpringBootConfiguration public class StopConfiguration { } В нужном пакете! Чиним

  160. @SpringBootConfiguration Нужный пакет для остановки

  161. Component Tests

  162. Spring Boot обновки 1. @SpringBootTest 2. @TestConfiguration 3. @MockBean &&

    @SpyBean 4. @DataJpaTest 5. @MockMvcTest
  163. @DataJpaTest 1. сканирует все репозитории

  164. @DataJpaTest 1. сканирует все репозитории 2. конфигурирует EntityManager 3. загружает

    другие конфигурации
  165. 1. сканирует все репозитории 2. конфигурирует EntityManager 3. загружает другие

    конфигурации 4. фильтрует все не относящееся к Data/JPA Применим знания @DataJpaTest
  166. Тестируем DefaultAssistantJpaBackendTest 1. @DataJpaTest не загружает компоненты Spring*

  167. Тестируем DefaultAssistantJpaBackendTest 1. @DataJpaTest не загружает компоненты Spring* 2. Делаем

    конфигурацию, загружаем недостающее
  168. Тестируем DefaultAssistantJpaBackendTest 1. @DataJpaTest не загружает компоненты Spring 2. Делаем

    конфигурацию, загружаем недостающее 3. Ничего не работает, из за @SpringBootConfiguration
  169. Тестируем DefaultAssistantJpaBackendTest 1. @DataJpaTest не загружает компоненты Spring* 2. Делаем

    конфигурацию, загружаем недостающее 3. Ничего не работает, из за @SpringBootConfiguration 4. Переносим в новый package – все @*Test тесты должны быть изолированы
  170. @WebMvcTest 1. Не грузит компоненты спринга

  171. @WebMvcTest 1. Не грузит компоненты спринга 2. Грузит только то

    что относится к Web
  172. @WebMvcTest 1. Не грузит компоненты спринга 2. Грузит только то

    что относится к Web 3. Сразу изолируем в отдельный пакет Получаем суперспособность: @Autowired MockMvc mockMvc;
  173. Где настраивать @MockBean 1. В @*Configuration – если мок нужен

    на этапе создания контекста 2. В тесте (@Before/setup/etc) если мок нужен только на этапе выполнения теста
  174. Что же делает @SpringBootTest 1. Без classes a. сканирует со

    своего пакета “вверх” в поисках @SpringBootConfiguration i. игнорирует остальных b. падает если не находит или находит несколько в одном пакете 2. classes=~@Configuration a. поднимет только указанные конфигурации 3. classes=~@TestConfiguration a. поднимет указанный контекст и продолжит сканирование. см пункт 1
  175. Зачем нужен @SpringBootTest 1. Полный тест на весь контекст 2.

    Изменение properties 3. Тесты с определенным скоупом – пакет/конфигурация/автоскан
  176. Зачем нужен @TestConfiguration 1. Если нужно не прерывать сканирование @SpringBootTest

    2. Изолированные тесты (игнорируется при сканировании)
  177. Зачем нужен @SpringBootConfiguration 1. Прерывать сканирование инициированное @SpringBootTest

  178. Выводы 1. Spring для Unit тестирования может быть быстрым

  179. Выводы 1. Spring для Unit тестирования может быть быстрым 2.

    Кэш контекстов – хрупкая штука
  180. Выводы 1. Spring для Unit тестирования может быть быстрым 2.

    Кэш контекстов – хрупкая штука 3. Для тестов – только @TestConfiguration
  181. Выводы 1. Spring для Unit тестирования может быть быстрым 2.

    Кэш контекстов – хрупкая штука 3. Для тестов – только @TestConfiguration 4. Изолировать группы тестов с помощью
  182. Выводы 1. Spring для Unit тестирования может быть быстрым 2.

    Кэш контекстов – хрупкая штука 3. Для тестов – только @TestConfiguration 4. Изолировать группы тестов с помощью a. выделения в пакеты b. @SpringBootConfiguration
  183. Выводы 1. Spring для Unit тестирования может быть быстрым 2.

    Кэш контекстов – хрупкая штука 3. Для тестов – только @TestConfiguration 4. Изолировать группы тестов с помощью a. выделения в пакеты (особенно для @*Test) b. @SpringBootConfiguration 5. SpringBootTest надо в основном использовать для микросервис тестов
  184. Выводы 1. Spring для Unit тестирования может быть быстрым 2.

    Кэш контекстов – хрупкая штука 3. Для тестов – только @TestConfiguration 4. Изолировать группы тестов с помощью a. выделения в пакеты b. @SpringBootConfiguration 5. SpringBootTest надо в основном использовать для микросервис тестов 6. Если есть DirtiesContext – стоит задуматься :)
  185. Шкала Тестов Unit Component Test Microservice Test System Test ➯

    ➯ ➯ ➯ Следующий доклад
  186. Unit Component Microservice Что нужно Junit/Mockito @ContextConfiguration @SpringBootTest Кто управляет

    new Spring Spring Boot
  187. QA 187

  188. 1. @ComponentScan > @TestConfiguration > @Configuratin ! @ComponentScan находит даже

    @TestConfiguration 2. @DataJpaTest > @SpringBootTest 3. @DataJpaTest и @WebMvcTest должны быть в отдельных пакетах Если есть сомнения – смотри автора! Juergen Hoeller* Дополнительно
  189. Ссылки • Видео с демо https://www.youtube.com/watch?v=-iyOjbRMvg8&list=PLpVeA1tdgfCAS9Hgp dnk2GKvRN24KSF5x&index=2 • https://github.com/lavcraft/conference-test-with-spring-boot-test