Project created by : Sunho Hong (https://github.com/sunhohong)
note : This is a recap of the tutorial
This tutorial project is based on following series of Youtube videos:
- JDK 17
- IntelliJ IDEA
- Gradle 7.3
- Spring Boot 2.5.6
- Kotlin 1.6.0
- JUnit 5.8.1
- Spring Web 2.5.6
- Spring Boot DevTools 2.5.6
- Create a new project with Spring Initializr (https://start.spring.io/)

Bank
- Kotlin provides getter and setter methods by default, but we can make getter and setter if it necessary
- Using
valto make it read-only. - Kotlin's data class doesn’t need toString(), equals(), hashCode() cause those functions are auto generated
data class Bank( val accountNumber: String, val trust: Double, val transactionFee: Int )
Data Source
- Create a service layer to handle business logic.
- Service layer could handle complex logics but in this case it just connects to the data source and controller layer.
BankServiceinterfaceinterface BankService { fun getBanks(): Collection<Bank> fun getBank(accountNumber: String): Bank fun addBank(bank: Bank): Bank fun updateBank(bank: Bank): Bank fun deleteBank(accountNumber: String) }
- Controller using
@RestControllerannotation to make it a REST controller.
- Run
./gradlew testto run all tests
- Run test on IDE menu
Run > Run 'All Tests'
Test
@Test
fun `should $DESCRIPTION$`() {
// given
$GIVEN$
// when
$WHEN$
// then
$THEN$
}
Nested test
@Nested
@DisplayName("$DESCRIPTION$")
@TestInstance(Lifecycle.PER_CLASS)
inner class $CLASS_NAME$ {
$CONTENT$
}- If the project doesn't build with Java version problem, try to change the version from project structure settings. (
find under file menu or CMD + ;)

- If the project doesn't build with Gradle version problem, try to change the JDK path in .zshrc (or .bashrc) file.
- Set up Code style and Save action in IDE settings.
- If
gradlew bootBuildImageshowsConnection to the Docker daemon at 'localhost' failed with errorerror, check Docker is running on local machine.
- Add the
HelloWorldController - Update
README - Add
readme_resourcesdirectory for README file
- Dependencies, plugins and version/env descriptions are in
build.gradle.ktsfile. - Project settings are in
settings.gradle.kts - Build and test the project using
gradlew buildcommand without IDE - Server port can be changed by
application.propertiesfileserver.port=9000
- The Bank model
- Refactor the Bank model
- Move attributes definition to Primary constructor
- Using Kotlin data class
- Kotlin provides getter and setter methods by default, but we can make getter and setter if it necessary.
- Primary constructor can be placed just right after the class definition like params.
data class Bank( val accountNumber: String, val trust: Double, val transactionFee: Int )
- Kotlin data class doesn’t need toString(), equals(), hashCode() cause those functions are auto generated
- Add BankDataSource interface
- Add MockBankDataSource class
- Add MockBankDataSourceTest
@Repositoryannotation notifies functionality of the class to Spring- This class is responsible for retrieving data, storing data and those kind of functionalities.
@Repository class MockBankDataSource : BankDataSource
- This class is responsible for retrieving data, storing data and those kind of functionalities.
- CMD + Shift + T : Create Test (Should be cursor on class name or method name)
- Ctrl + R : Rerun last command (It is useful when test again)
- Add BankService class
- Add BankServiceTest class
- Add Mockk - mocking library for Kotlin
- Add Mockk to mock a component in other layer.
- Set
relaxed = truewhen calling mockk to prevent occurring errors cause mockk doens't know what should be returned.- If specific return value is needed, we can use stub, but other cases just using relaxed mock
private val dataSource: BankDataSource = mockk(relaxed = true)
- If specific return value is needed, we can use stub, but other cases just using relaxed mock
- Add BankController class
- Add BankControllerTest class
@RequestMappingannotation makes route for the application.@GetMappingannotation makes a function to work as a REST GET method.@SpringBootTestannotation is needed to test a controller, it will set up the application context.@SpringBootTest internal class BankControllerTest
MockMvcdoes mocking http request, so it works as a request provider.@Autowiredannotation matches beans from Spring container to the target variable automatically.- But
@AutoConfigureMockMvcannotation should be applied to the class@SpringBootTest @AutoConfigureMockMvc internal class BankControllerTest { @Autowired lateinit var mockMvc: MockMvc
- [Kotlin]
lateinitkeyword initialize a variable after instance created, so it accelerate the application's boot process.
- Add
getBank()method to BankController - Add test for
getBank()method to BankControllerTest - Add negative case test for
getBank()method to BankControllerTest - Add Exception handler to BankController for handling
NoSuchElementExceptionin controller layer
@GetMappingannotation with path variable to handle dynamic path variable@PathVariableannotation to get the path variable from the request URL@GetMapping("/{accountNumber}") fun getBank(@PathVariable accountNumber: String): Bank {
- Refactor tests in
BankControllerTestto group tests by@Nestedannotation @Nestedannotation andinner classare used to group tests.- Add live template for nested test. See Live Template section.
- Refactor
/api/bankurl tobaseUrlconstant inBankControllerTest
- Add POST endpoint to
BankController. - Add test for POST endpoint to
BankControllerTest. - Add
ObjectMapperbean toBankControllerTestto convertBankobject to JSON string. - Add
IllegalArgumentExceptionhandler toBankControllerto handle request with existing account number.
- Refactor
mockMvcmember variable to@Autowiredprimary constructor inBankControllerTest. - Refactor assert object contents statements in
BankControllerTestto useObjectMapper. - Change mock banks list to
mutableListOfto add new bank.- (Kotlin basic) In Kotlin,
listOfis immutable list, so it can't be changed. mutableListOfis mutable list, so it can be changed directly.- If you want to use immutable list on this case, you should return new list with added or removed item.
From Effective Kotlin
- (Kotlin basic) In Kotlin,
- Add Patch endpoint to
BankController. - Add test for Patch endpoint to
BankControllerTest.
- Nothing special. Just add patch endpoint and test.
- Refactor assert object contents statements in
BankControllerTestto useObjectMapperwas in this tutorial. not in the tutorial 9.
- Add Delete endpoint to
BankController. - Add test for Delete endpoint to
BankControllerTest.
- Nothing special. Just add delete endpoint and test.
- [Kotlin]
Unitis a return type same asvoidin Java.
- Refactor test using
@DirtiesContextfit to "Isolated" principle of F.I.R.S.T. principles.
First Principles for Automated Tests
-
✅ Fast - fast feedback loop, run often
-
✅ Isolated - independent, arbitrary order.
-
✅ Repeatable - same result each time,not flacky
-
✅ Self-validating - actual vs. expected
-
✅ Timely - with (or even before!) production code
-
@DirtiesContextannotation is used to reset the context after each test. But it decreases the performance of tests.
- Add
NetworkDataSourceclass.
- How to use DI to select specific implementation of interface if there are multiple implementations.
- Disclaimer : Currently this tutorial is not working because the endpoint in the tutorial is not available anymore.
- Refactor
BankControllerTestto fit changes in tutorial 13. - Create a Jar file with
bootJartask. - Create a Docker image with
bootBuildImagetask.
- Gradle is a build tool for Java, Kotlin and maybe other languages.
- Gradle also used to automation I think it's like
rakein ROR of this case.- Default Gradle tasks include
build,clean,test,assemble,jar,bootJar,bootRun,bootBuildImage,dockerand many more.
- Default Gradle tasks include
gradlew bootRuncommand is used to run the application in terminal directly.gradlew bootJarmakes a jar file inbuild/libsdirectory. It can be run withjava -jarcommand.- There are three different ways to run the application without IDE.
- Run the application with
gradlew bootRuncommand. - Run the application with
java -jarcommand. - Run the application with Docker image.
- Run the application with