feat: added notification pattern (#2629)

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
This commit is contained in:
sugavanesh
2024-03-09 18:16:46 +05:30
committed by GitHub
parent b2ca49a4e5
commit 249efd1e71
17 changed files with 849 additions and 0 deletions
+251
View File
@@ -0,0 +1,251 @@
title: Notification
category: Behavioural
language: en
tags:
- Decoupling
- Presentation
- Domain
---
## Intent
To capture information about errors and other information that occurs in the domain layer
which acts as the internal logic and validation tool. The notification then communicates this information
back to the presentation for handling and displaying to the user what errors have occurred and why.
## Explanation
Real world example
> You need to register a worker for your company. The information submitted needs to be valid
> before the worker can be added to the database, and if there are any errors you need to know about them.
In plain words
> A notification is simply a way of collecting information about errors and communicating it to the user
> so that they are aware of them
**Programatic example**
Building off the example of registering a worker for a company, we can now explore a coded example for a full picture
of how this pattern looks when coded up. For full code please visit the github repository.
To begin with, the user submits information to a form through the presentation layer of our software. The information
given to register a worker is the worker's name, occupation, and date of birth. The program will then make sure none of
these fields are blank (validation) and that the worker is over 18 years old. If there are any errors,
the program will inform the user.
The code for the form is given below. This form acts as our presentation layer, taking input from the user and printing
output using a LOGGER (in this case). The form then gives this information to the service layer RegisterWorkerService
through a data transfer object (DTO).
The service handles information validation and the presentation can then check the notification stored within the DTO for
any errors and display them to the user if necessary. Otherwise, it will inform the user the submission was processed
successfully.
form:
```java
/**
* The form submitted by the user, part of the presentation layer,
* linked to the domain layer through a data transfer object and
* linked to the service layer directly.
*/
@Slf4j
public class RegisterWorkerForm {
String name;
String occupation;
LocalDate dateOfBirth;
RegisterWorkerDto worker;
/**
* Service super type which the form uses as part of its service layer.
*/
RegisterWorkerService service = new RegisterWorkerService();
/**
* Creates the form.
*
* @param name name of worker
* @param occupation occupation of the worker
* @param dateOfBirth date of birth of the worker
*/
public RegisterWorkerForm(String name, String occupation, LocalDate dateOfBirth) {
this.name = name;
this.occupation = occupation;
this.dateOfBirth = dateOfBirth;
}
/**
* Attempts to submit the form for registering a worker.
*/
public void submit() {
//Save worker information (like name, occupation, dob) to our transfer object to be communicated between layers
saveToWorker();
//call the service layer to register our worker
service.registerWorker(worker);
//check for any errors
if (worker.getNotification().hasErrors()) {
indicateErrors(); //displays errors to users
LOGGER.info("Not registered, see errors");
} else {
LOGGER.info("Registration Succeeded");
}
}
...
}
```
The data transfer object (DTO) created stores the information submitted (name, occupation, date of birth), as well as
information on the notification after this data has been validated (stored in the DTO class it extends).
This acts as the link between the service layer and our domain layer which runs the internal logic.
It also holds information on the error types created.
DTO:
```java
/**
* Data transfer object which stores information about the worker. This is carried between
* objects and layers to reduce the number of method calls made.
*/
@Getter
@Setter
public class RegisterWorkerDto extends DataTransferObject {
private String name;
private String occupation;
private LocalDate dateOfBirth;
/**
* Error for when name field is blank or missing.
*/
public static final NotificationError MISSING_NAME =
new NotificationError(1, "Name is missing");
/**
* Error for when occupation field is blank or missing.
*/
public static final NotificationError MISSING_OCCUPATION =
new NotificationError(2, "Occupation is missing");
/**
* Error for when date of birth field is blank or missing.
*/
public static final NotificationError MISSING_DOB =
new NotificationError(3, "Date of birth is missing");
/**
* Error for when date of birth is less than 18 years ago.
*/
public static final NotificationError DOB_TOO_SOON =
new NotificationError(4, "Worker registered must be over 18");
protected RegisterWorkerDto() {
super();
}
...
}
```
These errors are stored within a simple wrapper class called NotificationError.
Our service layer (RegisterWorkerService) represents the framework of our service layer. Currently, it will
run the commands necessary to validate our Java object without handling any of the internal logic itself,
passing on the work, along with our DTO, to the domain layer.
This validation itself is done in RegisterWorker which works within our domain layer. ServerCommand acts as
a SuperType here for the domain and holds any DTOs needed. If it passes validation, our worker is then added into
the database as submission was successful!
validation:
```java
/**
* Handles internal logic and validation for worker registration.
* Part of the domain layer which collects information and sends it back to the presentation.
*/
@Slf4j
public class RegisterWorker extends ServerCommand {
protected RegisterWorker(RegisterWorkerDto worker) {
super(worker);
}
/**
* Validates the data provided and adds it to the database in the backend.
*/
public void run() {
//make sure the information submitted is valid
validate();
if (!super.getNotification().hasErrors()) {
//Add worker to system in backend (not implemented here)
LOGGER.info("Register worker in backend system");
}
}
/**
* Validates our data. Checks for any errors and if found, stores them in our notification.
*/
private void validate() {
var ourData = ((RegisterWorkerDto) this.data);
//check if any of submitted data is not given
failIfNullOrBlank(ourData.getName(), RegisterWorkerDto.MISSING_NAME);
failIfNullOrBlank(ourData.getOccupation(), RegisterWorkerDto.MISSING_OCCUPATION);
failIfNullOrBlank(ourData.getDateOfBirth().toString(), RegisterWorkerDto.MISSING_DOB);
//only if DOB is not blank, then check if worker is over 18 to register.
if (!super.getNotification().getErrors().contains(RegisterWorkerDto.MISSING_DOB)) {
var dateOfBirth = ourData.getDateOfBirth();
var current = now().minusYears(18);
fail(dateOfBirth.compareTo(current) > 0, RegisterWorkerDto.DOB_TOO_SOON);
}
}
...
}
```
After all of this explanation, we can then simulate the following inputs into the form and submit them:
input:
```java
/**
* Variables to be submitted in the form.
*/
private static final String NAME = "";
private static final String OCCUPATION = "";
private static final LocalDate DATE_OF_BIRTH = LocalDate.of(2016, 7, 13);
RegisterWorkerForm form = new RegisterWorkerForm(NAME, OCCUPATION, DATE_OF_BIRTH);
form.submit();
```
The form then processes the submission and returns these error messages to the user, showing our notification worked.
output:
```java
18:10:00.075 [main] INFO com.iluwater.RegisterWorkerForm - Error 1: Name is missing: ""
18:10:00.079 [main] INFO com.iluwater.RegisterWorkerForm - Error 2: Occupation is missing: ""
18:10:00.079 [main] INFO com.iluwater.RegisterWorkerForm - Error 4: Worker registered must be over 18: "2016-07-13"
18:10:00.080 [main] INFO com.iluwater.RegisterWorkerForm - Not registered, see errors
```
## Class diagram
![alt text](./etc/notification.urm.png "Notification")
## Applicability
Use the notification pattern when:
* You wish to communicate information about errors between the domain layer and the presentation layer. This is most applicable when a seperated presentation pattern is being used as this does not allow for direct communication between the domain and presentation.
## Related patterns
* [Service Layer](https://java-design-patterns.com/patterns/service-layer/)
* [Data Transfer Object](https://java-design-patterns.com/patterns/data-transfer-object/)
* [Domain Model](https://java-design-patterns.com/patterns/domain-model/)
* [Remote Facade](https://martinfowler.com/eaaCatalog/remoteFacade.html)
* [Autonomous View](https://martinfowler.com/eaaDev/AutonomousView.html)
* [Layer Supertype](https://martinfowler.com/eaaCatalog/layerSupertype.html)
* [Separated Presentation](https://java-design-patterns.com/patterns/data-transfer-object/)
## Credits
* [Martin Fowler - Notification Pattern](https://martinfowler.com/eaaDev/Notification.html)
Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

+89
View File
@@ -0,0 +1,89 @@
@startuml
package com.iluwatar {
class App {
- DATE_OF_BIRTH : LocalDate {static}
- NAME : String {static}
- OCCUPATION : String {static}
- App()
+ main(args : String[]) {static}
}
class DataTransferObject {
# notification : Notification
+ DataTransferObject()
+ getNotification() : Notification
}
class Notification {
- errors : List<NotificationError>
+ Notification()
+ addError(error : NotificationError)
+ getErrors() : List<NotificationError>
+ hasErrors() : boolean
}
class NotificationError {
- errorId : int
- errorMessage : String
+ NotificationError(errorId : int, errorMessage : String)
+ getErrorId() : int
+ getErrorMessage() : String
+ toString() : String
}
class RegisterWorker {
- LOGGER : Logger {static}
# RegisterWorker(worker : RegisterWorkerDto)
# fail(condition : boolean, error : NotificationError)
# failIfNullOrBlank(s : String, error : NotificationError)
# isNullOrBlank(s : String) : boolean
+ run()
- validate()
}
class RegisterWorkerDto {
+ DOB_TOO_SOON : NotificationError {static}
+ MISSING_DOB : NotificationError {static}
+ MISSING_NAME : NotificationError {static}
+ MISSING_OCCUPATION : NotificationError {static}
- dateOfBirth : LocalDate
- name : String
- occupation : String
# RegisterWorkerDto()
+ getDateOfBirth() : LocalDate
+ getName() : String
+ getOccupation() : String
+ setDateOfBirth(dateOfBirth : LocalDate)
+ setName(name : String)
+ setOccupation(occupation : String)
+ setupWorkerDto(name : String, occupation : String, dateOfBirth : LocalDate)
}
class RegisterWorkerForm {
- LOGGER : Logger {static}
~ dateOfBirth : LocalDate
~ name : String
~ occupation : String
~ service : RegisterWorkerService
~ worker : RegisterWorkerDto
+ RegisterWorkerForm(name : String, occupation : String, dateOfBirth : LocalDate)
- checkError(error : NotificationError, info : String)
- indicateErrors()
- saveToWorker()
~ showError(info : String, message : String)
+ submit()
}
class RegisterWorkerService {
+ RegisterWorkerService()
+ registerWorker(registration : RegisterWorkerDto)
}
class ServerCommand {
# data : DataTransferObject
+ ServerCommand(data : DataTransferObject)
+ getNotification() : Notification
}
}
Notification --> "-errors" NotificationError
DataTransferObject --> "-notification" Notification
RegisterWorkerForm --> "-service" RegisterWorkerService
RegisterWorkerForm --> "-worker" RegisterWorkerDto
ServerCommand --> "-data" DataTransferObject
RegisterWorkerDto --> "-MISSING_DOB" NotificationError
RegisterWorkerDto --> "-MISSING_NAME" NotificationError
RegisterWorker --|> ServerCommand
RegisterWorkerDto --|> DataTransferObject
@enduml
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<artifactId>notification</artifactId>
</project>
@@ -0,0 +1,26 @@
package com.iluwatar;
import java.time.LocalDate;
/**
* The notification pattern captures information passed between layers, validates the information, and returns
* any errors to the presentation layer if needed.
*
* <p>In this code, this pattern is implemented through the example of a form being submitted to register
* a worker. The worker inputs their name, occupation, and date of birth to the RegisterWorkerForm (which acts
* as our presentation layer), and passes it to the RegisterWorker class (our domain layer) which validates it.
* Any errors caught by the domain layer are then passed back to the presentation layer through the
* RegisterWorkerDto.</p>
*/
public class App {
private static final String NAME = "";
private static final String OCCUPATION = "";
private static final LocalDate DATE_OF_BIRTH = LocalDate.of(2016, 7, 13);
public static void main(String[] args) {
var form = new RegisterWorkerForm(NAME, OCCUPATION, DATE_OF_BIRTH);
form.submit();
}
}
@@ -0,0 +1,16 @@
package com.iluwatar;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* Layer super type for all Data Transfer Objects.
* Also contains code for accessing our notification.
*/
@Getter
@NoArgsConstructor
public class DataTransferObject {
private final Notification notification = new Notification();
}
@@ -0,0 +1,26 @@
package com.iluwatar;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* The notification. Used for storing errors and any other methods
* that may be necessary for when we send information back to the
* presentation layer.
*/
@Getter
@NoArgsConstructor
public class Notification {
private final List<NotificationError> errors = new ArrayList<>();
public boolean hasErrors() {
return !this.errors.isEmpty();
}
public void addError(NotificationError error) {
this.errors.add(error);
}
}
@@ -0,0 +1,20 @@
package com.iluwatar;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Error class for storing information on the error.
* Error ID is not necessary, but may be useful for serialisation.
*/
@Getter
@AllArgsConstructor
public class NotificationError {
private int errorId;
private String errorMessage;
@Override
public String toString() {
return "Error " + errorId + ": " + errorMessage;
}
}
@@ -0,0 +1,79 @@
package com.iluwatar;
import java.time.LocalDate;
import java.time.Period;
import lombok.extern.slf4j.Slf4j;
/**
* Class which handles actual internal logic and validation for worker registration.
* Part of the domain layer which collects information and sends it back to the presentation.
*/
@Slf4j
public class RegisterWorker extends ServerCommand {
static final int LEGAL_AGE = 18;
protected RegisterWorker(RegisterWorkerDto worker) {
super(worker);
}
/**
* Validates the data provided and adds it to the database in the backend.
*/
public void run() {
validate();
if (!super.getNotification().hasErrors()) {
LOGGER.info("Register worker in backend system");
}
}
/**
* Validates our data. Checks for any errors and if found, adds to notification.
*/
private void validate() {
var ourData = ((RegisterWorkerDto) this.data);
//check if any of submitted data is not given
// passing for empty value validation
fail(isNullOrBlank(ourData.getName()), RegisterWorkerDto.MISSING_NAME);
fail(isNullOrBlank(ourData.getOccupation()), RegisterWorkerDto.MISSING_OCCUPATION);
fail(isNullOrBlank(ourData.getDateOfBirth()), RegisterWorkerDto.MISSING_DOB);
if (isNullOrBlank(ourData.getDateOfBirth())) {
// If DOB is null or empty
fail(true, RegisterWorkerDto.MISSING_DOB);
} else {
// Validating age ( should be greater than or equal to 18 )
Period age = Period.between(ourData.getDateOfBirth(), LocalDate.now());
fail(age.getYears() < LEGAL_AGE, RegisterWorkerDto.DOB_TOO_SOON);
}
}
/**
* Validates for null/empty value.
*
* @param obj any object
* @return boolean
*/
protected boolean isNullOrBlank(Object obj) {
if (obj == null) {
return true;
}
if (obj instanceof String) {
return ((String) obj).trim().isEmpty();
}
return false;
}
/**
* If a condition is met, adds the error to our notification.
*
* @param condition condition to check for.
* @param error error to add if condition met.
*/
protected void fail(boolean condition, NotificationError error) {
if (condition) {
super.getNotification().addError(error);
}
}
}
@@ -0,0 +1,59 @@
package com.iluwatar;
import java.time.LocalDate;
import lombok.Getter;
import lombok.Setter;
/**
* Data transfer object which stores information about the worker. This is carried between
* objects and layers to reduce the number of method calls made.
*/
@Getter
@Setter
public class RegisterWorkerDto extends DataTransferObject {
private String name;
private String occupation;
private LocalDate dateOfBirth;
/**
* Error for when name field is blank or missing.
*/
public static final NotificationError MISSING_NAME =
new NotificationError(1, "Name is missing");
/**
* Error for when occupation field is blank or missing.
*/
public static final NotificationError MISSING_OCCUPATION =
new NotificationError(2, "Occupation is missing");
/**
* Error for when date of birth field is blank or missing.
*/
public static final NotificationError MISSING_DOB =
new NotificationError(3, "Date of birth is missing");
/**
* Error for when date of birth is less than 18 years ago.
*/
public static final NotificationError DOB_TOO_SOON =
new NotificationError(4, "Worker registered must be over 18");
protected RegisterWorkerDto() {
super();
}
/**
* Simple set up function for capturing our worker information.
*
* @param name Name of the worker
* @param occupation occupation of the worker
* @param dateOfBirth Date of Birth of the worker
*/
public void setupWorkerDto(String name, String occupation, LocalDate dateOfBirth) {
this.name = name;
this.occupation = occupation;
this.dateOfBirth = dateOfBirth;
}
}
@@ -0,0 +1,66 @@
package com.iluwatar;
import java.time.LocalDate;
import lombok.extern.slf4j.Slf4j;
/**
* The form submitted by the user, part of the presentation layer,
* linked to the domain layer through a data transfer object and
* linked to the service layer directly.
*/
@Slf4j
public class RegisterWorkerForm {
String name;
String occupation;
LocalDate dateOfBirth;
RegisterWorkerDto worker;
RegisterWorkerService service = new RegisterWorkerService();
/**
* Constructor.
*
* @param name Name of the worker
* @param occupation occupation of the worker
* @param dateOfBirth Date of Birth of the worker
*/
public RegisterWorkerForm(String name, String occupation, LocalDate dateOfBirth) {
this.name = name;
this.occupation = occupation;
this.dateOfBirth = dateOfBirth;
}
/**
* Attempts to submit the form for registering a worker.
*/
public void submit() {
//Transmit information to our transfer object to communicate between layers
saveToWorker();
//call the service layer to register our worker
service.registerWorker(worker);
//check for any errors
if (worker.getNotification().hasErrors()) {
indicateErrors();
LOGGER.info("Not registered, see errors");
} else {
LOGGER.info("Registration Succeeded");
}
}
/**
* Saves worker information to the data transfer object.
*/
private void saveToWorker() {
worker = new RegisterWorkerDto();
worker.setName(name);
worker.setOccupation(occupation);
worker.setDateOfBirth(dateOfBirth);
}
/**
* Check for any errors with form submission and show them to the user.
*/
public void indicateErrors() {
worker.getNotification().getErrors().forEach(error -> LOGGER.error(error.toString()));
}
}
@@ -0,0 +1,18 @@
package com.iluwatar;
/**
* Service used to register a worker.
* This represents the basic framework of a service layer which can be built upon.
*/
public class RegisterWorkerService {
/**
* Creates and runs a command object to do the work needed,
* in this case, register a worker in the system.
*
* @param registration worker to be registered if possible
*/
public void registerWorker(RegisterWorkerDto registration) {
var cmd = new RegisterWorker(registration);
cmd.run();
}
}
@@ -0,0 +1,21 @@
package com.iluwatar;
import lombok.AllArgsConstructor;
/**
* Stores the dto and access the notification within it.
* Acting as a layer supertype in this instance for the domain layer.
*/
@AllArgsConstructor
public class ServerCommand {
protected DataTransferObject data;
/**
* Basic getter to extract information from our data.
*
* @return the notification stored within the data
*/
public Notification getNotification() {
return data.getNotification();
}
}
@@ -0,0 +1,14 @@
package com.iluwatar;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
class AppTest {
@Test
void shouldExecuteApplicationWithoutException() {
assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
@@ -0,0 +1,53 @@
package com.iluwatar;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.*;
class RegisterWorkerFormTest {
private RegisterWorkerForm registerWorkerForm;
@Test
void submitSuccessfully() {
// Ensure the worker is null initially
registerWorkerForm = new RegisterWorkerForm("John Doe", "Engineer", LocalDate.of(1990, 1, 1));
assertNull(registerWorkerForm.worker);
// Submit the form
registerWorkerForm.submit();
// Verify that the worker is not null after submission
assertNotNull(registerWorkerForm.worker);
// Verify that the worker's properties are set correctly
assertEquals("John Doe", registerWorkerForm.worker.getName());
assertEquals("Engineer", registerWorkerForm.worker.getOccupation());
assertEquals(LocalDate.of(1990, 1, 1), registerWorkerForm.worker.getDateOfBirth());
}
@Test
void submitWithErrors() {
// Set up the worker with a notification containing errors
registerWorkerForm = new RegisterWorkerForm(null, null, null);
// Submit the form
registerWorkerForm.submit();
// Verify that the worker's properties remain unchanged
assertNull(registerWorkerForm.worker.getName());
assertNull(registerWorkerForm.worker.getOccupation());
assertNull(registerWorkerForm.worker.getDateOfBirth());
// Verify the presence of errors
assertEquals(registerWorkerForm.worker.getNotification().getErrors().size(), 4);
}
}
@@ -0,0 +1,90 @@
package com.iluwatar;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
import static org.junit.jupiter.api.Assertions.*;
@Slf4j
class RegisterWorkerTest {
@Test
void runSuccessfully() {
RegisterWorkerDto validWorkerDto = createValidWorkerDto();
validWorkerDto.setupWorkerDto("name", "occupation", LocalDate.of(2000, 12, 1));
RegisterWorker registerWorker = new RegisterWorker(validWorkerDto);
// Run the registration process
registerWorker.run();
// Verify that there are no errors in the notification
assertFalse(registerWorker.getNotification().hasErrors());
}
@Test
void runWithMissingName() {
RegisterWorkerDto workerDto = createValidWorkerDto();
workerDto.setupWorkerDto(null, "occupation", LocalDate.of(2000, 12, 1));
RegisterWorker registerWorker = new RegisterWorker(workerDto);
// Run the registration process
registerWorker.run();
// Verify that the notification contains the missing name error
assertTrue(registerWorker.getNotification().hasErrors());
assertTrue(registerWorker.getNotification().getErrors().contains(RegisterWorkerDto.MISSING_NAME));
assertEquals(registerWorker.getNotification().getErrors().size(), 1);
}
@Test
void runWithMissingOccupation() {
RegisterWorkerDto workerDto = createValidWorkerDto();
workerDto.setupWorkerDto("name", null, LocalDate.of(2000, 12, 1));
RegisterWorker registerWorker = new RegisterWorker(workerDto);
// Run the registration process
registerWorker.run();
// Verify that the notification contains the missing occupation error
assertTrue(registerWorker.getNotification().hasErrors());
assertTrue(registerWorker.getNotification().getErrors().contains(RegisterWorkerDto.MISSING_OCCUPATION));
assertEquals(registerWorker.getNotification().getErrors().size(), 1);
}
@Test
void runWithMissingDOB() {
RegisterWorkerDto workerDto = createValidWorkerDto();
workerDto.setupWorkerDto("name", "occupation", null);
RegisterWorker registerWorker = new RegisterWorker(workerDto);
// Run the registration process
registerWorker.run();
// Verify that the notification contains the missing DOB error
assertTrue(registerWorker.getNotification().hasErrors());
assertTrue(registerWorker.getNotification().getErrors().contains(RegisterWorkerDto.MISSING_DOB));
assertEquals(registerWorker.getNotification().getErrors().size(), 2);
}
@Test
void runWithUnderageDOB() {
RegisterWorkerDto workerDto = createValidWorkerDto();
workerDto.setDateOfBirth(LocalDate.now().minusYears(17)); // Under 18
workerDto.setupWorkerDto("name", "occupation", LocalDate.now().minusYears(17));
RegisterWorker registerWorker = new RegisterWorker(workerDto);
// Run the registration process
registerWorker.run();
// Verify that the notification contains the underage DOB error
assertTrue(registerWorker.getNotification().hasErrors());
assertTrue(registerWorker.getNotification().getErrors().contains(RegisterWorkerDto.DOB_TOO_SOON));
assertEquals(registerWorker.getNotification().getErrors().size(), 1);
}
private RegisterWorkerDto createValidWorkerDto() {
return new RegisterWorkerDto();
}
}
+1
View File
@@ -210,6 +210,7 @@
<module>crtp</module>
<module>log-aggregation</module>
<module>health-check</module>
<module>notification</module>
<module>single-table-inheritance</module>
</modules>
<repositories>