3

I want to learn how to write JUnit tests and I fail completely.

This is my test:

    @Test
    public void testGetAllCustomers() {
        // given
        List<Customer> customerList = new ArrayList<Customer>();
        customerList.add(c1);
        customerList.add(c2);
        customerList.add(c3);
        given(customerRepository.findAll()).willReturn(customerList);
            
        // when
        List<Customer> resultList = customerService.getAllCustomers();
        
        // then
        assertThat(resultList).hasSize(3);
    }

The problem is that this simple assertion already fails. The returned list is empty. I know, I am new to all of this, but the failure is so unexpected from my point of view that I have no approach on how to solve the problem.

This is the whole code (not that much):

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;

import java.sql.Timestamp;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class CustomerServiceTests {
    
    @Mock
    private CustomerRepository customerRepository;
    
    @InjectMocks
    private CustomerService customerService;
    
    private Customer c1 = new Customer(
            1L, 
            "Hans Meyer", 
            false, 
            Timestamp.from(Instant.now()),
            null,
            null
    );
    private Customer c2 = new Customer(
            2L, 
            "Marie Hollande", 
            true, 
            Timestamp.from(Instant.now()),
            null,
            null
    );
    private Customer c3 = new Customer(
            3L, 
            "Mohammed Abbas", 
            false, 
            Timestamp.from(Instant.now()),
            null,
            null
    );
    
    @Before
    public void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    public void testGetAllCustomers() {
        // given
        List<Customer> customerList = new ArrayList<Customer>();
        customerList.add(c1);
        customerList.add(c2);
        customerList.add(c3);
        given(customerRepository.findAll()).willReturn(customerList);
        
        // when
        List<Customer> resultList = customerService.getAllCustomers();
        
        // then
        assertThat(resultList).hasSize(3);
    }
}

The function to test is just this and I know that it works:

public List<Customer> getAllCustomers() {
        return customerRepository.findAll();
}

Actually, I just want to learn how to write such tests, but for days I have been failing to write one. There are quite a few examples and explanations, but no matter what I try, I have not yet managed a working test. How to create a working test for getAllCustomers()? Thank you for your help!

FrozenTree
  • 33
  • 3

2 Answers2

1

Here's my recommendation: you should refactor your code and apply constructor-injection style, like this:

public class CustomerService {

  private final CustomerRepository customerRepository;

  public CustomerService(CustomerRepository customerRepository) {
    this.customerRepository = customerRepository;       // inject customerRepository via constructor
  }

  // ...

}

then, you don't need any Mockito annotations. Just remove them.

public class CustomerServiceTests {

    private CustomerRepository customerRepository; 
    private CustomerService customerService;

    // ... init Customer

    @Before
    public void setup() {
      customerRepository = mock(CustomerRepository.class); // inject mocked customerRepository via constructor
      customerService = new CustomerService(customerRepository);
    }

    @Test
    public void testGetAllCustomers() {
        // same as your test code
    }

}

You can see that CustomerServiceTests becomes very simple and trivial to test.

Kai-Sheng Yang
  • 1,535
  • 4
  • 15
  • 21
0

You initialize your mocks twice - first by @RunWith(MockitoJUnitRunner.class) and then with MockitoAnnotations.openMocks(this);

This means that when you enter your setUp method:

  • customerRepository points to R1
  • customerService.customerRepository points to R1

And after this method

  • customerRepository points to R2
  • customerService.customerRepository points to R1

Which means that your service was not re-initialized

Use a debugger to confirm this observation.

@InjectMocks javadoc says:

MockitoAnnotations.openMocks(this) method has to be called to initialize annotated objects. In above example, openMocks() is called in @Before (JUnit4) method of test's base class. For JUnit3 openMocks() can go to setup() method of a base class. Instead you can also put openMocks() in your JUnit runner (@RunWith) or use the built-in MockitoJUnitRunner.

Lesiak
  • 22,088
  • 2
  • 41
  • 65
  • 1
    Thank you very much! Your explanation really helps me to understand the mechanisms better. I always try to look in the docs as well, but of course with the amount of information it's not so easy to keep track of if you're new to all of that. – FrozenTree Apr 08 '22 at 22:23