Writing Unit Test, a simple guide

Abdelhaq ELAIBI
OCP digital factory
2 min readNov 15, 2022
Testing Pyramid

As a developer in the Digital Factory, you need to write unit tests for every feature (or method) you add, Unit testing is a must in our culture 😉

To help standardize how we write unit tests, here are some basics & good practices through a java example :

A good unit test must be FIRST :

  • Fast: The developer shouldn’t hesitate to run the unit tests at any point of their development cycle, They should run and show you the desired output in a matter of seconds
  • Independent: For any given unit test, for its environment variables, or its setup. It should be independent of everything else should so that its results are not influenced by any other factor. Generally, tests should follow the pattern: Given, when, then.
  • Repeatable: Tests should be repeatable and deterministic, their values shouldn’t change based on being run on different environments
  • Self-validating: You shouldn’t need to check manually, whether the test passed or not
  • Thorough: T stands for thorough and developers who practice test-driven development also spell it as timely. This means that when testing a function, we should consider a happy path as well as a negative scenario.

A good unit test name should explain a business rule, not a random name 😃

  • bad name: shouldCallCreate
  • good name: creatingAnomalyWithEmptyDescriptionShouldFail

Sample

package ma.ocpgroup.labs;

import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;

@Service
public class AnomalyService {
static List<String> STATUS = Arrays.asList("created", "on-doing", "fixed");


Anomaly createAnomaly(String description, LocalDate createdAt, String status) throws ConstraintViolationException {
if (isEmptyString(description)) {
throw new ConstraintViolationException("Empty description");
}
if (createdAt == null) {
throw new ConstraintViolationException("Empty creation date");
}
if (isEmptyString(status)) {
throw new ConstraintViolationException("Empty status");
}
if (!STATUS.contains(status)) {
throw new ConstraintViolationException("No matching status");
}
return new Anomaly(description, createdAt, status);
}

boolean isEmptyString(String string) {
return string == null || string.isEmpty();
}

}
package ma.ocpgroup.labs;


import org.junit.jupiter.api.Test;

import java.time.LocalDate;

import static org.junit.jupiter.api.Assertions.*;


public class AnomalyServiceTest {

AnomalyService anomalyService = new AnomalyService();


@Test
void creatingWithValidInputsShouldReturnNewAnomaly() throws ConstraintViolationException{
//given
String desc = "description de test";
LocalDate date20210412 = LocalDate.of(2021, 4, 12);
String statusInput = "created";
//when
Anomaly anomaly = anomalyService.createAnomaly(desc, date20210412, statusInput);
//then
assertNotNull(anomaly);
assertEquals(desc, anomaly.getDescription());
assertEquals(date20210412, anomaly.getCreatedAt());
assertEquals(statusInput, anomaly.getStatus());
}



@Test
void creatingWithEmptyDescriptionShouldFail(){
ConstraintViolationException thrown = assertThrows(
ConstraintViolationException.class,
() -> anomalyService.createAnomaly(null, LocalDate.now(), "going"),
"Expected to throw exception, but it didn't"
);

assertTrue(thrown.getMessage().contains("Empty description"));

}

@Test
void creatingWithNullCreationDateShouldFail(){
ConstraintViolationException thrown = assertThrows(
ConstraintViolationException.class,
() -> anomalyService.createAnomaly("description", null, "going"),
"Expected to throw exception, but it didn't"
);

assertTrue(thrown.getMessage().contains("Empty creation date"));

}

@Test
void creatingWithBadStatusShouldFail(){
ConstraintViolationException thrown = assertThrows(
ConstraintViolationException.class,
() -> anomalyService.createAnomaly("description ", LocalDate.now(), "going"),
"Expected to throw exception, but it didn't"
);

assertTrue(thrown.getMessage().contains("No matching status"));

}


}

--

--