From c0971deee824cd7cba4e0995a5227e3042259742 Mon Sep 17 00:00:00 2001 From: djmil Date: Tue, 18 Jul 2023 19:27:09 +0200 Subject: [PATCH] TDD: Unit tests --- Home.md | 161 ++++++++++++++------------------------------- Unit Tests.md | 119 +++++++++++++++++++++++++++++++++ _Sidebar.md | 11 ---- subfolder/Note1.md | 3 - testPage.md | 1 - 5 files changed, 169 insertions(+), 126 deletions(-) create mode 100644 Unit Tests.md delete mode 100644 _Sidebar.md delete mode 100644 subfolder/Note1.md delete mode 100644 testPage.md diff --git a/Home.md b/Home.md index 1f9e976..11d5597 100644 --- a/Home.md +++ b/Home.md @@ -1,111 +1,50 @@ ---- -gitea: none -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. - -# Spring Initializr - -In VsCode press `cmd+shif+p` and type `Spring Initilizr`. Choose: -- Gradle Project -- _SpringBoot version:_ latest (3.1.1) -- _project language:_ Java -- _group id:_ djmil -- _artifact id (aka project name):_ cashcard -- _packaging type:_ jar -- _java version:_ 17 -- _dependencies:_ - - 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. - -# TDD - -## myFirstTest - -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 example.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 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); - } -``` - - +--- +gitea: none +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. + +# Spring Initializr + +In VsCode press `cmd+shif+p` and type `Spring Initilizr`. Choose: +- Gradle Project +- _SpringBoot version:_ latest (3.1.1) +- _project language:_ Java +- _group id:_ djmil +- _artifact id (aka project name):_ cashcard +- _packaging type:_ jar +- _java version:_ 17 +- _dependencies:_ + - 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. + +# TDD + +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”. + +![Testing pyramid](https://blog.missiondata.com/wp-content/uploads/MD_TestingPyramid2x-1560x1045.png "Testing pyramid") + +## Unit Tests +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. + +## Integration Tests +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. + +## 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. + +## TDD Cycle + +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. +3. **Refactor:** Look for opportunities to simplify, reduce duplication, or otherwise improve the code without changing any behavior—to _refactor._ +4. Repeat! + + + + + diff --git a/Unit Tests.md b/Unit Tests.md new file mode 100644 index 0000000..c495432 --- /dev/null +++ b/Unit Tests.md @@ -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 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); +} +``` + diff --git a/_Sidebar.md b/_Sidebar.md deleted file mode 100644 index eca0b8c..0000000 --- a/_Sidebar.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -gitea: none -include_toc: true ---- - -This is a test for special page called '_sidebar' - -# [[Home]] -# subfolder -## Note1 -# [[testPage]] \ No newline at end of file diff --git a/subfolder/Note1.md b/subfolder/Note1.md deleted file mode 100644 index e6b2a04..0000000 --- a/subfolder/Note1.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/testPage.md b/testPage.md deleted file mode 100644 index 1e782cb..0000000 --- a/testPage.md +++ /dev/null @@ -1 +0,0 @@ -This wiki page is on the same level as [[Home]] page \ No newline at end of file