Skip to content

Commit

Permalink
Add docs for test module (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
makiftutuncu authored Feb 28, 2023
1 parent 5234838 commit 5935ab8
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 68 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ spring-boot-crud is a library to help build CRUD applications easily with Spring

| Latest Version | Java Version | Kotlin Version | Spring Boot Version |
| -------------- | ------------ | -------------- | ------------------- |
| 0.3.3 | 17 | 1.8.0 | 3.0.3 |
| 0.3.4 | 17 | 1.8.0 | 3.0.3 |

## Table of Contents

Expand All @@ -23,8 +23,8 @@ spring-boot-crud consists of 2 modules. You can click on a module for more infor

| Name | Details | Documentation |
| --------------------------------------- | -------------------- | ------------------------------------------------------------ |
| [spring-boot-crud-api](api/README.md) | API implementations | [![](https://img.shields.io/badge/docs-0.3.3-brightgreen.svg?style=for-the-badge&logo=kotlin&color=0095d5&labelColor=333333)](https://javadoc.io/doc/dev.akif/spring-boot-crud-api) |
| [spring-boot-crud-test](test/README.md) | Test implementations | [![](https://img.shields.io/badge/docs-0.3.3-brightgreen.svg?style=for-the-badge&logo=kotlin&color=0095d5&labelColor=333333)](https://javadoc.io/doc/dev.akif/spring-boot-crud-test) |
| [spring-boot-crud-api](api/README.md) | API implementations | [![](https://img.shields.io/badge/docs-0.3.4-brightgreen.svg?style=for-the-badge&logo=kotlin&color=0095d5&labelColor=333333)](https://javadoc.io/doc/dev.akif/spring-boot-crud-api) |
| [spring-boot-crud-test](test/README.md) | Test implementations | [![](https://img.shields.io/badge/docs-0.3.4-brightgreen.svg?style=for-the-badge&logo=kotlin&color=0095d5&labelColor=333333)](https://javadoc.io/doc/dev.akif/spring-boot-crud-test) |

## Examples

Expand Down
8 changes: 4 additions & 4 deletions api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ For Gradle with Kotlin DSL, add following to `build.gradle.kts`:

```kotlin
dependencies {
implementation('dev.akif:spring-boot-crud-api:0.3.3')
implementation('dev.akif:spring-boot-crud-api:0.3.4')
}
```
For Gradle, add following to `build.gradle`:

```kotlin
dependencies {
implementation 'dev.akif:spring-boot-crud-api:0.3.3'
implementation 'dev.akif:spring-boot-crud-api:0.3.4'
}
```
For Maven, add following to your `pom.xml`:
Expand All @@ -28,7 +28,7 @@ For Maven, add following to your `pom.xml`:
<dependency>
<groupId>dev.akif</groupId>
<artifactId>spring-boot-crud-api</artifactId>
<version>0.3.3</version>
<version>0.3.4</version>
</dependency>
</dependencies>
```
Expand Down Expand Up @@ -515,4 +515,4 @@ class CatController(service: CatService, mapper: CatMapper): CRUDController<UUID

The default configuration of Swagger UI is at `/swagger-ui.html` so if you navigate to that after running your application, you should have access to your generated API documentation.

![](../assets/swagger-ui.png)
![](../assets/swagger-ui.png)
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {

allprojects {
group = "dev.akif"
version = "0.3.3"
version = "0.3.4"

apply(plugin = "org.jetbrains.dokka")
}
Expand Down
210 changes: 208 additions & 2 deletions test/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,210 @@

# spring-boot-crud-test

TODO
This is the test module of spring-boot-crud. It brings some test utilities and base classes for testing your CRUD applications built with spring-boot-crud-api. See [Contents](#contents) section for details of each class it provides.

## Getting Started

To get started, add spring-boot-crud-test as a test dependency to your project.

For Gradle with Kotlin DSL, add following to `build.gradle.kts`:

```kotlin
dependencies {
testImplementation('dev.akif:spring-boot-crud-test:0.3.4')
}
```

For Gradle, add following to `build.gradle`:

```kotlin
dependencies {
testImplementation 'dev.akif:spring-boot-crud-test:0.3.4'
}
```

For Maven, add following to your `pom.xml`:

```xml
<dependencies>
<dependency>
<groupId>dev.akif</groupId>
<artifactId>spring-boot-crud-test</artifactId>
<version>0.3.4</version>
<scope>test</scope>
</dependency>
</dependencies>
```

## Contents

There are two main things spring-boot-crud-test provides: a test environment with utilities to be used in tests and base test classes which give you test cases for free, the same way spring-boot-crud-api gives you an API for free.

### 1. Testing Environment

Setting up a proper testing environment can be hard. In tests, especially in unit tests, you'll need mock/dummy/custom implementations of certain components. spring-boot-crud-test tries to make it easier for you to write tests for your applications by providing a few of those.

For the remainder of this document, we'll follow the example from [spring-boot-crud-api](../api/README.md) documentation so we'll add tests to our cats API.

#### 1.1. [AdjustableInstantProvider](src/main/kotlin/dev/akif/crud/AdjustableInstantProvider.kt)

There is an [InstantProvider](../api/src/main/kotlin/dev/akif/crud/common/InstantProvider.kt) component used for getting the current `java.time.Instant`. `AdjustableInstantProvider` is a custom implementation of this that can be used in tests. It provides:

* A constant time so every time `now` is called, the same `Instant` is returned
* An `adjust` method to modify the time so the passage of time can be simulated in tests
* A `reset` method to reset the modifications made to the time

#### 1.2. [CRUDTestData](src/main/kotlin/dev/akif/crud/CRUDTestData.kt)

This lets you organize your test data and gives you some test utilities. Things to highlight are:

* Instances of `InMemoryCRUDRepository` and `AdjustableInstantProvider` are created for you.
* You're required to have 3 different instances of your entity as test data (namely `testEntity1`, `testEntity2` and `testEntity3`). This is to have a minimal setup allowing pagination. You can always define more by setting the `moreTestEntities` array.
* Since there are entities and entities are mutable by nature, there is a `copy` method so a copy of an entity can be created. This is useful in creating expected cases in assertions.
* There is a `randomId` method to generate a random id of your entity's id type.
* There is an `areDuplicates` method for defining the uniqueness condition of your entities.
* There are `xToY` methods for mapping between different versions of an entity (with or without some modifications). This is useful when testing multiple layers.

A test data class for cats could be implemented as:

```kotlin
import dev.akif.crud.CRUDTestData
import java.util.UUID

class CatTestData : CRUDTestData<UUID, CatEntity, Cat, CreateCat, UpdateCat, CatTestData>(typeName = "Cat") {
private val catId1 = UUID.randomUUID()
private val catId2 = UUID.randomUUID()
private val catId3 = UUID.randomUUID()

override val testEntity1: CatEntity =
CatEntity(
id = catId1,
name = "Cookie",
breed = "Tabby",
age = 4,
version = 0,
createdAt = now,
updatedAt = now,
deletedAt = null
)

override val testEntity2: CatEntity =
CatEntity(
id = catId2,
name = "Kitty",
breed = "Persian",
age = 3,
version = 0,
createdAt = now.plusSeconds(1),
updatedAt = now.plusSeconds(1),
deletedAt = null
)

override val testEntity3: CatEntity =
CatEntity(
id = catId3,
name = "Meowth",
breed = "Scottish Fold",
age = 2,
version = 0,
createdAt = now.plusSeconds(2),
updatedAt = now.plusSeconds(2),
deletedAt = null
)

override val moreTestEntities: Array<CatEntity> =
emptyArray()

override fun areDuplicates(e1: CatEntity, e2: CatEntity): Boolean =
e1.name == e2.name
&& e1.breed == e2.breed
&& e1.age == e2.age

override fun copy(entity: CatEntity): CatEntity =
CatEntity(
id = entity.id,
name = entity.name,
breed = entity.breed,
age = entity.age,
version = entity.version,
createdAt = entity.createdAt,
updatedAt = entity.updatedAt,
deletedAt = entity.deletedAt
)

override fun randomId(): UUID =
UUID.randomUUID()

override fun entityToCreateModel(entity: CatEntity): CreateCat =
CreateCat(
name = entity.name!!,
breed = entity.breed!!,
age = entity.age!!
)

override fun entityToUpdateModelWithModifications(entity: CatEntity): UpdateCat =
UpdateCat(
name = "${entity.name}-updated",
age = entity.age?.plus(1) ?: 1
)

override fun entityToUpdateModelWithNoModifications(entity: CatEntity): UpdateCat =
UpdateCat(
name = entity.name!!,
age = entity.age!!
)
}
```

#### 1.3. [InMemoryCRUDRepository](src/main/kotlin/dev/akif/crud/InMemoryCRUDRepository.kt)

This is a map-based in-memory implementation of [CRUDRepository](../api/src/main/kotlin/dev/akif/crud/CRUDRepository.kt) to use in tests. It implements the repository methods the same way a real database. It supports having initial entities and `clean`/`reset` for specific test cases.

### 2. Unit Tests

Unit tests are fundamental in software testing. While writing unit tests, we focus on a single unit. In a CRUD application, this would entail different components at different layers. spring-boot-crud-test provides base classes for such tests.

#### 2.1. [CRUDServiceTest](src/main/kotlin/dev/akif/crud/CRUDServiceTest.kt)

This is a base class for the unit test of a service class. It provides the required set-up and following test cases for free:

* creating new entities
* should fail with already exists error when trying to create an entity that already exists
* should create a new entity with the same data of a deleted entity and return it
* should create a new entity and return it
* getting all entities
* should return at least 3 entities with default pagination
* should not return deleted entities
* should return correct entities with pagination
* should return empty page when no entities exist
* getting an entity
* should return null when trying to get a deleted entity
* should return correct entity
* should return null when trying to get an entity that doesn't exist
* updating an entity
* should fail with already exists error when trying to update an entity as duplicate of another entity
* should fail with not found error when trying to update an entity that doesn't exist
* should update return updated entity
* should update with the same data of a deleted entity return updated entity
* deleting an entity
* should fail with not found error when trying to delete an entity that doesn't exist
* should delete

A unit test for `CatService` could be implemented as:

```kotlin
import dev.akif.crud.CRUDServiceTest
import org.junit.jupiter.api.DisplayName
import java.util.UUID

@DisplayName("CatService")
class CatServiceTest : CRUDServiceTest<UUID, CatEntity, Cat, CreateCat, UpdateCat, CatMapper, CatRepository, CatService, CatTestData>(
typeName = "Cat",
mapper = CatMapper(),
testData = CatTestData()
) {
override fun buildService(mapper: CatMapper, testData: CatTestData): CatService =
CatService(testData.instantProvider, testData.repository, mapper)
}
```

Please note that you can use given dependencies `mapper` and `testData` to build `CatService`. These are the dependencies `CRUDServiceTest` builds for you internally, which are reset for every test case.
Loading

0 comments on commit 5935ab8

Please sign in to comment.