Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Curse Of Spring Boot Test [version for QA]

Curse Of Spring Boot Test [version for QA]

Developers and tests. There are different opinions, and Kirill himself thinks that developers should write tests. In this session, he'll talk about tests that help developers write code and verify already written code on an app level.

Being a Java developer, Kirill will do it through old and familiar JUnit and spring-boot-test frameworks, paying extra attention to special aspects of the work of spring-boot-test when testing on the boundary of different app components (@RestController, @Component, @Service, Repository...). He'll try to dispel some magic which frameworks do in your stead to bring some more awareness to your test writing.

40951719c6ca509831d5c38b764661c9?s=128

Kirill Tolkachev

December 07, 2018
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 ◦ @MvcTest • Кэширование spring контекстов • Шкала тестов
  5. Немного теории

  6. Шкала Тестов Unit Component Test Microservice Test System Test ➯

    ➯ ➯ ➯
  7. Шкала Тестов Unit Component Test Microservice Test System Test ➯

    ➯ ➯ ➯
  8. Unit/Component тесты. Для чего?

  9. Unit/Component тесты. Для чего?

  10. Unit/Component тесты. Для чего? Ваши тесты Тут

  11. Unit/Component тесты. Для чего? Тесты уменьшают неопределённость

  12. Есть два типа тестов Простой Сложный

  13. Есть два типа тестов Какой сам выберешь Простой Сложный

  14. Есть два типа тестов Какой сам выберешь, а какой разработчику

    оставишь? Простой Сложный
  15. Когда пишут тесты?

  16. Когда пишут тесты? 1. Требование заказчика

  17. Когда пишут тесты? 1. Требование заказчика 2. Культура

  18. Когда пишут тесты? 1. Требование заказчика 2. Культура Перед кодом

  19. Когда пишут тесты? 1. Требование заказчика 2. Культура Перед кодом

    Вместе кодом
  20. Когда пишут тесты? 1. Требование заказчика 2. Культура Перед кодом

    Вместе кодом После кода
  21. Unit Component Test Про какие тесты будем говорить? ➯ ➯

    Перед кодом Вместе кодом После кода
  22. Unit Component Test ➯ ➯ Про какие тесты будем говорить?

    Перед кодом Вместе кодом После кода
  23. Начнём

  24. router joker jbaruch-assistant Default Answers Database Joker Resolver $tokens.joker JBaruch

    Resolver ... $tokens.jbaruch Дано
  25. Дано Чат поддержки тестировщиков assistant joker-assistant jbaruch-assistant Queue мы web

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

  27. Эксперты

  28. Demo

  29. Дано Чат поддержки тестировщиков assistant joker-assistant jbaruch-assistant Queue мы web

    rest rest rest Default Answers Database
  30. None
  31. None
  32. None
  33. А давайте тестировать

  34. А давайте тестировать router joker jbaruch-assistant Default Answers Database Joker

    Resolver $tokens.joker JBaruch Resolver ... $tokens.jbaruch
  35. Demo JokerWordsFrequencyResolverTest

  36. А давайте тестировать. Тест #1 1. Пишем JokerWordsFrequencyResolverTest.

  37. Demo

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

    private String answers; public JokerWordsFrequencyResolver(WordsComposer wordsComposer) { super(wordsComposer); } @Override public QuestionType getQuestionType() { return JOKER; } }
  39. Тест №1.5 public class JokerWordsFrequencyResolverTest { @Test public void name()

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

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

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

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

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

  45. Not Passed

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

  47. WordsComposer:48 @Value("${garbage}") void setGarbage(String[] garbage) { Запчасти Spring

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

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

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

  51. Unit Component Test ➯ ➯ Про какие тесты будем говорить?

    Перед кодом Вместе кодом После кода
  52. Unit Component Test ➯ ➯ Про какие тесты будем говорить?

    Перед кодом Вместе кодом После кода
  53. Unit Component Test ➯ ➯ Про какие тесты будем говорить?

    Перед кодом Вместе кодом После кода @Value("${garbage}") void setGarbage(String[] garbage) { Инициализируется Spring`ом
  54. Ещё немного теории

  55. IoC, DI, Spring и друзья

  56. IoC, DI, Spring и друзья

  57. IoC, DI, Spring и друзья

  58. IoC, DI, Spring и друзья Кино про супергероев

  59. IoC, DI, Spring и друзья Кино про супергероев

  60. IoC, DI, Spring и друзья Кино про супергероев

  61. IoC, DI, Spring и друзья Кино про супергероев IoC

  62. ФабрикаГероев IoC, DI, Spring и друзья Кино про супергероев IoC

  63. IoC, DI, Spring и друзья IoC для инверсии поведения

  64. IoC, DI, Spring и друзья public class СуперЗлодейТест { @Before

    public void setUp() throws Exception { ... } } Тоже инверсия контроля IoC для инверсии поведения
  65. IoC, DI, Spring и друзья public class СъемочнаяПлощадка { public

    static void main(String[] args) { Киношка съёмка = new Киношка().снимать(); съёмка.герой.бить(); съёмка.злодей.страдать(); съёмка.злодей.бить(); съёмка.герой.страдать(); съёмка.герой.страдать(); } }
  66. IoC, DI, Spring и друзья public class СъемочнаяПлощадка { public

    static void main(String[] args) { Киношка съёмка = new Киношка().снимать(); съёмка.герой.бить(); съёмка.злодей.страдать(); съёмка.злодей.бить(); съёмка.герой.страдать(); съёмка.герой.страдать(); } } NullPointerException
  67. IoC, DI, Spring и друзья public class Киношка { СуперГерой

    герой; СуперЗлодей злодей; public Киношка снимать() { return new Киношка(); } }
  68. IoC, DI, Spring и друзья public class СуперГерой implements Герой

    { private СуперЗлодей вражина; @Override public void бить() { вражина.бить(); } } public class СуперЗлодей implements Герой { private СуперГерой вражина; @Override public void бить() { вражина.страдать(); } } Кто проставляет?
  69. public class ФабрикаГероев { public Object родить() { if (new

    Random().nextBoolean()) { return new СуперГерой(); } return new СуперЗлодей(); } } IoC, DI, Spring и друзья
  70. public class ФабрикаГероев { public Object родить() { if (new

    Random().nextBoolean()) { return new СуперГерой(); } return new СуперЗлодей(); } } IoC, DI, Spring и друзья
  71. IoC, DI, Spring и друзья Spring

  72. IoC, DI, Spring и друзья Spring • @Autowired • @Component/@Service

    • @Configuration
  73. IoC, DI, Spring и друзья @Component public class Киношка {

    @Autowired СуперГерой герой; @Autowired СуперЗлодей злодей; public static Киношка снимать() { return new Киношка(); } } Spring • @Autowired • @Component/@Service • @Configuration
  74. IoC, DI, Spring и друзья @Component public class Киношка {

    @Autowired СуперГерой герой; @Autowired СуперЗлодей злодей; public static Киношка снимать() { return new Киношка(); } } Spring • @Autowired • @Component/@Service • @Configuration
  75. IoC, DI, Spring и друзья @Component public class СуперГерой implements

    Герой { @Autowired СуперЗлодей вражина; @Override public void бить() { вражина.бить(); } } Spring • @Autowired • @Component/@Service • @Configuration
  76. Demo

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

    class JokerWordsFrequencyResolverTest { @Autowired JokerWordsFrequencyResolver jokerWordsFrequencyResolver; @Test public void name() throws Exception { jokerWordsFrequencyResolver.setAnswers("objects"); int match = jokerWordsFrequencyResolver.match( Question. builder().body("objects ...").build()); assertThat(match, equalTo(1)); } }
  78. Тест №1.5 @Configuration public class JokerWordsFrequencyResolverTestConfig { @Bean public JokerWordsFrequencyResolver

    jokerWordsFrequencyResolver( WordsComposer wordsComposer) { return new JokerWordsFrequencyResolver(wordsComposer); } }
  79. Тест №1.5 @Configuration public class JokerWordsFrequencyResolverTestConfig { @Bean public JokerWordsFrequencyResolver

    jokerWordsFrequencyResolver( WordsComposer wordsComposer) { return new JokerWordsFrequencyResolver(wordsComposer); } }
  80. Тест №1.5 @Configuration @ComponentScan("com.conference.spring.test.common") public class JokerWordsFrequencyResolverTestConfig { @Bean public

    JokerWordsFrequencyResolver jokerWordsFrequencyResolver( WordsComposer wordsComposer) { return new JokerWordsFrequencyResolver(wordsComposer); } }
  81. Тест №1.5 @Configuration @ComponentScan("com.conference.spring.test.common") public class JokerWordsFrequencyResolverTestConfig { @Bean public

    JokerWordsFrequencyResolver jokerWordsFrequencyResolver( WordsComposer wordsComposer) { return new JokerWordsFrequencyResolver(wordsComposer); } }
  82. Тест №1.5 @RunWith(SpringRunner. class) @ContextConfiguration (classes = JokerWordsFrequencyResolverTestConfig. class) public

    class JokerWordsFrequencyResolverTest { @Autowired JokerWordsFrequencyResolver jokerWordsFrequencyResolver; @Test public void name() throws Exception { jokerWordsFrequencyResolver.setAnswers("objects"); int match = jokerWordsFrequencyResolver.match( Question. builder().body("objects ...").build()); assertThat(match, equalTo(1)); } }
  83. Passed

  84. Ещё немного теории

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

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

    Juergen Hoeller * ... */ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner
  87. /** * {@code SpringExtension} integrates the <em>Spring TestContext … </em>

    * into JUnit 5's <em>Jupiter</em> programming model. ... * @author Sam Brannen * @since 5.0 */ public class SpringExtension implements BeforeAllCallback, … { SpringExtension — Junit5
  88. SpringExtension — Junit5 @SpringJUnitConfig @SpringJUnitWebConfig

  89. 1. Пишем TextBasedQuestionTypeResolverTest А давайте тестировать. Тест #2

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

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

    TextBasedQuestionTypeResolver на примере Барух vs Джокер кейса А давайте тестировать. Тест #2
  92. Demo TextBasedQuestionTypeResolverTest

  93. @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("псих")); assertThat(groovy, equalTo(JBARUCH)); assertThat(objects, equalTo(JOKER)); } } Тест #2
  94. @Configuration public class TextBasedQuestionTypeResolverTestConfig { @Bean public TextBasedQuestionTypeResolver textBasedQuestionTypeResolver( List<WordsFrequencyResolver>

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

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

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

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

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

  100. Not Passed

  101. class JokerWordsFrequencyResolver @Value("${tokens.joker}") private String answers; class JBaruchWordsFrequencyResolver @Value("${tokens.jbaruch}") private

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

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

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

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

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

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

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

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

  110. Demo

  111. @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(JOKER)); } } Тест #2
  112. @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(JOKER)); } } Тест #2
  113. Not Passed

  114. @RunWith(SpringRunner. class) @SpringBootTest @ActiveProfiles ("joker_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(JOKER)); } } Тест #2 Для подгрузки application-joker_vs_jbaruch.yml
  115. Passed

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

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

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

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

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

  121. Only once … only once … only once

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

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

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

  125. Demo

  126. @SpringBootTest @ContextHierarchy({ @ContextConfiguration(classes = TextBasedQuestionTypeResolverTestConfig.class), @ContextConfiguration(classes = CommonConfig.class) }) @ActiveProfiles("joker_vs_jbaruch")

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

  128. Only once … only once … only once … only

    once … only once Четыре раза...
  129. None
  130. @Configuration @Import(CommonConfig.class) public class JokerWordsFrequencyResolverTestConfig { @Configuration @Import(CommonConfig.class) public class

    TextBasedQuestionTypeResolverTestConfig {
  131. Убираем @Import(CommonConfig.class)

  132. Not Passed

  133. None
  134. Не найден spring bean WordsComposer

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

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

    = TextBasedQuestionTypeResolverTestConfig.class) }) @ActiveProfiles("joker_vs_jbaruch") @RunWith(SpringRunner.class) public class TextBasedQuestionTypeResolverTest { ... CommonConfig теперь первый
  137. Passed

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  155. Demo AnswerCacheServiceJPABackend

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

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

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

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

    @TestConfiguration 4. @DataJpaTest 5. @MockMvcTest
  160. Как тестируем @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 … } }
  161. Как тестируем @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 … } }
  162. Как тестируем @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 … } }
  163. Как тестируем – Конфигурация @Configuration public class AnswerCacheServiceJPABackendTestConfig { @Bean

    public AnswerCacheServiceJPABackend answerCacheServiceJpaBackend( QuestionRepository qR, AnswersRepository aR) { return new AnswerCacheServiceJPABackend(qR, aR); } }
  164. Как тестируем – сам тест @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
  165. Passed

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

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

  168. Not Passed

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    static void main(String[] args) { SpringApplication.run(DeveloperAssistantApplication.class, args); } }
  201. Как чинить @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 {
  202. Как чинить @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 {
  203. Как чинить /** * @author Phillip Webb * @since 1.4.0

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

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

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

  207. Component Tests

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  224. Есть два типа тестов Какой сам выберешь, а какой разработчику

    оставишь? Простой Сложный
  225. Простой Сложный Понятный Есть два типа тестов Какой сам выберешь,

    а какой разработчику оставишь?
  226. 1. Не боимся залезать в кишки приложения 2. Spring Boot

    богат на инструменты для тестирования 3. Но вносит свои ограничения – структура тестов Выводы
  227. Шкала Тестов Unit Component Test Microservice Test System Test ➯

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

    new Spring Spring Boot
  229. QA 229

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

    @TestConfiguration 2. @DataJpaTest > @SpringBootTest 3. @DataJpaTest и @WebMvcTest должны быть в отдельных пакетах Если есть сомнения – смотри автора! Juergen Hoeller* Дополнительно
  231. Замечания 1. Spring для Unit тестирования может быть быстрым

  232. 1. Spring для Unit тестирования может быть быстрым 2. Кэш

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

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

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

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

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

    контекстов – хрупкая штука 3. Для тестов – только @TestConfiguration 4. Изолировать группы тестов с помощью a. выделения в пакеты b. @SpringBootConfiguration 5. SpringBootTest надо в основном использовать для микросервис тестов 6. Если есть DirtiesContext – стоит задуматься :) Дополнительно
  238. 1. Demo Source with Spring Boot 2.1 and Gradle —

    https://github.com/lavcraft/spring-boot-curse 2. Old Demo Source with Spring Boot 1.5 and Maven — https://github.com/lavcraft/conference-test-with-spring-boot-test 3. Spring Test Reference Guide 4. Spring Boot Test Reference Guide 5. Spring 1.4 Test Improvements 6. Custom Test Slice with Spring Boot Ссылки