TDD: Unit tests
parent
7ea1a78b5b
commit
c0971deee8
161
Home.md
161
Home.md
@ -1,111 +1,50 @@
|
|||||||
---
|
---
|
||||||
gitea: none
|
gitea: none
|
||||||
include_toc: true
|
include_toc: true
|
||||||
---
|
---
|
||||||
|
|
||||||
This repo is my first attempt to learn `SpringBoot` following [this](https://spring.academy/courses/building-a-rest-api-with-spring-boot/lessons/introduction) tutorial. The setup is Visual Code IDE alongside with [SpringBoot](https://code.visualstudio.com/docs/java/java-spring-boot) plugin.
|
This repo is my first attempt to learn `SpringBoot` following [this](https://spring.academy/courses/building-a-rest-api-with-spring-boot/lessons/introduction) tutorial. The setup is Visual Code IDE alongside with [SpringBoot](https://code.visualstudio.com/docs/java/java-spring-boot) plugin.
|
||||||
|
|
||||||
# Spring Initializr
|
# Spring Initializr
|
||||||
|
|
||||||
In VsCode press `cmd+shif+p` and type `Spring Initilizr`. Choose:
|
In VsCode press `cmd+shif+p` and type `Spring Initilizr`. Choose:
|
||||||
- Gradle Project
|
- Gradle Project
|
||||||
- _SpringBoot version:_ latest (3.1.1)
|
- _SpringBoot version:_ latest (3.1.1)
|
||||||
- _project language:_ Java
|
- _project language:_ Java
|
||||||
- _group id:_ djmil
|
- _group id:_ djmil
|
||||||
- _artifact id (aka project name):_ cashcard
|
- _artifact id (aka project name):_ cashcard
|
||||||
- _packaging type:_ jar
|
- _packaging type:_ jar
|
||||||
- _java version:_ 17
|
- _java version:_ 17
|
||||||
- _dependencies:_
|
- _dependencies:_
|
||||||
- SpringWeb
|
- SpringWeb
|
||||||
|
|
||||||
Essentially, this will generate a default minimalistic jet functional SpringBoot project. Entry point, aka `main()` can be found in [src/main/java/djmil/cashcard/CashcardApplication.java](http://192.168.8.55:3000/HQLAx/FamilyCashCard/src/branch/main/src/main/java/djmil/cashcard/CashcardApplication.java). To run the application - press `ctrl+F5` or Play button in the top right corner of an editor.
|
Essentially, this will generate a default minimalistic jet functional SpringBoot project. Entry point, aka `main()` can be found in [src/main/java/djmil/cashcard/CashcardApplication.java](http://192.168.8.55:3000/HQLAx/FamilyCashCard/src/branch/main/src/main/java/djmil/cashcard/CashcardApplication.java). To run the application - press `ctrl+F5` or Play button in the top right corner of an editor.
|
||||||
|
|
||||||
# TDD
|
# TDD
|
||||||
|
|
||||||
## myFirstTest
|
Different tests can be written at different levels of the system. At each level, there is a balance between the speed of execution, the “cost” to maintain the test, and the confidence it brings to system correctness. This hierarchy is often represented as a “testing pyramid”.
|
||||||
|
|
||||||
Let's start with the simplest thing you can imagine: a single test method with a single statement. Create [src/test/java/example/cashcard/CashCardJsonTest.java](http://192.168.8.55:3000/HQLAx/FamilyCashCard/src/commit/5ff71154302523ab5ebd0a291e3f5819aed8fdb9/src/test/java/djmil/cashcard/CashCardJsonTest.java):
|
![Testing pyramid](https://blog.missiondata.com/wp-content/uploads/MD_TestingPyramid2x-1560x1045.png "Testing pyramid")
|
||||||
|
|
||||||
``` java
|
## Unit Tests
|
||||||
package example.cashcard;
|
A [[Unit Tests]] exercises a small “unit” of the system that is isolated from the rest of the system. They should be simple and speedy. You want a high ratio of Unit Tests in your testing pyramid as they’re key to designing highly cohesive, loosely coupled software.
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
## Integration Tests
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
Integration Tests exercise a subset of the system and may exercise groups of units in one test. They are more complicated to write and maintain, and run slower than unit tests.
|
||||||
|
|
||||||
public class CashCardJsonTest {
|
## End-to-End Tests
|
||||||
|
An End-to-End Test exercises the system using the same interface that a user would, such as a web browser. While extremely thorough, End-to-End Tests can be very slow and fragile because they use simulated user interactions in potentially complicated UIs. Implement the smallest number of these tests.
|
||||||
@Test
|
|
||||||
public void myFirstTest() {
|
## TDD Cycle
|
||||||
assertThat(1).isEqualTo(42);
|
|
||||||
}
|
Software development teams love to move fast. So how do you go fast forever? By continuously improving and simplifying your code – this is called **refactoring**. One of the only ways you can safely refactor is when you have a trustworthy test suite. Thus, the best time to refactor the code you're currently focusing on is during the TDD cycle. This is called the Red, Green, Refactor development loop:
|
||||||
}
|
|
||||||
```
|
1. **Red:** Write a failing test for the desired functionality.
|
||||||
|
2. **Green:** Implement the simplest thing that can work to make the test pass.
|
||||||
The `@Test` annotation is part of the JUnit library, and the `assertThat` method is part of the AssertJ library. Both of these libraries are imported after the package statement.
|
3. **Refactor:** Look for opportunities to simplify, reduce duplication, or otherwise improve the code without changing any behavior—to _refactor._
|
||||||
|
4. Repeat!
|
||||||
A common convention (but not a requirement) is to always use the Test suffix for test classes. We’ve done that here. The full class name CashCardJsonTest.java gives you a clue about the nature of the test we're about to write.
|
|
||||||
|
|
||||||
In true Test-First fashion, we've written a failing test first. It's important to have a failing test first so you can have high confidence that whatever you did to fix the test actually worked.
|
|
||||||
|
|
||||||
Toggle terminal with `ctrl+tilda` and type
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./gradlew test
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing the CashCard Data Contract
|
|
||||||
|
|
||||||
```java
|
|
||||||
import org.springframework.boot.test.json.JacksonTester;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
@JsonTest
|
|
||||||
public class CashCardJsonTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private JacksonTester<CashCard> json;
|
|
||||||
```
|
|
||||||
|
|
||||||
Marking CashCardJsonTest with `@JsonTest` annotation makes it a test class which uses the Jackson framework (which is included as part of Spring). This provides extensive JSON testing and parsing support. It also establishes all the related behavior to test JSON objects.
|
|
||||||
|
|
||||||
### @Autowired
|
|
||||||
|
|
||||||
`@Autowired` is an annotation that directs Spring to create an object of the requested type. `JacksonTester` is a convenience wrapper to the Jackson JSON parsing library. It handles serialization and deserialization of JSON objects.
|
|
||||||
|
|
||||||
To create a CashCard class and the constructor that’s used in the cashCardSerializationTest() test, create the file `src/main/java/example/cashcard/CashCard.java` with the following contents (notice that this file is under in the src/main directory, not the src/test directory):
|
|
||||||
|
|
||||||
```java
|
|
||||||
package example.cashcard;
|
|
||||||
|
|
||||||
public record CashCard(Long id, Double amount) {
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### The contract file
|
|
||||||
|
|
||||||
`src/test/resources/example/cashcard/expected.json`
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": 99,
|
|
||||||
"amount": 123.45
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### The test
|
|
||||||
|
|
||||||
```java
|
|
||||||
@Test
|
|
||||||
public void cashCardSerializationTest() throws IOException {
|
|
||||||
CashCard cashCard = new CashCard(99L, 123.45);
|
|
||||||
assertThat(json.write(cashCard)).isStrictlyEqualToJson("expected.json");
|
|
||||||
assertThat(json.write(cashCard)).hasJsonPathNumberValue("@.id");
|
|
||||||
assertThat(json.write(cashCard)).extractingJsonPathNumberValue("@.id")
|
|
||||||
.isEqualTo(99);
|
|
||||||
assertThat(json.write(cashCard)).hasJsonPathNumberValue("@.amount");
|
|
||||||
assertThat(json.write(cashCard)).extractingJsonPathNumberValue("@.amount")
|
|
||||||
.isEqualTo(123.45);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
119
Unit Tests.md
Normal file
119
Unit Tests.md
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
---
|
||||||
|
gitea: none
|
||||||
|
include_toc: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# My first unit test
|
||||||
|
|
||||||
|
Let's start with the simplest thing you can imagine: a single test method with a single statement. Create [src/test/java/example/cashcard/CashCardJsonTest.java](http://192.168.8.55:3000/HQLAx/FamilyCashCard/src/commit/5ff71154302523ab5ebd0a291e3f5819aed8fdb9/src/test/java/djmil/cashcard/CashCardJsonTest.java):
|
||||||
|
|
||||||
|
``` java
|
||||||
|
package djmil.cashcard;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public class CashCardJsonTest {
|
||||||
|
@Test
|
||||||
|
public void myFirstTest() {
|
||||||
|
assertThat(1).isEqualTo(42);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `@Test` annotation is part of the JUnit library, and the `assertThat` method is part of the AssertJ library. Both of these libraries are imported after the package statement.
|
||||||
|
|
||||||
|
A common convention (but not a requirement) is to always use the Test suffix for test classes. We’ve done that here. The full class name CashCardJsonTest.java gives you a clue about the nature of the test we're about to write.
|
||||||
|
|
||||||
|
In true Test-First fashion, we've written a failing test first. It's important to have a failing test first so you can have high confidence that whatever you did to fix the test actually worked.
|
||||||
|
|
||||||
|
Toggle terminal with `ctrl+tilda` and type
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew test
|
||||||
|
```
|
||||||
|
|
||||||
|
# Testing the CashCard Data Contract
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.springframework.boot.test.json.JacksonTester;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
@JsonTest
|
||||||
|
public class CashCardJsonTest {
|
||||||
|
@Autowired
|
||||||
|
private JacksonTester<CashCard> json;
|
||||||
|
```
|
||||||
|
|
||||||
|
Marking CashCardJsonTest with `@JsonTest` annotation makes it a test class which uses the Jackson framework (which is included as part of Spring). This provides extensive JSON testing and parsing support. It also establishes all the related behavior to test JSON objects.
|
||||||
|
|
||||||
|
### @Autowired
|
||||||
|
|
||||||
|
`@Autowired` is an annotation that directs Spring to create an object of the requested type. `JacksonTester` is a convenience wrapper to the Jackson JSON parsing library. It handles serialization and deserialization of JSON objects.
|
||||||
|
|
||||||
|
To create a CashCard class and the constructor that’s used in the `cashCardSerializationTest()` test, create the file `src/main/java/djmil/cashcard/CashCard.java` with the following contents (notice that this file is under in the `src/main` directory, not the `src/test` directory):
|
||||||
|
|
||||||
|
```java
|
||||||
|
package djmil.cashcard;
|
||||||
|
|
||||||
|
public record CashCard(Long id, Double amount) {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### The contract file
|
||||||
|
|
||||||
|
`src/test/resources/djmil/cashcard/expected.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 99,
|
||||||
|
"amount": 123.45
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**NOTE** Resources
|
||||||
|
Pay attention to the path `djmil/cashcard/` is essentially a *package name*. It is shared between different aspects of the project:
|
||||||
|
- src/main/java - code
|
||||||
|
- src/tests/java - tests
|
||||||
|
- src/tests/resources - static resources for testing.
|
||||||
|
Essentially `gradle` is responsible to map different parts of source code onto final package to be accessible for java via *classpath*.
|
||||||
|
|
||||||
|
### The test
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Test
|
||||||
|
public void cashCardSerializationTest() throws IOException {
|
||||||
|
CashCard cashCard = new CashCard(99L, 123.45);
|
||||||
|
|
||||||
|
assertThat(json.write(cashCard)).isStrictlyEqualToJson("expected.json");
|
||||||
|
|
||||||
|
assertThat(json.write(cashCard)).hasJsonPathNumberValue("@.id");
|
||||||
|
assertThat(json.write(cashCard)).extractingJsonPathNumberValue("@.id")
|
||||||
|
.isEqualTo(99);
|
||||||
|
|
||||||
|
assertThat(json.write(cashCard)).hasJsonPathNumberValue("@.amount")
|
||||||
|
assertThat(json.write(cashCard)).extractingJsonPathNumberValue("@.amount")
|
||||||
|
.isEqualTo(123.45);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`.isStrictlyEqualToJson("expected.json");` will try to load static file from `FamilyCashCard/build/resources/test/djmil/cashcard` directory.
|
||||||
|
|
||||||
|
# Testing Deserialization
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Test
|
||||||
|
public void cashCardDeserializationTest() throws IOException {
|
||||||
|
String expected = """
|
||||||
|
{
|
||||||
|
"id":1000,
|
||||||
|
"amount":67.89
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
assertThat(json.parse(expected)).isEqualTo(new CashCard(1000L, 67.89));
|
||||||
|
assertThat(json.parseObject(expected).id()).isEqualTo(1000);
|
||||||
|
assertThat(json.parseObject(expected).amount()).isEqualTo(67.89);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
11
_Sidebar.md
11
_Sidebar.md
@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
gitea: none
|
|
||||||
include_toc: true
|
|
||||||
---
|
|
||||||
|
|
||||||
This is a test for special page called '_sidebar'
|
|
||||||
|
|
||||||
# [[Home]]
|
|
||||||
# subfolder
|
|
||||||
## Note1
|
|
||||||
# [[testPage]]
|
|
@ -1,3 +0,0 @@
|
|||||||
This note inside sub folder was created by Obsidian. I hope that GiteWiki engine would be able to handle this.
|
|
||||||
|
|
||||||
A link to the [[Home]] page
|
|
@ -1 +0,0 @@
|
|||||||
This wiki page is on the same level as [[Home]] page
|
|
Loading…
Reference in New Issue
Block a user