SlideShare a Scribd company logo
Testing Spring
Applications
Riyadh Java Meetup
Mohammad Hewedy
github.com/mhewedy
1
1
Agenda
● Unit Test: What and Why?
● Test Driven Development
● Test Patterns
● Test Doubles
● Testing Spring Applications
○ Unit Testing
○ Integration Testing and the TestContext Framework
○ Slice Testing in Spring boot
● Demo
2
What?
3
4
A unit test is a piece of code written by a developer that exercises a very small,
specific area of functionality of the code being tested.
Unit tests are performed to prove that a piece of code does what the
developer thinks it should do.
Pragmatic Unit Testing in Java with JUnit
5
Why?
6
● It will make your designs better.
● Reduce the amount of time you spend debugging.
● Give confidence in the code.
● Make sure code do what you want.
● Do what you want all of the Time.
● Make sure you can depend on code.
● Document your intent.
● Shows how to use your code.
● Avoid any collateral damage
7
“Whac-a-Mole” effect
https://www.bakadesuyo.com/2012/10/whac-a-mole-teach-ability-focus/
8
a.k.a. “Samir Ghanem” effect
9
Why not?
10
● It takes too much time to write the tests?
● It takes too long to run the tests?
● It’s not my job to test my code?
● I’m being paid to write code, not to write tests?
● I feel guilty about putting testers and QA staff out of work?
11
Test Pyramid
Unit Tests
Integration Tests
(Service Tests)
E2E Tests
(UI Tests)
Fast
Slow
12
Characteristics of Good Tests
● Automatic
● Thorough
● Repeatable
● Independent
● Professional
● Speed
13
Always, “Spring The Trap”
Jim Baird - Adventurer
14
How?
15
class MathTest(TestCase):
def test_add(self):
math = Math()
result = math.add(10, 20)
assert_equals(30, result)
def test_multiply(self):
math = Math()
result = math.multiply(10, 20)
assert_equals(200, result)
class Math:
def add(self, x, y):
return x + y
def multiply(self, x, y):
return x * y
16
Test Driven Development
Red/Green/Refactor
Write
a test
Make it
compile
Run to
see it
fails
Make it
run
(see next)
Remove
Duplicate
(Refactor)
17
How to “Make it run” (Geen Bar Pattern)
1. Fake it
2. Triangulate
3. Obvious implementation
18
Green Bar Pattern in Action
19
Test Patterns
● Arrange, Act, Assert.
● Arrange, Assert-not, Act, Assert.
● Given, When, Then.
● Setup, Exercise, Verify, Teardown.
20
// spock framework
def "test listToString"() {
given:
def list = Arrays.asList("HELLO", "WORLD")
when:
def listToString = Util.listToString(list)
then:
listToString == "HELLOnWORLDn"
}
https://github.com/mhewedy/spwrap/blob/master/src/test/groovy/spwrap/UtilTest.groovy
21
@Unroll
def "#testDB : using implicit ResultSet Mapper to map result set"() {
given:
setup(testDB)
when:
customerDao.createCustomer0(“Abdullah”, “Mohammad”)
def list = customerDao.listCustomers(list)
then:
1 == list.size()
with(list.get(0)){
“Abdullah” == firstName()
“Mohammad” == lastName()
}
cleanup:
cleanup(testDB)
where:
testDB << [HSQL, MYSQL, SQLServer, ORACLE]
}
22
https://github.com/mhewedy/spwrap/blob/master/src/test/groovy/spwrap/DAOIntTest.groovy
Test Doubles
23
interface Mailer {
Boolean send(String to, String subject);
}
// billing is SUT
billing.bill(cusomer);
// mailer is collaborator
mailer.send(cusomer.getEmail(), postBillHtmlTemplate);
Collaborator object vs SUT
24
Dummy
class DummyMailer implements Mailer {
Boolean send(String to, String subject) {
return null;
}
}
25
Stub
class SuccessfullySendingMailerStub implements Mailer {
Boolean send(String to, String subject) {
return true;
}
}
26
Spy
class SuccessfullySendingMailerSpy implements Mailer {
public int callCount;
public String recordedTo;
public String recordedSubject;
Boolean send(String to, String subject) {
this.callCount += 1;
this.recordedTo = to;
this.recordedSubject = subject;
return true;
}
}
27
Mock
class SuccessfullySendingMailerMock implements Mailer {
private int callCount;
private String recordedTo;
private String recordedSubject;
Boolean send(String to, String subject) {
this.callCount += 1;
this.recordedTo = to;
this.recordedSubject = subject;
return true;
}
boolean verify(String expectedTo, String expectedSubject) {
return callCount == 1 &&
this.recordedTo.equals(expectedTo) &&
this.recordedSubject.equals(expectedSubject);
}
} 28
Fake
class MailerFake implements Mailer {
Boolean send(String to, String subject) {
return SimulatorDataSource.getMailer()
.containsRecord(to, subject);
}
}
29
To Mock or not to Mock
● The Mockist vs The Classicist
● The Mini-integration Tests
● The ObjectMother
● SUT and Collaborator objects
● Ways to Test the service layer:
○ Mock the Repository layer.
○ Use in-memory database.
○ Use a real test database.
30
Testing Spring
Applications
31
Unit Testing
32
Mocks provided by spring
● Environment:
○ MockEnvironment and MockPropertySource
● JNDI
○ SimpleNamingContextBuilder
○ e.g. To bind an embedded datasource to a jndi name
● Servlet api
○ MockHttpServletRequest, MockHttpServletResponse, MockHttpSession, etc.
Exists in package: org.springframework.mock
33
Support Classes
● ReflectionTestUtils
● AopTestUtils
● ModelAndViewAssert
● JdbcTestUtils
34
Integration Testing
35
Integration Testing
● Slower than unit tests.
● Perform some integration testing without requiring application
deployment.
● Exists in package: org.springframework.test
36
Goals of Spring Integration Testing support
● Context Management and Caching
● Dependency Injection of Test Fixtures
● Transaction Management
● Easily executing SQL Scripts
37
Important Annotations
● @ContextConfiguration
● @ActiveProfiles
● @TestPropertySource
● @DirtiesContext
● @Commit & @Rollback
● @BeforeTransaction & @AfterTransaction
● @Sql & @SqlConfig
● @Autowired, @PersistenceContext, @Qualifier, etc.
● @IfProfileValue, @Timed, @Repeat (junit4)
● @SpringJUnitConfig, @EnabledIf, @DisabledIf (junit5)
38
@TestExecutionListeners
● Important component of TestContext framework
● Accepts implementations of TestExecutionListener interface
● Used to implement many of the features
● Example implementations include:
○ ServletTestExecutionListener
○ DependencyInjectionTestExecutionListener
○ DirtiesContextTestExecutionListener
○ TransactionalTestExecutionListener
○ SqlScriptsTestExecutionListener
39
Meta Annotations
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@DisabledIf( expression =
"#{systemProperties['os.name'].toLowerCase().contains('mac')}",
reason = "Disabled on Mac OS")
public @interface DisabledOnMac {}
40
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ContextConfiguration(classes = {GeoConfig.class})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevGeoTestConfig {}
41
TestContext
Framework
42
JUnit 5 how is different from JUnit4?
● JUnit 4 uses Runner and Rule (TestRule & MethodRule)
○ @RunWith & @Rule
○ Examples:
■ MockitoJUnitRunner
■ SpringRunner
■ MockitoRule
■ WireMockRule
● JUnit 5 uses Extension
○ @ExtendWith
○ Composable
○ Examples:
■ SpringExtension
■ MockitoExtension
43
Spring TestContext is Test-Framework Agnostic
TestContextManager is
the entry point for Test
frameworks (JUnit 4,
JUnit5, TestNG) to the
TestContext framework.
44
Overview
● Support the Integration Testing
goals of spring
● Exists in package:
org.springframework.test.
context
● Core Classes
○ TestContextManager
○ TestContext
○ TestExecutionListener
○ SmartContextLoader
○ TestContextBootstrapper
45
TestContextManager
46
public void beforeTestMethod(Object testInstance, Method testMethod) throws Exception {
String callbackName = "beforeTestMethod";
getTestContext().updateState(testInstance, testMethod, null);
for (TestExecutionListener testExecutionListener : getTestExecutionListeners())
{
try {
testExecutionListener.beforeTestMethod(getTestContext());
}catch (Throwable ex) {
handleBeforeException(ex, callbackName,
testExecutionListener,
testInstance, testMethod);
}
}
} 47
TestContextBootstrapper
● Used to provide the TestContextManager by TestContext and the list
of TestExecutionListener
● You can customize using @BootstrapWith
● @SpringBootTest is itself annotated with
@BootstrapWith(SpringBootTestContextBootstrapper.class)
48
TestExecutionListener
● By default the following listeners are registered:
○ ServletTestExecutionListener
○ DirtiesContextBeforeModesTestExecutionListener
○ DependencyInjectionTestExecutionListener
○ DirtiesContextTestExecutionListener
○ TransactionalTestExecutionListener
○ SqlScriptsTestExecutionListener
● You can customize using @TestExecutionListeners or using
SpringFactoriesLoader mechanism.
● beware not to override default configured listeners
49
Features of
TestContext
50
Context Management
● Provide Context Loading from XML, Groovy and Annotated Classes
Example:
@ContextConfiguration(locations={"/app-config.xml", "/test-
config.xml"})
● Provide Context Initialization using classes implementation
ApplicationContextInitializer
● Provide Caching for the loaded Context between tests
● Can evict the cache using @DirtiesContext
51
@RunWith(SpringRunner.class)
@ContextConfiguration
public class OrderServiceTest {
@Configuration
static class Config {
@Bean
public OrderService orderService() {
OrderService orderService = new OrderServiceImpl();
return orderService;
}
}
@Autowired
private OrderService orderService;
@Test
public void testOrderService() {
// test the orderService
}
} 52
Dependency Injection
● Implemented using DependencyInjectionTestExecutionListener
● Can use setter injection or field injection
● JUnit 5 you can use constructor injection
● Uses @Autowired or @Inject
53
Transaction Management
● Implemented using TransactionalTestExecutionListener.
● PlatformTransactionManager should exist in ApplicationContext
(auto-configured in spring boot).
● Must declare Spring @Transactional on test class or method.
● Cause test method transactions to rolled-back by default.
● Before and After Test callbacks are included in the transaction.
● Differentiate between Spring-managed and application-managed
transactions vs test-managed transactions
54
Transaction Management
● Use @Commit to force commit the transaction
● Use @BeforeTransaction and @AfterTransaction to run out-of-
transaction (it runs before Before and after After)
● o.s.t.c.transaction.TestTransaction could be used to run
programmed transactions
55
@DataJpaTest
@Transactional
@RunWith(SpringRunner::class)
class AccountRepositoryTest {
@Autowired
lateinit var accountRepository: AccountRepository
@Autowired
lateinit var entityManager: TestEntityManager
@Test
fun `test check balance never returns null for a valid account`() {
// Arrange
val account = Account()
entityManager.persist(account)
entityManager.flush() // to avoid false positives
// Act
val balance = accountRepository.checkBalance(account.id)
// Assert
assertThat(balance).isEqualTo(0.0)
}
}
56
Executing SQL Scripts
● @Sql implemented using SqlScriptsTestExecutionListener which
delegates to ResourceDatabasePopulator
● ResourceDatabasePopulator also used by spring boot auto-
configurations ("spring.datasource.schema" and
"spring.datasource.data")
● ResourceDatabasePopulator delegates to
o.s.jdbc.datasource.init.ScriptUtils
57
@SpringJUnitConfig // Junit 5
@Sql("/test-schema.sql")
class DatabaseTests {
@Test
@Sql(scripts="/test-schema.sql", config=@SqlConfig(commentPrefix =
"`"))
@Sql("/test-user-data.sql")
void userTest {
// test code here
}
}
58
Spring MVC Test Framework
● Built on the Servlet API mock objects
● Does not use a running Servlet container
● Why not pure Unit test is not enough? Need to test request mapping,
conversion, validation, etc.
● Two ways to create MockMvc:
○ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
○ this.mockMvc = MockMvcBuilders.standaloneSetup(new
AccountController()).build();
59
@Autowired
private MockMvc mockMvc; // auto-configured by spring boot
@MockBean
private AccountService accountService;
@Test
public void testDepositInvalidInput() throws Exception {
// ....
this.mockMvc.perform(put("/api/v1/account/1/balance/deposit")
.content(jsonObject.toString())
.contentType(MediaType.APPLICATION_JSON_UTF8))
.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.key")
.value("input.invalid.amountDto.amount.NotNull"));
}
60
Client-Side REST Tests
// Arrange
RestTemplate restTemplate = new RestTemplate()
MockRestServiceServer server =
MockRestServiceServer.bindTo(restTemplate).build();
server.expect(times(1),
requestTo("/hotels/42")).andExpect(method(HttpMethod.GET))
.andRespond(withSuccess("{ "id" : "42", "name" : "Holiday Inn"}",
MediaType.APPLICATION_JSON));
// Act
HotelService hotelService = new HotelService(restTemplate);
Hotel hotel = hotelService.findById(4); //should call the /hotels/42 endpoint
// Assert
server.verify();
61
Features of
Spring boot
62
Helpful Tools
● spring-boot-starter-test brings:
○ JUnit
○ Spring-Test
○ AssertJ: A fluent assertion library.
○ Hamcrest: A library of matcher objects (also known as constraints or predicates).
○ Mockito: A Java mocking framework.
○ JSONassert: An assertion library for JSON.
○ JsonPath: XPath for JSON.
63
Overview
● Use @SpringBootTest instead of @ContextConfiguration.
● By default @SpringBootTest run in MOCK environment
● Search for configurations automatically, no need to configure it manually.
○ - It search for classes annotated by @SpringBootApplication or
@SpringBootConfiguration.
● you can use nested @TestConfiguration to customize the primary
configuration.
● Use @MockBean to define Mockito mocks for beans inside the
ApplicationContext (mocking in integration tests)
64
Overview
● Can do real end-to-end servlet test using TestRestTemplate or
WebTestClient with @SpringBootTest(webEnvironment =
WebEnvironment.RANDOM_PORT)
○ Provide assertions similar to MockMvc
○ Can use REST Assured api as well
65
Testing Slices
● Configured in spring-boot-test-autoconfigure
● Load part of the application configurations/components
● You cannot use multiple slices together, but can @...Test one and
@AutoConfigure... or @ImportAutoConfiguration the others.
● Loads the corresponding Auto Configurations and beans
○ Example, @WebMvcTests does not regular @Componenet classes
66
Testing Slices
● Testing Slices support:
○ @JsonTest
○ @WebMvcTest
○ @WebFluxTest
○ @DataJpaTest
○ @DataRedisTest
○ @RestClientTest
○ ...
67
Demo
68
git clone https://github.com/mhewedy/primitive-bank.git
Extras
69
Using kotlin to write Tests?
70
References
● Test-Driven Development by Example
● Pragmatic Unit Testing in Java with JUnit
● https://blog.cleancoder.com/uncle-bob/2014/05/14/TheLittleMocker.html
● https://www.martinfowler.com/bliki/TestPyramid.html
● https://martinfowler.com/articles/mocksArentStubs.html
● https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html
● https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-
testing.html
● https://www.petrikainulainen.net/programming/testing/writing-clean-tests-to-verify-or-
not-to-verify/
71

More Related Content

What's hot

What's hot (20)

Spring boot introduction
Spring boot introductionSpring boot introduction
Spring boot introduction
 
Spring framework Introduction
Spring framework IntroductionSpring framework Introduction
Spring framework Introduction
 
Introduction to Spring Framework
Introduction to Spring FrameworkIntroduction to Spring Framework
Introduction to Spring Framework
 
Workshop Spring - Session 1 - L'offre Spring et les bases
Workshop Spring  - Session 1 - L'offre Spring et les basesWorkshop Spring  - Session 1 - L'offre Spring et les bases
Workshop Spring - Session 1 - L'offre Spring et les bases
 
Spring Boot and REST API
Spring Boot and REST APISpring Boot and REST API
Spring Boot and REST API
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Spring boot - an introduction
Spring boot - an introductionSpring boot - an introduction
Spring boot - an introduction
 
Spring Data JPA
Spring Data JPASpring Data JPA
Spring Data JPA
 
Real Life Clean Architecture
Real Life Clean ArchitectureReal Life Clean Architecture
Real Life Clean Architecture
 
Introduction to Aspect Oriented Programming
Introduction to Aspect Oriented ProgrammingIntroduction to Aspect Oriented Programming
Introduction to Aspect Oriented Programming
 
Spring GraphQL
Spring GraphQLSpring GraphQL
Spring GraphQL
 
우아한 모노리스
우아한 모노리스우아한 모노리스
우아한 모노리스
 
Spring Boot on Amazon Web Services with Spring Cloud AWS
Spring Boot on Amazon Web Services with Spring Cloud AWSSpring Boot on Amazon Web Services with Spring Cloud AWS
Spring Boot on Amazon Web Services with Spring Cloud AWS
 
Spring Data JDBC: Beyond the Obvious
Spring Data JDBC: Beyond the ObviousSpring Data JDBC: Beyond the Obvious
Spring Data JDBC: Beyond the Obvious
 
이벤트 기반 분산 시스템을 향한 여정
이벤트 기반 분산 시스템을 향한 여정이벤트 기반 분산 시스템을 향한 여정
이벤트 기반 분산 시스템을 향한 여정
 
Declarative Clients in Spring
Declarative Clients in SpringDeclarative Clients in Spring
Declarative Clients in Spring
 
Spring Boot Tutorial
Spring Boot TutorialSpring Boot Tutorial
Spring Boot Tutorial
 
Declarative Concurrency with Reactive Programming
Declarative Concurrency with Reactive ProgrammingDeclarative Concurrency with Reactive Programming
Declarative Concurrency with Reactive Programming
 
Introduction to Spring Cloud
Introduction to Spring Cloud           Introduction to Spring Cloud
Introduction to Spring Cloud
 
Completable future
Completable futureCompletable future
Completable future
 

Similar to Testing Spring Applications

Plone testingdzug tagung2010
Plone testingdzug tagung2010Plone testingdzug tagung2010
Plone testingdzug tagung2010
Timo Stollenwerk
 
Strategy-driven Test Generation with Open Source Frameworks
Strategy-driven Test Generation with Open Source FrameworksStrategy-driven Test Generation with Open Source Frameworks
Strategy-driven Test Generation with Open Source Frameworks
Dimitry Polivaev
 

Similar to Testing Spring Applications (20)

Test driven development
Test driven developmentTest driven development
Test driven development
 
Testing in FrontEnd World by Nikita Galkin
Testing in FrontEnd World by Nikita GalkinTesting in FrontEnd World by Nikita Galkin
Testing in FrontEnd World by Nikita Galkin
 
Никита Галкин "Testing in Frontend World"
Никита Галкин "Testing in Frontend World"Никита Галкин "Testing in Frontend World"
Никита Галкин "Testing in Frontend World"
 
Plone testingdzug tagung2010
Plone testingdzug tagung2010Plone testingdzug tagung2010
Plone testingdzug tagung2010
 
Strategy-driven Test Generation with Open Source Frameworks
Strategy-driven Test Generation with Open Source FrameworksStrategy-driven Test Generation with Open Source Frameworks
Strategy-driven Test Generation with Open Source Frameworks
 
Behaviour Driven Development and Thinking About Testing
Behaviour Driven Development and Thinking About TestingBehaviour Driven Development and Thinking About Testing
Behaviour Driven Development and Thinking About Testing
 
Bdd and-testing
Bdd and-testingBdd and-testing
Bdd and-testing
 
Grails unit testing
Grails unit testingGrails unit testing
Grails unit testing
 
Advanced Dagger talk from 360andev
Advanced Dagger talk from 360andevAdvanced Dagger talk from 360andev
Advanced Dagger talk from 360andev
 
Unit Testing - The Whys, Whens and Hows
Unit Testing - The Whys, Whens and HowsUnit Testing - The Whys, Whens and Hows
Unit Testing - The Whys, Whens and Hows
 
Unit testing
Unit testingUnit testing
Unit testing
 
Developer Test - Things to Know
Developer Test - Things to KnowDeveloper Test - Things to Know
Developer Test - Things to Know
 
UPC Testing talk 2
UPC Testing talk 2UPC Testing talk 2
UPC Testing talk 2
 
Writing Tests with the Unity Test Framework
Writing Tests with the Unity Test FrameworkWriting Tests with the Unity Test Framework
Writing Tests with the Unity Test Framework
 
3 WAYS TO TEST YOUR COLDFUSION API
3 WAYS TO TEST YOUR COLDFUSION API3 WAYS TO TEST YOUR COLDFUSION API
3 WAYS TO TEST YOUR COLDFUSION API
 
3 WAYS TO TEST YOUR COLDFUSION API -
3 WAYS TO TEST YOUR COLDFUSION API - 3 WAYS TO TEST YOUR COLDFUSION API -
3 WAYS TO TEST YOUR COLDFUSION API -
 
Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)Developer Tests - Things to Know (Vilnius JUG)
Developer Tests - Things to Know (Vilnius JUG)
 
