Skip to content

Commit

Permalink
Day 21: Changing tides.
Browse files Browse the repository at this point in the history
  • Loading branch information
yanncourtel committed Dec 21, 2023
1 parent d4e2152 commit 057f507
Show file tree
Hide file tree
Showing 11 changed files with 242 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Here are the different challenges :
- [Day 18: Automatically detect Linguistic Anti-Patterns (LAP).](exercise/day18/docs/challenge.md)
- [Day 19: Loosing up dead weight.](exercise/day19/docs/challenge.md)
- [Day 20: No more exceptions in our domain.](exercise/day20/docs/challenge.md)
- [Day 21: Refactor the tests and production code to Output-based tests.](exercise/day21/docs/challenge.md)

### Solutions
A solution proposal will be published here every day during the `Advent Of Craft` containing `the code` and a `step by step` guide.
Expand Down
113 changes: 113 additions & 0 deletions exercise/day21/docs/challenge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
## Day 21: Changing tides.

Today, you are still fighting and while the main storm
passed you are still not out of danger yet!

You will need to reach the nearby island quickly to recover
since your ship is badly broken.

Today's exercise is about fixing tests and fixing code.

A refactoring is needed but a first step needs to be made
in the tests.

> **Challenge of day 21: Refactor the tests and production code to Output-based tests.**
Before refactoring the code, here are some explanations regarding the different kind of tests as explained by Vladimir
Khorikov in his book [Unit Testing Principles, Practices and Patterns.](https://www.manning.com/books/unit-testing).

### Different styles of tests

#### State-Based

```java
class StateBasedTests {
@Test
void it_should_add_given_product_to_the_order() {
val product = new Product("Free Guy");
val sut = new Order();

sut.add(product);

// Verify the state
assertThat(sut.getProducts())
.hasSize(1)
.allMatch(item -> item.equals(product));
}

@AllArgsConstructor
class Product {
private final String name;
}

class Order {
private final List<Product> products = new ArrayList<>();

List<Product> getProducts() {
return Collections.unmodifiableList(products);
}

void add(Product product) {
products.add(product);
}
}
}
```

![State-Based](img/state-based.png)

#### Output-Based

```java
class OutputBasedTests {
@Test
void discount_of_2_products_should_be_2_percent() {
val product1 = new Product("Kaamelott");
val product2 = new Product("Free Guy");

// Call on the SUT (here PriceEngine)
// No side effects -> Pure function
val discount = PriceEngine.calculateDiscount(product1, product2);

assertThat(discount).isEqualTo(0.02);
}
}
```

![Output-Based](img/output-based.png)

#### Communication-Based

```java
class CommunicationBasedTests {
@Test
void greet_a_user_should_send_an_email_to_it() {
final var email = "john.doe@email.com";
final var emailGatewayMock = mock(EmailGateway.class);
// Substitute collaborators with Test Double
final var sut = new Controller(emailGatewayMock);

sut.greetUser(email);

// Verify that the SUT calls those collaborators correctly
verify(emailGatewayMock, times(1)).sendGreetingsEmail(email);
}

interface EmailGateway {
Try<String> sendGreetingsEmail(String email);
}

@AllArgsConstructor
class Controller {
private final EmailGateway emailGateway;

public Try<String> greetUser(String email) {
return emailGateway.sendGreetingsEmail(email);
}
}
}
```

![Communication-Based](img/communication-based.png)

![snippet of the day](snippet.png)
Binary file added exercise/day21/docs/img/communication-based.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added exercise/day21/docs/img/output-based.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added exercise/day21/docs/img/state-based.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added exercise/day21/docs/snippet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions exercise/day21/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.advent-of-craft</groupId>
<artifactId>advent-of-craft2023</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<artifactId>audit</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<mockito-core.version>4.8.1</mockito-core.version>
</properties>

<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito-core.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
52 changes: 52 additions & 0 deletions exercise/day21/src/main/java/audit/AuditManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package audit;

import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;

public class AuditManager {
private final int maxEntriesPerFile;
private final String directoryName;
private final FileSystem fileSystem;

public AuditManager(int maxEntriesPerFile, String directoryName, FileSystem fileSystem) {
this.maxEntriesPerFile = maxEntriesPerFile;
this.directoryName = directoryName;
this.fileSystem = fileSystem;
}

public void addRecord(String visitorName, LocalDateTime timeOfVisit) {
String[] filePaths = fileSystem.getFiles(directoryName);
String[] sorted = sortByIndex(filePaths);
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String newRecord = visitorName + ";" + timeOfVisit.format(dateTimeFormatter);

if (sorted.length == 0) {
String newFile = Paths.get(directoryName, "audit_1.txt").toString();
fileSystem.writeAllText(newFile, newRecord);
return;
}

int currentFileIndex = sorted.length - 1;
String currentFilePath = sorted[currentFileIndex];
List<String> lines = fileSystem.readAllLines(currentFilePath);

if (lines.size() < maxEntriesPerFile) {
lines.add(newRecord);
String newContent = String.join(System.lineSeparator(), lines);
fileSystem.writeAllText(currentFilePath, newContent);
} else {
String newName = "audit_" + (currentFileIndex + 2) + ".txt";
String newFile = Paths.get(directoryName, newName).toString();
fileSystem.writeAllText(newFile, newRecord);
}
}

private String[] sortByIndex(String[] filePaths) {
return Arrays.stream(filePaths)
.sorted()
.toArray(String[]::new);
}
}
11 changes: 11 additions & 0 deletions exercise/day21/src/main/java/audit/FileSystem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package audit;

import java.util.List;

public interface FileSystem {
String[] getFiles(String directoryName);

void writeAllText(String filePath, String content);

List<String> readAllLines(String filePath);
}
36 changes: 36 additions & 0 deletions exercise/day21/src/test/java/audit/AuditManagerTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package audit;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.time.LocalDateTime;
import java.util.List;

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class AuditManagerTests {
@Test
void addsNewVisitorToANewFileWhenEndOfLastFileIsReached() {
FileSystem fileSystemMock = Mockito.mock(FileSystem.class);
when(fileSystemMock.getFiles("audits"))
.thenReturn(new String[]{
"audits/audit_2.txt",
"audits/audit_1.txt"}
);
when(fileSystemMock.readAllLines("audits/audit_2.txt"))
.thenReturn(List.of(
"Peter;2019-04-06 16:30:00",
"Jane;2019-04-06 16:40:00",
"Jack;2019-04-06 17:00:00"
));

var sut = new AuditManager(3, "audits", fileSystemMock);

sut.addRecord("Alice", LocalDateTime.parse("2019-04-06T18:00:00"));

verify(fileSystemMock).writeAllText("audits/audit_3.txt", "Alice;2019-04-06 18:00:00");
}
}


1 change: 1 addition & 0 deletions exercise/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<module>day18</module>
<module>day19</module>
<module>day20</module>
<module>day21</module>
</modules>

<properties>
Expand Down

0 comments on commit 057f507

Please sign in to comment.