An Interest In:
Web News this Week
- May 1, 2024
- April 30, 2024
- April 29, 2024
- April 28, 2024
- April 27, 2024
- April 26, 2024
- April 25, 2024
How To Unit Test Like a Pro
What Is Unit Testing
Testing a 'unit' in the system separately from other units.
But What Does a Unit Mean
There are a lot of different opinios regarding the answer to this question.
- Some people say a unit is a function, some say it's a class.
However, the definition I find most logical is the one describing a unit as a 'behavior' of the system.
It's a scenario that has meaning in the business logic.
For example, create a new Shipment using the client's inputs, and save them to the database.
- not really save, but fake it (mock it) we will see it a bit later
But Why Does The Behavior Description Seems The Most Logical
Tests would be closer to real-life
You wouldnt write useless tests. (for example, a useless null pointer check)
We can refactor our code without breaking tests since we test the outputs of a unit and not its inner workings
However, there are some things that we might not want to test in a unit
Like what?
Most commonly, dependencies on the outer world.
- Database connection (get me the record with this Id)
- API calls (call Twitter's API and get all my tweets)
Those dependencies we need to mock.
Mocking is defining how a dependency should act in certain conditions.
- oh dear database connection, please return this object if your x method got called with the y parameter.
Let's look at an example
We have a Shipment class
@RequiredArgsConstructor public class Shipment { private final String productCode; private final String owner; private final int count; }
Shipment Repository for database operations
public class ShipmentRepository { public Optional<Shipment> findShipment(String owner, String productCode) { // mock method return Optional.empty(); } public void save(Shipment shipment) { // mock method } }
A validation service
public class VerificationService { public boolean shipmentIsntValid(String owner, String productCode, int count) { return owner.isEmpty() || productCode.isEmpty() || count < 0; } }
The Shipment service
@RequiredArgsConstructor public class ShipmentService { private final VerificationService verificationService; private final ShipmentRepository shipmentRepository; public String createAndSaveShipment(String owner, String productCode, int count){ if (verificationService.shipmentIsntValid(owner, productCode, count)) return "Shipment isn't valid"; if (shipmentExistInDB(owner, productCode)) return "Shipment Not Found"; createNewShipment(owner, productCode, count); return "Shipment Is Created"; } private void createNewShipment(String owner, String productCode, int count) { Shipment shipment = new Shipment(owner, productCode, count); shipmentRepository.save(shipment); } public boolean shipmentExistInDB(String owner, String productCode) { return shipmentRepository.findShipment(owner, productCode).isPresent(); } }
For the unit testing, we will use Junit5 and Mockito.
Let's look at a bad example of Unit Tests
@ExtendWith(MockitoExtension.class) public class UniTestTests { @Mock ShipmentRepository shipmentRepo; @Test public void givenData_DetectValidity(){ VerificationService verificationService = new VerificationService(); boolean result = verificationService.shipmentIsntValid("jhon", "3HFF", 2); assertFalse(result); } @Test public void givenData_DetectShipmentExistsInDB(){ ShipmentService shipmentService = new ShipmentService(new VerificationService(), shipmentRepo); Shipment shipment = new Shipment("ahmed", "3HKK", 4); doReturn(Optional.of(shipment)).when(shipmentRepo).findShipment("ahmed", "3HKK"); boolean result = shipmentService.shipmentExistInDB("ahmed", "3HKK"); assertTrue(result); } }
Why are those bad unit tests?
- ShipmentService and VerificationService are two closely related methods that work together to produce a single behavior (adding a new Shipment).
- However, we are testing them in isolation, both in separate tests.
Now, Let's look at a good unit testing example
@ExtendWith(MockitoExtension.class) public class UniTestTests { @Mock ShipmentRepository shipmentRepo; @Test public void givenShipmentData_CreateNewShipment(){ VerificationService verificationService = new VerificationService(); ShipmentService shipmentService = new ShipmentService(verificationService, shipmentRepo); doReturn(Optional.empty()).when(shipmentRepo).findShipment("jalil", "3HXX"); doNothing().when(shipmentRepo).save(any()); String result = shipmentService.createAndSaveShipment("jalil", "3HXX", 10); assertEquals("Shipment Is Created", result); } }
To Sum it in Few Words
A unit test is meant to validate how feature behavior in the system.
It could be a bunch of classes and functions interacting together to produce this behavior.
Source Code on GitHub
Original Link: https://dev.to/jarjanazy/how-to-unit-test-like-a-pro-59kj
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To