0

I am trying to write unit tests for Repository layer classes with Junit and Mockito. I have mocked the base class that supplies NamedParameterJdbcOperations and tried to inject into the repo class. In the repo class, we are loading sql queries from files on classpath. This is done in a method that is annotated with @PostConstruct. When trying to test a method of the repo, it is not able to find or load the query and thus throwing NullPointerException.

Need help / suggestion on how to deal with such scenario.

PS: I am not allowed to change the repo class implementation.

Attaching the code of repo and test class for reference.

RepositoryImpl.java

@Repository
public class RepositoryImpl extends AppJdbcImpl implements
    Repository {

private static final StudentMapper STUDENT_ROW_MAPPER = new StudentMapper();
private static final CourseMapper COURSE_ROW_MAPPER = new CourseMapper();

@Value("classpath:sql/sql1.sql")
private Resource sql1;
private String query1;

@Value("classpath:sql/sql2.sql")
private Resource sql2;
private String query2;

public RepositoryImpl() { }

public RepositoryImpl(NamedParameterJdbcOperations jdbc) {
    super(jdbc);
}

@PostConstruct
public void setUp() {
    query1 = loadSql(sql1);
    query2 = loadSql(sql2);
}

public Iterable<Course> findCoursesByStudentId(int studentId) throws
    DataAccessException {

    try {
        return jdbc().queryForObject(query1,
            ImmutableMap.of("studentId", studentId),
            COURSE_ROW_MAPPER);

    } catch (EmptyResultDataAccessException emptyResult) {
        return null;
    } catch (DataAccessException e) {
        // Need to create exception classes and throw specific exceptions
        throw e;
    }

}

public Iterable<Student> findStudentsByCourseId(int courseId) throws DataAccessException {

    try {

        return jdbc().query(query2,
            ImmutableMap.of("courseId", courseId),
            STUDENT_ROW_MAPPER);

    } catch (DataAccessException e) {
        // Need to create exception classes and throw specific exceptions
        throw e;
    }

}

private String loadSql(Resource resource) {
    try {
        return CharStreams.toString(new InputStreamReader(resource.getInputStream()));
    } catch (IOException e) {
        return null;
    }
}

}

RespositoryImplTest.java

@RunWith(MockitoJUnitRunner.class)
public class RepositoryImplTest {
@Mock
private NamedParameterJdbcOperations jdbc;

@Mock
private ResultSet resultSet;

@Mock
private StudentMapper studentMapper;

@Mock
private CourseMapper CourseMapper;

@InjectMocks
private RepositoryImpl repository;

private Student student1;
private Student student2;

private Course course1;
private Course course2;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);

    course1 = new Course(1, "Karate");
    course2 = new Course(2, "Riding");
    course8 = new Course(8, "Swimming");
    List<Course> courseList = Arrays.asList(course1, course2, course8);

    student1 = new Student(1, "Chuck", "Norris", 27, new Arrays.asList(course1, course2));
    student2 = new Student(2, "Bruce", "Lee", 54, new Arrays.asList(course1, course8));
    List<Student> studentList = Arrays.asList(student1, student2);

    when(jdbc.queryForObject(Matchers.anyString(), anyMap(),
        isA(StudentMapper.class)))
        .thenAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
                Object[] args = invocationOnMock.getArguments();
                int queryParam = Integer.parseInt(args[0].toString());

                Iterable<Credentials> result = studentList.stream()
                .filter(d -> d.getId() == queryParam)
                .collect(Collectors.toList());

                return result;
            }
        });
}

@Test
public void findCoursesByStudentId() {
    Iterable<Course> result = repository.findCoursesByStudentId(1);
    assertNotNull(result);
}

}

In repo class, exception is thrown as query1 is null.

Need help to properly solving the issue.

Thanks, Baru

yassadi
  • 524
  • 1
  • 9
  • 20
Baru
  • 1
  • 2
  • So why won't you call `respository.setUp()` after creating the instance? – yegodm Apr 13 '18 at 13:21
  • You're not running the test using a spring runner, and you don't let spring create and inject the repository. So PostConstruct is not called by anyone. You could call it yourself, but the `@Value` annotated fields will be null (again, because the repo is not created by Spring). Read the Spring documentation about testing. – JB Nizet Apr 13 '18 at 13:22
  • @ JB Nizet, you are correct. Because of the @Value annotation on the fields, calling setUp() will not help in the testing. I will refer the docs once again, but had to use Junit and Mockito for the testing purpose. – Baru Apr 13 '18 at 13:26
  • Testing DAOs using mocks is, IMHO, a giant anti-pattern. What you want to test is that your queries work fine. Not that you call queryForObject with any string and any map. This increases code coverage, but doesn't actually test anything. So, setup your database with test data (using DbSetup, or DBUnit, or simply your own JDBC code), then call your reporitory method, and check it returns what it's expected to return. – JB Nizet Apr 13 '18 at 13:31
  • Found a work around to address my situation. Used `org.springframework.test.util.ReflectionTestUtils` and set the fields. This helped me to avoid NPE and set a expected value to the private fields that are being built in @PostConstruct annotated method. This is how the fields are set `ReflectionTestUtils.setField(repository, "query1", "query1"); ReflectionTestUtils.setField(repository, "query2", "query2");` – Baru Apr 16 '18 at 08:10

1 Answers1

0
@RunWith(MockitoJUnitRunner.class)

you start test with mockito starter, not spring starter. It's mean that spring not provided you beans. Mockito starter nothing know about PostConstruct annotation.

You may call PostConstruct method youself in sturUp junit method or in test method.

Виктор
  • 221
  • 3
  • 5
  • Did that, but that did not help. Threw NPE as it did not process fields with @Value. – Baru Apr 13 '18 at 13:30
  • Found a work around to address my situation. Used `org.springframework.test.util.ReflectionTestUtils` and set the fields. This helped me to avoid NPE and set a expected value to the private fields that are being built in @PostConstruct annotated method. This is how the fields are set `ReflectionTestUtils.setField(repository, "query1", "query1"); ReflectionTestUtils.setField(repository, "query2", "query2");` – Baru Apr 16 '18 at 08:15