0

I am trying to create a JUnit test case for application's repository class.

Repository is as follows:

    @Repository
public interface AddressRepo extends JpaRepository<SourceAddress, int>, JpaSpecificationExecutor<SourceAddress> {

    @Query(value = "select * from Address ", nativeQuery = true)
    List<Address> getAdressResults();
}

Entity class is as follows:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "ADDRESS")
public class Address implements Serializable {
   
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID", nullable = false)
    private int id;

    @Column(name = "line1")
    private String line1;

    @Column(name = "line2")
    private String line2;
}

I have tried the following but it return null pointer exception stating that repository is null:

 @Mock
Repository repository;



@Test
public void testRepo(){
    Address address = new Address();
    address.setid(1));
    address.setLine1("address line 1");

    List<Address> addressList = new ArrayList<>();
    addressList.add(address);

    Mockito.when(repository.getAddressResults()).thenReturn(addressList);
    List<Address> addresses = repository.getAddressResults();
    assertThat(addresses.get(0).getLine1().equalsIgnoreCase("address line 1"));

}

How can I test this Repository?

Thanks

  • 4
    if you mock the class, you cannot test it. Therefore, you should try a different approach. One option would be to use an in-memory database such as H2: https://www.baeldung.com/spring-jpa-test-in-memory-database. alternatively, you can use test containers, but they require a more complex setup: https://www.testcontainers.org/ – Emanuel Trandafir Apr 14 '23 at 10:18
  • Which test runner are you using? But if you mock your class, you are no longer testing your class – you are testing the mock. The test as shown does not test anything (well, except that methods stubbed by Mockito return the stubbed value). Related: https://stackoverflow.com/questions/74027324/why-are-my-mocked-methods-not-called-when-executing-a-unit-test – knittl Apr 14 '23 at 13:02

2 Answers2

1

As rightly pointed out in comments - you're mocking the class you're trying to test, which defeats the whole idea of testing in the first place.

There're multiple ways and philosophies how to approach testing, but leaving the philosophical discussions aside, in the case of testing the repository what you're actually trying to test is that:

  1. The data is saved in the data store of choice, say a relational database.
  2. The query for the data is correct and returns the expected value(s).

Thus, having a mocked instance of the test subject does not make sense.

Having said that, if your environment permits, I'd go with Testcontainers. They have the advantage, that you can test your code against the actual database process of the DB of your choice. E.g. assuming you're using Postgres Database, the test code could look like this:

// imports and packaging omitted for brevity
@SpringBootTest(
  classes = {AddressRepo.class}
)
@Testcontainers
class AddressRepoTest {
  
  private static final PostgreSQLContainer<?> POSTGRES_SQL_CONTAINER =
      new PostgreSQLContainer<>("postgres:13.4");
  
  @BeforeAll
  static void setupContainer() {
    POSTGRES_SQL_CONTAINER.start();
  }

  @AfterAll
  static void cleanup() {
    POSTGRES_SQL_CONTAINER.stop();
  }

  @DynamicPropertySource
  static void springProperties(DynamicPropertyRegistry registry) {
    registry.add("spring.datasource.url", POSTGRES_SQL_CONTAINER::getJdbcUrl);
    registry.add("spring.datasource.username", POSTGRES_SQL_CONTAINER::getUsername);
    registry.add("spring.datasource.password", POSTGRES_SQL_CONTAINER::getPassword);
  }

  @Autowired
  private AddressRepository addressRepository;

  @Test
  public void testRepo(){
    // given  
    Address address = new Address();
    address.setid(1));
    address.setLine1("address line 1");
    addressRepository.save(address)

    // when
    List<Address> addresses = repository.getAddressResults();

    // then
    assertThat(addresses.get(0).getLine1().equalsIgnoreCase("address line 1"));
  }
}

The code above assumes that you have proper dependencies setup in your build tool, you can find Testcontainer docs here: https://www.testcontainers.org/.

ttarczynski
  • 949
  • 1
  • 10
  • 19
1

To test a repository, you can use test slice with the annotation @DataJpaTest. It allows you to test a part of your application by loading the concerned beans.

While testing with Spring, you don't have to load all the context.

Harry Coder
  • 2,429
  • 2
  • 28
  • 32