Android testing
Android testingAndroid testing
Android testing
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven Development
 
Unit testing basic
Unit testing basicUnit testing basic
Unit testing basic
 

Recently uploaded

Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
mbmh111980
 
JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
Max Lee
 

Recently uploaded (20)

SOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBrokerSOCRadar Research Team: Latest Activities of IntelBroker
SOCRadar Research Team: Latest Activities of IntelBroker
 
How to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabberHow to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabber
 
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdfMastering Windows 7 A Comprehensive Guide for Power Users .pdf
Mastering Windows 7 A Comprehensive Guide for Power Users .pdf
 
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
 
Benefits of Employee Monitoring Software
Benefits of  Employee Monitoring SoftwareBenefits of  Employee Monitoring Software
Benefits of Employee Monitoring Software
 
Designing for Privacy in Amazon Web Services
Designing for Privacy in Amazon Web ServicesDesigning for Privacy in Amazon Web Services
Designing for Privacy in Amazon Web Services
 
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
Paketo Buildpacks : la meilleure façon de construire des images OCI? DevopsDa...
 
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAGAI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
 
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital TransformationWSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
WSO2Con2024 - WSO2's IAM Vision: Identity-Led Digital Transformation
 
Accelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with PlatformlessAccelerate Enterprise Software Engineering with Platformless
Accelerate Enterprise Software Engineering with Platformless
 
JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
 
Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024
 
Using IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New ZealandUsing IESVE for Room Loads Analysis - Australia & New Zealand
Using IESVE for Room Loads Analysis - Australia & New Zealand
 
AI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in MichelangeloAI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in Michelangelo
 
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
Tree in the Forest - Managing Details in BDD Scenarios (live2test 2024)
 
AI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning FrameworkAI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning Framework
 
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdfA Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
 
A Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data MigrationA Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data Migration
 
INGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by DesignINGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by Design
 
iGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by SkilrockiGaming Platform & Lottery Solutions by Skilrock
iGaming Platform & Lottery Solutions by Skilrock
 

Testing Spring Applications

  • 1. Testing Spring Applications Riyadh Java Meetup Mohammad Hewedy github.com/mhewedy 1 1
  • 2. Agenda ● Unit Test: What and Why? ● Test Driven Development ● Test Patterns ● Test Doubles ● Testing Spring Applications ○ Unit Testing ○ Integration Testing and the TestContext Framework ○ Slice Testing in Spring boot ● Demo 2
  • 4. 4
  • 5. A unit test is a piece of code written by a developer that exercises a very small, specific area of functionality of the code being tested. Unit tests are performed to prove that a piece of code does what the developer thinks it should do. Pragmatic Unit Testing in Java with JUnit 5
  • 7. ● It will make your designs better. ● Reduce the amount of time you spend debugging. ● Give confidence in the code. ● Make sure code do what you want. ● Do what you want all of the Time. ● Make sure you can depend on code. ● Document your intent. ● Shows how to use your code. ● Avoid any collateral damage 7
  • 11. ● It takes too much time to write the tests? ● It takes too long to run the tests? ● It’s not my job to test my code? ● I’m being paid to write code, not to write tests? ● I feel guilty about putting testers and QA staff out of work? 11
  • 12. Test Pyramid Unit Tests Integration Tests (Service Tests) E2E Tests (UI Tests) Fast Slow 12
  • 13. Characteristics of Good Tests ● Automatic ● Thorough ● Repeatable ● Independent ● Professional ● Speed 13
  • 14. Always, “Spring The Trap” Jim Baird - Adventurer 14
  • 16. class MathTest(TestCase): def test_add(self): math = Math() result = math.add(10, 20) assert_equals(30, result) def test_multiply(self): math = Math() result = math.multiply(10, 20) assert_equals(200, result) class Math: def add(self, x, y): return x + y def multiply(self, x, y): return x * y 16
  • 17. Test Driven Development Red/Green/Refactor Write a test Make it compile Run to see it fails Make it run (see next) Remove Duplicate (Refactor) 17
  • 18. How to “Make it run” (Geen Bar Pattern) 1. Fake it 2. Triangulate 3. Obvious implementation 18
  • 19. Green Bar Pattern in Action 19
  • 20. Test Patterns ● Arrange, Act, Assert. ● Arrange, Assert-not, Act, Assert. ● Given, When, Then. ● Setup, Exercise, Verify, Teardown. 20
  • 21. // spock framework def "test listToString"() { given: def list = Arrays.asList("HELLO", "WORLD") when: def listToString = Util.listToString(list) then: listToString == "HELLOnWORLDn" } https://github.com/mhewedy/spwrap/blob/master/src/test/groovy/spwrap/UtilTest.groovy 21
  • 22. @Unroll def "#testDB : using implicit ResultSet Mapper to map result set"() { given: setup(testDB) when: customerDao.createCustomer0(“Abdullah”, “Mohammad”) def list = customerDao.listCustomers(list) then: 1 == list.size() with(list.get(0)){ “Abdullah” == firstName() “Mohammad” == lastName() } cleanup: cleanup(testDB) where: testDB << [HSQL, MYSQL, SQLServer, ORACLE] } 22 https://github.com/mhewedy/spwrap/blob/master/src/test/groovy/spwrap/DAOIntTest.groovy
  • 24. interface Mailer { Boolean send(String to, String subject); } // billing is SUT billing.bill(cusomer); // mailer is collaborator mailer.send(cusomer.getEmail(), postBillHtmlTemplate); Collaborator object vs SUT 24
  • 25. Dummy class DummyMailer implements Mailer { Boolean send(String to, String subject) { return null; } } 25
  • 26. Stub class SuccessfullySendingMailerStub implements Mailer { Boolean send(String to, String subject) { return true; } } 26
  • 27. Spy class SuccessfullySendingMailerSpy implements Mailer { public int callCount; public String recordedTo; public String recordedSubject; Boolean send(String to, String subject) { this.callCount += 1; this.recordedTo = to; this.recordedSubject = subject; return true; } } 27
  • 28. Mock class SuccessfullySendingMailerMock implements Mailer { private int callCount; private String recordedTo; private String recordedSubject; Boolean send(String to, String subject) { this.callCount += 1; this.recordedTo = to; this.recordedSubject = subject; return true; } boolean verify(String expectedTo, String expectedSubject) { return callCount == 1 && this.recordedTo.equals(expectedTo) && this.recordedSubject.equals(expectedSubject); } } 28
  • 29. Fake class MailerFake implements Mailer { Boolean send(String to, String subject) { return SimulatorDataSource.getMailer() .containsRecord(to, subject); } } 29
  • 30. To Mock or not to Mock ● The Mockist vs The Classicist ● The Mini-integration Tests ● The ObjectMother ● SUT and Collaborator objects ● Ways to Test the service layer: ○ Mock the Repository layer. ○ Use in-memory database. ○ Use a real test database. 30
  • 33. Mocks provided by spring ● Environment: ○ MockEnvironment and MockPropertySource ● JNDI ○ SimpleNamingContextBuilder ○ e.g. To bind an embedded datasource to a jndi name ● Servlet api ○ MockHttpServletRequest, MockHttpServletResponse, MockHttpSession, etc. Exists in package: org.springframework.mock 33
  • 34. Support Classes ● ReflectionTestUtils ● AopTestUtils ● ModelAndViewAssert ● JdbcTestUtils 34
  • 36. Integration Testing ● Slower than unit tests. ● Perform some integration testing without requiring application deployment. ● Exists in package: org.springframework.test 36
  • 37. Goals of Spring Integration Testing support ● Context Management and Caching ● Dependency Injection of Test Fixtures ● Transaction Management ● Easily executing SQL Scripts 37
  • 38. Important Annotations ● @ContextConfiguration ● @ActiveProfiles ● @TestPropertySource ● @DirtiesContext ● @Commit & @Rollback ● @BeforeTransaction & @AfterTransaction ● @Sql & @SqlConfig ● @Autowired, @PersistenceContext, @Qualifier, etc. ● @IfProfileValue, @Timed, @Repeat (junit4) ● @SpringJUnitConfig, @EnabledIf, @DisabledIf (junit5) 38
  • 39. @TestExecutionListeners ● Important component of TestContext framework ● Accepts implementations of TestExecutionListener interface ● Used to implement many of the features ● Example implementations include: ○ ServletTestExecutionListener ○ DependencyInjectionTestExecutionListener ○ DirtiesContextTestExecutionListener ○ TransactionalTestExecutionListener ○ SqlScriptsTestExecutionListener 39
  • 40. Meta Annotations @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @DisabledIf( expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}", reason = "Disabled on Mac OS") public @interface DisabledOnMac {} 40
  • 41. @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @ContextConfiguration(classes = {GeoConfig.class}) @ActiveProfiles("dev") @Transactional public @interface TransactionalDevGeoTestConfig {} 41
  • 43. JUnit 5 how is different from JUnit4? ● JUnit 4 uses Runner and Rule (TestRule & MethodRule) ○ @RunWith & @Rule ○ Examples: ■ MockitoJUnitRunner ■ SpringRunner ■ MockitoRule ■ WireMockRule ● JUnit 5 uses Extension ○ @ExtendWith ○ Composable ○ Examples: ■ SpringExtension ■ MockitoExtension 43
  • 44. Spring TestContext is Test-Framework Agnostic TestContextManager is the entry point for Test frameworks (JUnit 4, JUnit5, TestNG) to the TestContext framework. 44
  • 45. Overview ● Support the Integration Testing goals of spring ● Exists in package: org.springframework.test. context ● Core Classes ○ TestContextManager ○ TestContext ○ TestExecutionListener ○ SmartContextLoader ○ TestContextBootstrapper 45
  • 47. public void beforeTestMethod(Object testInstance, Method testMethod) throws Exception { String callbackName = "beforeTestMethod"; getTestContext().updateState(testInstance, testMethod, null); for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) { try { testExecutionListener.beforeTestMethod(getTestContext()); }catch (Throwable ex) { handleBeforeException(ex, callbackName, testExecutionListener, testInstance, testMethod); } } } 47
  • 48. TestContextBootstrapper ● Used to provide the TestContextManager by TestContext and the list of TestExecutionListener ● You can customize using @BootstrapWith ● @SpringBootTest is itself annotated with @BootstrapWith(SpringBootTestContextBootstrapper.class) 48
  • 49. TestExecutionListener ● By default the following listeners are registered: ○ ServletTestExecutionListener ○ DirtiesContextBeforeModesTestExecutionListener ○ DependencyInjectionTestExecutionListener ○ DirtiesContextTestExecutionListener ○ TransactionalTestExecutionListener ○ SqlScriptsTestExecutionListener ● You can customize using @TestExecutionListeners or using SpringFactoriesLoader mechanism. ● beware not to override default configured listeners 49
  • 51. Context Management ● Provide Context Loading from XML, Groovy and Annotated Classes Example: @ContextConfiguration(locations={"/app-config.xml", "/test- config.xml"}) ● Provide Context Initialization using classes implementation ApplicationContextInitializer ● Provide Caching for the loaded Context between tests ● Can evict the cache using @DirtiesContext 51
  • 52. @RunWith(SpringRunner.class) @ContextConfiguration public class OrderServiceTest { @Configuration static class Config { @Bean public OrderService orderService() { OrderService orderService = new OrderServiceImpl(); return orderService; } } @Autowired private OrderService orderService; @Test public void testOrderService() { // test the orderService } } 52
  • 53. Dependency Injection ● Implemented using DependencyInjectionTestExecutionListener ● Can use setter injection or field injection ● JUnit 5 you can use constructor injection ● Uses @Autowired or @Inject 53
  • 54. Transaction Management ● Implemented using TransactionalTestExecutionListener. ● PlatformTransactionManager should exist in ApplicationContext (auto-configured in spring boot). ● Must declare Spring @Transactional on test class or method. ● Cause test method transactions to rolled-back by default. ● Before and After Test callbacks are included in the transaction. ● Differentiate between Spring-managed and application-managed transactions vs test-managed transactions 54
  • 55. Transaction Management ● Use @Commit to force commit the transaction ● Use @BeforeTransaction and @AfterTransaction to run out-of- transaction (it runs before Before and after After) ● o.s.t.c.transaction.TestTransaction could be used to run programmed transactions 55
  • 56. @DataJpaTest @Transactional @RunWith(SpringRunner::class) class AccountRepositoryTest { @Autowired lateinit var accountRepository: AccountRepository @Autowired lateinit var entityManager: TestEntityManager @Test fun `test check balance never returns null for a valid account`() { // Arrange val account = Account() entityManager.persist(account) entityManager.flush() // to avoid false positives // Act val balance = accountRepository.checkBalance(account.id) // Assert assertThat(balance).isEqualTo(0.0) } } 56
  • 57. Executing SQL Scripts ● @Sql implemented using SqlScriptsTestExecutionListener which delegates to ResourceDatabasePopulator ● ResourceDatabasePopulator also used by spring boot auto- configurations ("spring.datasource.schema" and "spring.datasource.data") ● ResourceDatabasePopulator delegates to o.s.jdbc.datasource.init.ScriptUtils 57
  • 58. @SpringJUnitConfig // Junit 5 @Sql("/test-schema.sql") class DatabaseTests { @Test @Sql(scripts="/test-schema.sql", config=@SqlConfig(commentPrefix = "`")) @Sql("/test-user-data.sql") void userTest { // test code here } } 58
  • 59. Spring MVC Test Framework ● Built on the Servlet API mock objects ● Does not use a running Servlet container ● Why not pure Unit test is not enough? Need to test request mapping, conversion, validation, etc. ● Two ways to create MockMvc: ○ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); ○ this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build(); 59
  • 60. @Autowired private MockMvc mockMvc; // auto-configured by spring boot @MockBean private AccountService accountService; @Test public void testDepositInvalidInput() throws Exception { // .... this.mockMvc.perform(put("/api/v1/account/1/balance/deposit") .content(jsonObject.toString()) .contentType(MediaType.APPLICATION_JSON_UTF8)) .andDo(print()) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.key") .value("input.invalid.amountDto.amount.NotNull")); } 60
  • 61. Client-Side REST Tests // Arrange RestTemplate restTemplate = new RestTemplate() MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplate).build(); server.expect(times(1), requestTo("/hotels/42")).andExpect(method(HttpMethod.GET)) .andRespond(withSuccess("{ "id" : "42", "name" : "Holiday Inn"}", MediaType.APPLICATION_JSON)); // Act HotelService hotelService = new HotelService(restTemplate); Hotel hotel = hotelService.findById(4); //should call the /hotels/42 endpoint // Assert server.verify(); 61
  • 63. Helpful Tools ● spring-boot-starter-test brings: ○ JUnit ○ Spring-Test ○ AssertJ: A fluent assertion library. ○ Hamcrest: A library of matcher objects (also known as constraints or predicates). ○ Mockito: A Java mocking framework. ○ JSONassert: An assertion library for JSON. ○ JsonPath: XPath for JSON. 63
  • 64. Overview ● Use @SpringBootTest instead of @ContextConfiguration. ● By default @SpringBootTest run in MOCK environment ● Search for configurations automatically, no need to configure it manually. ○ - It search for classes annotated by @SpringBootApplication or @SpringBootConfiguration. ● you can use nested @TestConfiguration to customize the primary configuration. ● Use @MockBean to define Mockito mocks for beans inside the ApplicationContext (mocking in integration tests) 64
  • 65. Overview ● Can do real end-to-end servlet test using TestRestTemplate or WebTestClient with @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) ○ Provide assertions similar to MockMvc ○ Can use REST Assured api as well 65
  • 66. Testing Slices ● Configured in spring-boot-test-autoconfigure ● Load part of the application configurations/components ● You cannot use multiple slices together, but can @...Test one and @AutoConfigure... or @ImportAutoConfiguration the others. ● Loads the corresponding Auto Configurations and beans ○ Example, @WebMvcTests does not regular @Componenet classes 66
  • 67. Testing Slices ● Testing Slices support: ○ @JsonTest ○ @WebMvcTest ○ @WebFluxTest ○ @DataJpaTest ○ @DataRedisTest ○ @RestClientTest ○ ... 67
  • 70. Using kotlin to write Tests? 70
  • 71. References ● Test-Driven Development by Example ● Pragmatic Unit Testing in Java with JUnit ● https://blog.cleancoder.com/uncle-bob/2014/05/14/TheLittleMocker.html ● https://www.martinfowler.com/bliki/TestPyramid.html ● https://martinfowler.com/articles/mocksArentStubs.html ● https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html ● https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features- testing.html ● https://www.petrikainulainen.net/programming/testing/writing-clean-tests-to-verify-or- not-to-verify/ 71

Editor's Notes

  1. Automatic: In Invoking the tests In Checking the results That the test must determine for itself whether it passed or failed. This idea of having the tests run by themselves and check themselves is critical, because it means that you don’t have to think about it—it just happens as part of the project Throught: Test everything that’s likely break based on the needs of your project Repeatable: independent of the environment run over and over again, in any order, and produce the same results. Independent: testing one thing at a time independent from the environment and each other Independent also means that no test relies on any other test Professional: written and maintained to the same professional standards as your production code code reuse.
  2. cause the production code to exhibit the very bug you’re trying to detect, and verify that the test fails as expected. Steps when fix a bug: 1. Identify the bug. 2. Write a test that fails, to prove the bug exists. 3. Fix the code such that the test now passes. 4. Verify that all tests still pass (i.e., you didn’t break anything else as a result of the fix
  3. - @ContextConfiguration <<inheritable>> <<load additional test configuration, ApplicationContextInitializer or configure ContextLoader>> - @ActiveProfiles <<inheritable>> - @TestPropertySource <<class level>> - @TestExecutionListeners: TestExecutionListener: beforeTestClass prepareTestInstance beforeTestExecution beforeTestMethod afterTestExecution afterTestMethod afterTestClass examples: ServletTestExecutionListener DependencyInjectionTestExecutionListener DirtiesContextTestExecutionListener TransactionalTestExecutionListener SqlScriptsTestExecutionListener - @DirtiesContext: DirtiesContextTestExecutionListener <<class&method level>> - @BootstrapWith TestContextBootstraper - @Commit & @Rollback: TransactionalTestExecutionListener - @BeforeTransaction & @AfterTransaction: TransactionalTestExecutionListener - @Sql & @SqlConfig: SqlScriptsTestExecutionListener - @Autowired, @PersistenceContext, @Qualifier, etc. : DependencyInjectionTestExecutionListener - @IfProfileValue, @Timed, @Repeat (junit4) - @SpringJUnitConfig, @EnabledIf, @DisabledIf (junit5)
  4. TestContextManager is independent of the Underlying Test framework It is being used by JUnit 5 by being used by SpringExtensions JUnit 5 Extension And used by JUnit 4 via Rules (SpringMethodRule) and Runners (Spring JUnit4ClassRunner) via the RunBeforeTestMethodCallbacks