How To Unit Test Like a Pro
Tags: java, webdev, tutorial, programming
Published: 2021-05-06 20:54:15

# 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 wouldn’t 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 📦
```java
@RequiredArgsConstructor  
public class Shipment {  
   private final String productCode;  
   private final String owner;  
   private final int count;  
}
```
Shipment Repository for database operations 🗃
```java
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 🔍
```java
public class VerificationService {  
    public boolean shipmentIsntValid(String owner, String productCode, int count) {  
        return owner.isEmpty()  
                || productCode.isEmpty()  
                || count < 0;  
    }  
}
```
The Shipment service 🥋
```java
@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 ❌

```java
@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 ✅

```java
@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](https://github.com/Jarjanazy/java-tutorial/tree/main/src/main/java/unitTestTutorial)