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
#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.
Related
Below is main code consist of one util class and service class using it
#PropertySource("classpath:atlas-application.properties")
public class ApacheAtlasUtils {
#Value("${atlas.rest.address}")
private String atlasURL;
#Value("${atlas.rest.user}")
private String atlasUsername;
#Value("${atlas.rest.password}")
private String atlasPassword;
private AtlasClientV2 client;
public AtlasClientV2 createClient() {
if (client == null) {
return new AtlasClientV2(new String[] {atlasURL}, new String[] {atlasUsername, atlasPassword});
} else {
return client;
}
}
}
Service Class is below :-
#Override
public Page<SearchResultDto> findFilesWithPages(QueryParent queryParent, Pageable pageable)
throws AtlasServiceException {
// Some code
client = new ApacheAtlasUtils().createClient();
//some code
}
I am writing unit test for service method and I am getting exception for createClient method asking for values for url, username and password which should not happen as this should be mocked but the mocking is giving me below error
java.lang.IllegalArgumentException: Base URL cannot be null or empty.
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:141)
at org.apache.atlas.AtlasServerEnsemble.<init>(AtlasServerEnsemble.java:35)
at org.apache.atlas.AtlasBaseClient.determineActiveServiceURL(AtlasBaseClient.java:318)
at org.apache.atlas.AtlasBaseClient.initializeState(AtlasBaseClient.java:460)
at org.apache.atlas.AtlasBaseClient.initializeState(AtlasBaseClient.java:448)
at org.apache.atlas.AtlasBaseClient.<init>(AtlasBaseClient.java:132)
at org.apache.atlas.AtlasClientV2.<init>(AtlasClientV2.java:82)
at com.jlr.stratus.commons.utils.ApacheAtlasUtils.createClient(ApacheAtlasUtils.java:40)
at com.jlr.stratus.rest.service.impl.FileSearchService.findFilesWithPages(FileSearchService.java:49)
The Test code is as follows:-
private FileSearchService fileSearchService;
#Spy
private ApacheAtlasUtils apacheAtlasUtils;
#Mock
private AtlasClientV2 client;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
fileSearchService = new FileSearchService();
}
#Test
public void findFilesWithPages_searchAll() throws AtlasServiceException {
Mockito.doReturn(client).when(apacheAtlasUtils).createClient();
service.search(queryParent,pageable);
}
Your idea with spying is adequate (you can even go for mocking if you do not actually need any true implementation of that class).
The problem lies in the implementation:
// Some code
client = new ApacheAtlasUtils().createClient();
//some code
}
Instead of having the ApacheAtlasUtils as an instance variable (or a supplier method) you create the instance on the fly.
Mockito is not smart enough to catch that operation and replace the real object with you spy.
With the supplier method you can set up your test as follows:
#Spy
private FileSearchService fileSearchService = new FileSearchService();
#Spy
private ApacheAtlasUtils apacheAtlasUtils = new ApacheAtlasUtils();
#Mock
private AtlasClientV2 client;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
doReturn(apacheAtlasUtils).when(fileSearchService).getApacheUtils();
}
in your SUT:
#Override
public Page<SearchResultDto> findFilesWithPages(QueryParent queryParent, Pageable pageable)
throws AtlasServiceException {
// Some code
client = getApacheUtils().createClient();
//some code
}
ApacheAtlasUtils getApacheUtils(){
return new ApacheAtlasUtils();
}
I try to write some programme using Java, Hibernate and Spring. I see NullPointerException when I call the SQL query. I tried perhaps every samples of code on the internet, but I don't know how to do this. How can I solve it? This is my sample code. There is my full code of programme: https://github.com/lukasz-chojn/films_database/tree/master/src/main/java/Films
#Controller
public class DataFromUserController {
private FilmDaoImpl filmDaoImpl = new FilmDaoImpl();
private DirectorDaoImpl directorDaoImpl = new DirectorDaoImpl();
private Locale locale = Locale.getDefault();
private Scanner sc = new Scanner(System.in).useLocale(locale);
public FilmDs collectData() {
//....
filmDaoImpl.insert(film);
directorDaoImpl.insert(director);
return film;
}
private String collectTitle() {
System.out.print("Podaj tytuł filmu: ");
String tytul = sc.nextLine();
List<String> existingEntry = filmDaoImpl.existingTitle();
if (existingEntry.contains(tytul)) {
System.out.println("Tytuł jest już w bazie. Podaj inny");
return collectTitle();
}
if (tytul.isEmpty()) {
System.out.println("Tytuł nie może być pusty. Podaj go");
return collectTitle();
}
return tytul;
}//......
}
#Service
#Transactional
public class FilmDaoImpl implements FilmDAO {
#PersistenceContext
private EntityManager entityManager;
//......
#Override
#SuppressWarnings("unchecked")
public List<String> existingTitle() {
List<String> titleInTheDatabase;
// here is NPE
return titleInTheDatabase = entityManager.createQuery("SELECT film.tytul FROM FilmDs film").getResultList();
}
//....
}
You're creating instances on your own instead of letting spring inject it for you which is why you're losing all the dependencies like entitymanager, etc. change your DataFromUserController
#Controller
public class DataFromUserController {
#Autowired
private FilmDAO filmDao;
#Autowired
private DirectorDAO directorDao;
.....
}
I am trying to mock a method of a private field that has a return type of void. In my test, I am trying to mock aClass.doSomething() to throw an IllegalStateException and I need to verify that recover() is called. Here is an example:
public class ClassToTest implements Runnable {
private ClassToMock aClass;
#Override
public void run() {
try{
aClass.doSomething("some parameter");
} catch(IllegalStateException e) {
logger.error("Something bad happened", e);
recover();
}
}
public void recover(){
logger.info("I am recovering");
}
}
I have done each piece separately:
Mock a method call of a private field
Mock a method that has void return type
Throw exception
Verify a private method call
but I wasn't able to put all together. Any help is appreciated
I thought I'd elaborate GhostCat's comments:
Stay with Mockito
Mockito is more than a mocking framework - it's a discipline. If you read carefully the documentation for Mockito and restrain yourself from resorting to PowerMock et al you will learn good OOP practice.
Read how to do dependency injection with constructors
Primum non nocere - first refactor your code like this:
public class ClassToTest implements Runnable {
private final ClassToMock aClass;
private final Logger logger;
//injection of collaborators through the constructor
public ClassToTest(ClassToMock aClass, Logger logger) {
this.aClass = aClass;
this.logger = logger;
}
#Override
public void run() {
try{
aClass.doSomething("some parameter");
} catch(IllegalStateException e) {
logger.error("Something bad happened", e);
recover();
}
}
public void recover() { //there is no need for this method to be public - see Effective Java item 13
logger.info("I am recovering");
}
}
Now your code is testable using Mockito without resorting to more complex mocking frameworks:
//mocks
#Mock ClassToMock classToMock;
#Mock Logger mockLogger;
//system under test
ClassToTest classToTest;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks();
classToTest = new ClassToTest(classToMock, mockLogger);
}
#Test
public void whenRun_thenDoesSomethingWithSomeParameter() {
//act
classToTest.run();
//assert
verify(classToMock).doSomething(eq("some parameter"));
}
#Test
public void givenAnIllegalStateForAClass_whenRun_thenLogsError() {
//arrange
IllegalStateException e = new IllegalStateException();
when(classToMock.doSomething(anyString()).thenThrow(e);
//act
classToTest.run();
//assert
verify(mockLogger).error(eq("Something bad happened"), same(e));
}
#Test
public void givenAnIllegalStateForAClass_whenRun_thenLogsRecovery() {
//arrange
when(classToMock.doSomething(anyString()).thenThrow(new IllegalStateException());
//act
classToTest.run();
//assert
verify(mockLogger).info(eq("I am recovering"));
}
I'm new to Testing side.I'm using Spring Mvc in my application. I followed some tutorials to write for controller and service Test Case. I'm facing error in service test. Please help !
Service :
#Autowired
private PatientDao patientDao;
#Autowired
private PrefixDao prefixDao;
public Patient createPatient(Patient patient) throws Exception {
patient.setAgeorDob();
return createPatientInSync(patient);
}
private synchronized Patient createPatientInSync(Patient patient)
throws Exception {
try {
Prefix prefix = prefixDao.getPrefixForType(PrefixType.PATIENT);
patient.setPatientNo(prefix.getPrefixedNumber());
patientDao.createPatient(patient); //SAVE PATIENT
prefixDao.incrementPrefix(prefix);
} catch (ConstraintViolationException ex) {
throw new InternalErrorException("Please enter valid data", ex);
} catch (NullPointerException e) {
e.printStackTrace();
throw new InternalErrorException(
"Please create Prefix for Patient", e);
}
return patient;
}
Service Test case:
#ContextConfiguration(locations = {
"classpath:/applicationContext-resources.xml",
"classpath:/applicationContext-service.xml",
"classpath:/applicationContext-dao.xml",
"classpath:/applicationContext.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
public class PatientServiceTest {
#Autowired
#Mock
private PatientDao patientDao;
#InjectMocks
private PatientServiceImpl patientService = new PatientServiceImpl();
private PrefixDao prefixDao;
#Before
public void doSetup() {
patientDao = mock(PatientDao.class);
prefixDao = mock(PrefixDao.class);
// Mockito.mock(PatientDao.class);
}
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testSaveUser() throws Exception {
Patient mockPatient = new Patient();
mockPatient.setFirstName("Aravinth");
mockPatient.setSex(Gender.Male);
mockPatient.setAgeOrDob("24");
Prefix prefix = new Prefix();
prefix.setPrefixType(PrefixType.PATIENT);
prefix.setPrefix("Pat-");
prefix.setSequenceNo(23);
when(prefixDao.getPrefixForType(PrefixType.PATIENT)).thenReturn(prefix);
System.out.println(prefix.getSequenceNo());
mockPatient = patientService.createPatient(mockPatient);
assertEquals("Aravinth", mockPatient.getFirstName());
verify(patientDao, times(1)).createPatient(mockPatient);
}
}
Verify times works fine.I got Nullpointer in assertEquals.
Need to #Mock PrefixDao first.
If you are using Junit 5, no need to run initMocks(this). Otherwise you need this: MockitoAnnotations.initMocks(this);
With that, the mockito will wire two mock Dao objects to your service.
Also I don't see you mock action for patientDao.
When(patientDao.create()).thenReturn(...);
I'm getting a very strange error with Mockito:
java.lang.IllegalStateException
at java.util.LinkedList$ListItr.remove(LinkedList.java:923)
at org.mockito.internal.debugging.WarningsFinder.find(WarningsFinder.java:36)
at org.mockito.internal.debugging.WarningsPrinterImpl.print(WarningsPrinterImpl.java:28)
at org.mockito.internal.debugging.WarningsCollector.getWarnings(WarningsCollector.java:34)
at org.mockito.internal.junit.JUnitRule$1.evaluate(JUnitRule.java:29) <26 internal calls>
There is no reference to my test code, but here are the classes I'm using and the test:
Endpoint.java: Uses Retrofit 2.0
public interface Endpoint {
#GET("items/{itemId}")
Call<List<Item>> list(#Path("itemId") String itemId);
Call<List<Item>> list(#Path("itemId") String itemId, #Path("alternativeId") String alternativeId);
}
Query.java
public class Query {
private String itemId;
// constructor, etc
public String getItemId() {
return itemId;
}
}
ItemDataSource.java: Unfinished implementation (following TDD)
public class ItemDataSource {
private Endpoint endpoint;
// constructor, etc
public Optional<Item> get(Query query) {
List<Item> list = null;
try {
list = endpoint.list(query.getItemId()).execute().body();
} catch (IOException e) {
e.printStackTrace();
}
Template result = modelAdapter.apply(list.get(0));
return Optional.ofNullable(result);
}
}
ItemDataSourceTest.java:
public class TemplateNetworkDataSourceTest {
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#Mock
private Query query;
#Mock
private Item item;
#Mock(answer = RETURNS_DEEP_STUBS)
private ItemEndpoint endpoint;
#Test
public void shouldProvideItemFromEndpoint() throws Exception {
when(query.getItemId()).thenReturn("itemId");
when(endpoint.list("itemId").execute()).thenReturn(Response.success(singletonList(networkTemplate)));
ItemDataSource dataSource = new ItemDataSource(endpoint);
Optional<Item> actual = dataSource.get(query);
assertThat(actual.get()).isEqualTo(item);
}
#Test
public void shouldProvideNoItemsWhenNotFound() throws Exception {
//when(query.getProductId()).thenReturn("incorrect_productId"); // <- works
when(endpoint.list(anyString()).execute()).thenReturn(Response.success(emptyList()));
ItemDataSource dataSource = new ItemDataSource(endpoint);
Optional<Item> actual = dataSource.get(query);
assertThat(actual.isPresent()).isFalse();
}
}
If I run it, the first test runs correctly but the second gives me the provided error. If I comment out the first line in the statement of the second test it works (so that could be the answer) but I'm wondering if I'm missing something or if this is a bug on Mockito.
From my slight digging through Mockito's code I see that a List is modified when collecting the warnings after the test is executed.