Unit testing involves writing small pieces of code to test specific functionality. Integration testing involves testing components together without full deployment. Spring Boot provides helpful tools for testing Spring applications, including test slices to load only parts of the configuration. The document discusses unit testing, integration testing, test patterns, test doubles, the Spring TestContext framework, and testing Spring Boot applications.
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
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
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
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
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
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
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
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
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
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
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
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.
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
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