113 © VictorRentea.ro
a training by
Integration Testing
with Spring
victorrentea@gmail.com VictorRentea.ro @victorrentea
All code is at https://github.com/victorrentea/unit-testing.git on branch JAX_Mainz
Victor Rentea
VictorRentea.ro
Blog, Best Talks, Video Courses, ...
Independent Trainer
for companies and individuals
Founder of
Bucharest Software Craftsmanship Community
Java Champion
❤️ Simple Design, Refactoring, Unit Testing ❤️
Technical Training
400 days
(100+ online)
2000 devs
8 years
More details on VictorRentea.ro
50 companies
Reach out
to me:
Hibernate
Spring Functional Prog
Java Performance
Reactive Prog
Design Patterns
Pragmatic DDD
Clean Code
Refactoring
Unit Testing
TDD
any
lang
@victorrentea
Intense
116 © VictorRentea.ro
a training by
files
databases
queues
web services
Integration Tests
Failed Test Tolerance
If your test talks to and it fails,
is it a bug?
Probably not!😏
I hope it's them! 🙏
Tests failed
occasionally
some months after...
117 © VictorRentea.ro
a training by
Unit Tests
Integration
failure ➔ maybe a bug
failure ➔ always a bug
119 © VictorRentea.ro
a training by
Zero Tolerance for Failed Tests
120 © VictorRentea.ro
a training by
🚨 USB device connected to Jenkins
121 © VictorRentea.ro
a training by
Let's Test a Search
Search Product
Name:
Supplier: IKEA
Search
...
DEV DB
eg Oracle
H2 in-memory
H2 standalone
DB in Docker
eg Oracle
122 © VictorRentea.ro
a training by
Testing...
▪with a Database
▪with external REST calls
▪your REST API
Agenda
123 © VictorRentea.ro
a training by
CODE
128 © VictorRentea.ro
a training by
Used to debug test
dependencies
Cleanup After Test
+10-60 wasted seconds
Spring
Don't push it to Jenkins!
@DirtiesContext
or
Manually
Before
132 © VictorRentea.ro
a training by
Isolated Repository Tests
@SpringBootTest
@RunWith(SpringRunner.class) // if on JUnit 4
public class TrainingServiceTest {
@Autowired
private TrainingRepo repo;
@Before/@BeforeEach
public void checkEmptyDatabase() {
assertThat(repo.findAll()).isEmpty();
}
@Test
public void getAll() {
...
repo.save(...);
repo.search(...);
....
}
@DirtiesContext(methodMode = AFTER_METHOD)
Recreates the embedded DB
Use For: debugging (time waste)
repo.deleteAll(); ... // others.deleteAll()… // in FK order
@Transactional Run each test in a Transaction,
rolled back after each test.
Best For: Relational DBs
In Large Apps:
Check that the state is clean
Manual Cleanup
Use For: non-transacted resources (eg files, nosql)
133 © VictorRentea.ro
a training by
If every test class is @Transactional
Can I still have problems?
YES
Intermediate COMMITs
aka nested transaction
@Transactional(REQUIRES_NEW)
134 © VictorRentea.ro
a training by
Inserting Test Data
repo.save()
in @Test
@BeforeEach
@BeforeEach in superclass (inheritance)
@RegisterExtension (composition with JUnit5)
Insert via @Profile
A bean persists data at startup
src/test/resources/data.sql
if you use schema.sql
Incremental Scripts
@Sql
135 © VictorRentea.ro
a training by
Composing Fixtures with JUnit5
public class CommonData
implements BeforeEachCallback {
private User user;
public User getUser() {
return user;
}
@Override
public void beforeEach(ExtensionContext context) {
EntityManager em = SpringExtension.getApplicationContext(context)
.getBean(EntityManager.class);
user = new User().setUsername("test");
em.persist(user);
}
}
@RegisterExtension
public CommonData commonData = new CommonData();
@Test
public void noCriteria() {
User user = commonData.getUser();
repo.save(new Post().setCreatedBy(user));
assertThat(repo.findAll()).hasSize(1);
}
in your test class:
136 © VictorRentea.ro
a training by
Running SQL Scripts
@Sql("data.sql")
@Sql(script="classpath:cleanup.sql",
phase=BEFORE_TEST_METHOD)
@interface Cleanup {}
before or after @Test
runs in the same transaction
cleanup or initial data insert
137 © VictorRentea.ro
a training by
What DB to use in Tests
in-memory H2
(create schema via Hibernate)
Same DB with TestContainers
(create schema via scripts)
Personal Schema on Physical DB
(eg. REGLISS_VICTOR)
Shared Test Schema on Physical DB Legacy 500+ tables schemas
For PL/SQL, custom DB features
For JPA + native standard SQL
140 © VictorRentea.ro
a training by
@txn
Feature: Records Search
Background:
Given Supplier "X" exists
Scenario Outline: Product Search
Given One product exists
And That product has name "<productName>"
When The search criteria name is "<searchName>"
Then That product is returned by search: "<returned>"
Examples:
| productName | searchName | returned |
| a | X | false |
| a | a | true |
Gherkin Language
wasted effort
if business never sees it
*
≈ @Before
= Separate transaction / test
.feature
141 © VictorRentea.ro
a training by
142 © VictorRentea.ro
a training by
@Bean
@Mock
Create a Mock
(Mockito)
Define a bean
(Spring)
143 © VictorRentea.ro
a training by
@Bean
@Mock
Replaces that bean with a Mockito Mock
(Auto-reset() after each @Test)
145 © VictorRentea.ro
a training by
Application Context is Reused
Starting Spring is slow.
Pro Tip:
Maximize Reuse
by test classes with the same:
@ActiveProfiles
@MockBean set
custom properties
locations= .xml
config classes= .class
more
146 © VictorRentea.ro
a training by
= +30 sec test run time
Optimizing Spring Tests Speed
1. Tune JVM: -ea -noverify -mx2048m -XX:TieredStopAtLevel=1
6. Limit Auto-Configuration (aka slice tests)
2. Lazy-Load only Tested Beans: spring.main.lazy-initialization=true
3. Disable Web: @SpringBootTest(webEnvironment = NONE)
https://stackoverflow.com/a/49663075 +
https://spring.io/blog/2016/08/30/custom-test-slice-with-spring-boot-1-4
5. Run in parallel
4. Reuse between Test Classes: Sprint Context (#banners) + Dockers + WireMocks
Debugging Test Context reuse: logging.level.org.springframework.test.context.cache=DEBUG
(Least invasive first)
147 © VictorRentea.ro
a training by
Reusing Test Context
@SpringBootTest
@Transactional
@ActiveProfiles({"db-mem", "test"})
public abstract class SpringTestBase {
@MockBean
protected FileRepo fileRepoMock;
... all @MockBeans ever needed
}
@ActiveProfiles("db-mem")
public class FeedProcessorWithMockTest
extends SpringTestBase {
@MockBean
when(fileRepoMock).then...
}
nothing requiring
dedicated context
160 © VictorRentea.ro
a training by
My Application
SafetyClient
@MockBean
In-memory DB
(H2)
ProductRepo
Remote Sever
Real DB
@Transactional
WireMock
Local Docker
Real DB
.feature
What We've Covered
@DirtiesContext
ProductService
@Transactional
162 © VictorRentea.ro
a training by
Unit Tests
Integration
Typically Fast
eg. 100 tests/sec
www . A - color . com
... or Slow
Spring, in-mem DB, Docker
failure ➔ maybe a bug
failure ➔ always a bug
Goal
Run all Honest test after commit
Optimize Test Speed
Company Training:
victorrentea@gmail.com
Training for You:
victorrentea.teachable.com
Thank You!
@victorrentea
Blog, Talks, Curricula:
victorrentea.ro

Integration testing with spring @JAX Mainz

  • 1.
    113 © VictorRentea.ro atraining by Integration Testing with Spring victorrentea@gmail.com VictorRentea.ro @victorrentea All code is at https://github.com/victorrentea/unit-testing.git on branch JAX_Mainz
  • 2.
    Victor Rentea VictorRentea.ro Blog, BestTalks, Video Courses, ... Independent Trainer for companies and individuals Founder of Bucharest Software Craftsmanship Community Java Champion ❤️ Simple Design, Refactoring, Unit Testing ❤️
  • 3.
    Technical Training 400 days (100+online) 2000 devs 8 years More details on VictorRentea.ro 50 companies Reach out to me: Hibernate Spring Functional Prog Java Performance Reactive Prog Design Patterns Pragmatic DDD Clean Code Refactoring Unit Testing TDD any lang @victorrentea Intense
  • 4.
    116 © VictorRentea.ro atraining by files databases queues web services Integration Tests Failed Test Tolerance If your test talks to and it fails, is it a bug? Probably not!😏 I hope it's them! 🙏 Tests failed occasionally some months after...
  • 5.
    117 © VictorRentea.ro atraining by Unit Tests Integration failure ➔ maybe a bug failure ➔ always a bug
  • 6.
    119 © VictorRentea.ro atraining by Zero Tolerance for Failed Tests
  • 7.
    120 © VictorRentea.ro atraining by 🚨 USB device connected to Jenkins
  • 8.
    121 © VictorRentea.ro atraining by Let's Test a Search Search Product Name: Supplier: IKEA Search ... DEV DB eg Oracle H2 in-memory H2 standalone DB in Docker eg Oracle
  • 9.
    122 © VictorRentea.ro atraining by Testing... ▪with a Database ▪with external REST calls ▪your REST API Agenda
  • 10.
    123 © VictorRentea.ro atraining by CODE
  • 11.
    128 © VictorRentea.ro atraining by Used to debug test dependencies Cleanup After Test +10-60 wasted seconds Spring Don't push it to Jenkins! @DirtiesContext or Manually Before
  • 12.
    132 © VictorRentea.ro atraining by Isolated Repository Tests @SpringBootTest @RunWith(SpringRunner.class) // if on JUnit 4 public class TrainingServiceTest { @Autowired private TrainingRepo repo; @Before/@BeforeEach public void checkEmptyDatabase() { assertThat(repo.findAll()).isEmpty(); } @Test public void getAll() { ... repo.save(...); repo.search(...); .... } @DirtiesContext(methodMode = AFTER_METHOD) Recreates the embedded DB Use For: debugging (time waste) repo.deleteAll(); ... // others.deleteAll()… // in FK order @Transactional Run each test in a Transaction, rolled back after each test. Best For: Relational DBs In Large Apps: Check that the state is clean Manual Cleanup Use For: non-transacted resources (eg files, nosql)
  • 13.
    133 © VictorRentea.ro atraining by If every test class is @Transactional Can I still have problems? YES Intermediate COMMITs aka nested transaction @Transactional(REQUIRES_NEW)
  • 14.
    134 © VictorRentea.ro atraining by Inserting Test Data repo.save() in @Test @BeforeEach @BeforeEach in superclass (inheritance) @RegisterExtension (composition with JUnit5) Insert via @Profile A bean persists data at startup src/test/resources/data.sql if you use schema.sql Incremental Scripts @Sql
  • 15.
    135 © VictorRentea.ro atraining by Composing Fixtures with JUnit5 public class CommonData implements BeforeEachCallback { private User user; public User getUser() { return user; } @Override public void beforeEach(ExtensionContext context) { EntityManager em = SpringExtension.getApplicationContext(context) .getBean(EntityManager.class); user = new User().setUsername("test"); em.persist(user); } } @RegisterExtension public CommonData commonData = new CommonData(); @Test public void noCriteria() { User user = commonData.getUser(); repo.save(new Post().setCreatedBy(user)); assertThat(repo.findAll()).hasSize(1); } in your test class:
  • 16.
    136 © VictorRentea.ro atraining by Running SQL Scripts @Sql("data.sql") @Sql(script="classpath:cleanup.sql", phase=BEFORE_TEST_METHOD) @interface Cleanup {} before or after @Test runs in the same transaction cleanup or initial data insert
  • 17.
    137 © VictorRentea.ro atraining by What DB to use in Tests in-memory H2 (create schema via Hibernate) Same DB with TestContainers (create schema via scripts) Personal Schema on Physical DB (eg. REGLISS_VICTOR) Shared Test Schema on Physical DB Legacy 500+ tables schemas For PL/SQL, custom DB features For JPA + native standard SQL
  • 18.
    140 © VictorRentea.ro atraining by @txn Feature: Records Search Background: Given Supplier "X" exists Scenario Outline: Product Search Given One product exists And That product has name "<productName>" When The search criteria name is "<searchName>" Then That product is returned by search: "<returned>" Examples: | productName | searchName | returned | | a | X | false | | a | a | true | Gherkin Language wasted effort if business never sees it * ≈ @Before = Separate transaction / test .feature
  • 19.
  • 20.
    142 © VictorRentea.ro atraining by @Bean @Mock Create a Mock (Mockito) Define a bean (Spring)
  • 21.
    143 © VictorRentea.ro atraining by @Bean @Mock Replaces that bean with a Mockito Mock (Auto-reset() after each @Test)
  • 22.
    145 © VictorRentea.ro atraining by Application Context is Reused Starting Spring is slow. Pro Tip: Maximize Reuse by test classes with the same: @ActiveProfiles @MockBean set custom properties locations= .xml config classes= .class more
  • 23.
    146 © VictorRentea.ro atraining by = +30 sec test run time Optimizing Spring Tests Speed 1. Tune JVM: -ea -noverify -mx2048m -XX:TieredStopAtLevel=1 6. Limit Auto-Configuration (aka slice tests) 2. Lazy-Load only Tested Beans: spring.main.lazy-initialization=true 3. Disable Web: @SpringBootTest(webEnvironment = NONE) https://stackoverflow.com/a/49663075 + https://spring.io/blog/2016/08/30/custom-test-slice-with-spring-boot-1-4 5. Run in parallel 4. Reuse between Test Classes: Sprint Context (#banners) + Dockers + WireMocks Debugging Test Context reuse: logging.level.org.springframework.test.context.cache=DEBUG (Least invasive first)
  • 24.
    147 © VictorRentea.ro atraining by Reusing Test Context @SpringBootTest @Transactional @ActiveProfiles({"db-mem", "test"}) public abstract class SpringTestBase { @MockBean protected FileRepo fileRepoMock; ... all @MockBeans ever needed } @ActiveProfiles("db-mem") public class FeedProcessorWithMockTest extends SpringTestBase { @MockBean when(fileRepoMock).then... } nothing requiring dedicated context
  • 25.
    160 © VictorRentea.ro atraining by My Application SafetyClient @MockBean In-memory DB (H2) ProductRepo Remote Sever Real DB @Transactional WireMock Local Docker Real DB .feature What We've Covered @DirtiesContext ProductService @Transactional
  • 26.
    162 © VictorRentea.ro atraining by Unit Tests Integration Typically Fast eg. 100 tests/sec www . A - color . com ... or Slow Spring, in-mem DB, Docker failure ➔ maybe a bug failure ➔ always a bug Goal Run all Honest test after commit Optimize Test Speed
  • 27.
    Company Training: victorrentea@gmail.com Training forYou: victorrentea.teachable.com Thank You! @victorrentea Blog, Talks, Curricula: victorrentea.ro