By using JMockit #Capturing, it was unable to capture call to any spring data jpa repository methods.
public interface UserRepository extends JpaRepository<UserEntity, Long> {
UserEntity findByName(String name);
}
public class DefaultUserService implements UserService {
public User getUser(Long id) {
return userRepo.findOne( id );
}
public User getUser(String name) {
return userRepo.findByName( name );
}
}
public class UserServiceTest extends AbstractServiceTest {
#Inject UserService userService;
**#Capturing UserRepository userRepo;**
#Test
public void getUser_ShouldReturnUserObject() {
**new Expectations() {{
userRepo.findByName(anyString);
result = new UserEntity(1l, null, null, null, null);
}};**
User user = userService.getUser("abc");
assertNotNull(user.getId());
assertEquals(1l, user.getId().longValue());
}
}
However by replacing
UserRepository
with
JpaRepository<UserEntity, Long>
in the test class, JMockit able to intercept call to any methods available in JpaRepository interface like findOne() or findAll().
But it just not able to capture calls to custom repository methods that extends JpaRepository such as findByName().
I prefer to use #Capturing for this scenario even though JMockit state-based testing such as MockUp and Deencapsulation can solve this issue because it is much more simpler.
Any one has any ideas to solve this issue?
Without knowing what AbstractServiceTest + Spring is doing with respect to the #Inject fields, I can't tell why #Capturing fails. Maybe Spring creates a proxy object which delegates to another, but even then...
Anyway, unit tests like these can be more easily written:
public class UserServiceTest
{
#Tested DefaultUserService userService;
#Injectable UserRepository userRepo;
#Test
public void getUser_ShouldReturnUserObject()
{
new Expectations() {{
userRepo.findByName(anyString);
result = new User(1l, null, null, null, null);
}};
User user = userService.getUser("abc");
assertNotNull(user.getId());
assertEquals(1l, user.getId().longValue());
}
}
Unless you want to perform integration testing, it's generally best to let the mocking tool (JMockit in this case) do the injection of mock objects into tested objects.
Related
My app has a service layer which is composed by CDI applications scoped beans:
#ApplicationScoped
#Transactional
public class PostService {
#Inject private PostRepository postRepo;
#Inject private UserRepository userRepo;
#Inject private SectionRepository sectionRepo;
#Inject private LoggedInUser loggedInUser;
public PostDto getPost(#PostExists int id){
Post p = postRepo.findById(id);
//create post DTO from p
return post;
}
public void delete(#PostExists int id){
postRepo.remove(postRepo.findById(id));
}
public int newPost(#NotBlank #Max(255) String title,
#Max(2000) String body,
#SectionExists String sectionName){
User user = userRepo.getByName(loggedInUser.getUsername());
Section section = sectionRepo.getByName(sectionName);
Post post = new Post();
post.setTitle(title);
post.setContent(body == null || body.isBlank() ? "" : body);
post.setAuthor(user);
post.setSection(section);
post.setType(TEXT);
return postRepo.insert(post).getId();
}
}
When a method gets called, an interceptor (in my case BValInterceptor.class from Apache BVal) checks if the method contract is respected by checking the annotations and validating the parameters accordingly.
As you can see, there are some custom constraints like #SectionExists, #PostExists that may hit the database:
public class SectionExistsValidator implements ConstraintValidator<SectionExists, String> {
#Inject SectionRepository sectionRepo;
#Override
public void initialize(SectionExists constraintAnnotation) {}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return (sectionRepo.getByName(value) != null);
}
}
public class PostExistsValidator implements ConstraintValidator<PostExists, Integer> {
#Inject PostRepository postRepo;
#Override
public void initialize(PostExists constraintAnnotation) {}
#Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return (postRepo.findById(value) != null);
}
}
What I'd like to do is to unit test my business methods (getpost, delete, newPost) together with its validators. The validators that may hit the database should be mocked (or their dependency should be mocked).
How can I achieve this? How could I make injections (and mock injections) work for validators in unit tests?
Here what I'm using:
TomEE 8.0.8
Apache BVal for Bean Validation JSR 303/JSR380 (included in TomEE)
Apache OpenWebBeans for CDI (included in TomEE)
JUnit 5
Mockito
I can use OpenEJB's ApplicationComposer or Arquillian to run an embedded container. However, I've never used Arquillian.
In the end I went for this really cool library (cdimock) that does exactly what i needed: put the mocks in a custom CDI scope so that the same mock instances can be injected in other beans inside the test case. Such thing can also be achievable with cdi-unit #Produces #Mock annotations (Although i haven't tried it personally since it only supports Weld)
This is my test class' code:
#RunWithApplicationComposer(mode = ExtensionMode.PER_EACH)
#ExtendWith({MockitoExtension.class, CdiMocking.class})
#MockitoSettings(strictness = LENIENT)
#Classes(cdi = true,
value={PostService.class},
cdiInterceptors = BValInterceptor.class,
cdiStereotypes = CdiMock.class)
public class PostServiceTest {
#Mock SectionRepository sectionRepository;
#Mock PostRepository postRepository;
#Mock UserRepository userRepository;
#Inject PostService service;
#BeforeEach
void setUp() {}
#AfterEach
void tearDown() {}
#Test
public void noSectionFoundNewPost(){
String sectionName = "idontexist";
when(sectionRepository.getByName(sectionName)).thenReturn(null);
assertThrows(ConstraintViolationException.class,
() -> service.newPost("title", "body", sectionName));
}
}
In the code i'm using OpenEJB's Application Composer but i can easily switch to any embedded CDI container
I'm developing REST App for the IT courses. One of the requirements was to use Spring-JDBC with all queries to be stored in separate properties file. Here is a part of my UserDaoImpl:
#Repository
#RequiredArgsConstructor
#PropertySource(value = "classpath:/user_queries.properties")
#Log
public class UserDaoImpl implements UserDao {
private final NamedParameterJdbcTemplate template;
private final RowMapper<User> rowMapper;
#Value("${find.by_id}")
private String findById;
#Override
public Optional<User> findById(int id) {
SqlParameterSource param = new MapSqlParameterSource("id", id);
User user = null;
try {
user = template.queryForObject(findById, param, BeanPropertyRowMapper.newInstance(User.class));
} catch (DataAccessException ex) {
String.format("User with id - %d, not found.", id);
}
return Optional.ofNullable(user);
}
}
All method work fine, but troubles started when I wrote my Test to this class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {TestDBConfiguration.class})
public class UserDaoTest {
#Autowired
DataSource dataSource;
#Test
public void findByIdTest() {
NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(dataSource);
UserRowMapper userMapper = new UserRowMapper();
UserDao userDao = new UserDaoImpl(template, userMapper);
User user = new User(1, "John", "Doe",
"$2a$04$MzVXtd4o0y4DOlyHMMLMDeE4/eezrsT5Xad.2lmGr/NkCpwBgvn3e",
"jdoe#gmail.com", true);
assertEquals(Optional.of(user), userDao.findById(1));
}
}
When I run this Test my findById is always null. Is there any way to run this test and keep all queries inside the separate file?
Since your UserDaoImpl uses Spring annotations (#Value, #PropertySource, etc), you have to initialize it via Spring to enable their injection. Or else you'd have to supply all these values yourself manually.
So in your test you should inject the DAO itself:
#Autowired
private UserDao userDao;
Or if you want to go manual way (not recommended), allow setting field findById via constructor (recommended) same way you're passing template, userMapper.
I am trying to test a Service Interface that has a specified implementation in a TestConfiguration. The Service Interface gets DAO interface injected which also has a specified implementation in a TestConfiguration. The implementation of the Service is defined as the real service that will be in production while the Test DAO is a new custom implementation of the interface.
In 95% of cases, I want to use the Test DAO Implementation. However, in some tests I want to override the functionality of the DAO. Since there are so few instances where I want to override the DAO I want to just mock using Mockito on a conditional basis without creating another implementation of the DAO Interface (for edge cases like returning an empty list from the DAO). Ideally, these would be in the same class that's dedicated to testing the Service.
I have tried using the #Spy annotation on the DAO Bean in the Test Class. I have tried using the #MockBean on the DAO bean. I have tried using Mockito's doReturn and when functionality to overwrite the DAO's default implementation, however, I always get back the result from the Test DAO implementation defined in the TestConfiguration.
I did change the text of what I am doing since it is company code, but this is exactly what I'm trying to do otherwise.
My TestConfiguration is defined like so
#TestConfiguration
public class TestAppConfiguration {
#Bean
public PersonService personService() {
return new PersonServiceImpl(personDao());
}
#Bean
public PersonDao personDao() {
return new TestPersonDaoImpl();
}
}
My Service Implementation is as follows
public class PersonServiceImpl implements PersonService {
private Logger logger = LoggerFactory.getLogger(PersonServiceImpl.class);
private PersonDao personDao;
public PersonServiceImpl(PersonDao personDao){
this.personDao = personDao;
}
#Override
public List<PersonDto> getAllPersons() {
return personDao.findAll().stream()
.map(person -> PersonDtoFactory.getPersonDto(person))
.collect(Collectors.toList());
}
#Override
public PersonDto getPersonById(Long id) {
return PersonDtoFactory.getPersonDto(personDao.findById(id));
}
}
My Test DAO Implementation is as follows
public class TestPersonDaoImpl implements PersonDao {
#Override
public List<PersonModel> findAll() {
return getPersons();
}
#Override
public List<PersonModel> findById(id) {
return getPersons().stream()
.filter(person -> person.getId() == id)
.collect(Collectors.toList());
}
private List<PersonModel> getPersons() {
List<PersonModel> personList = new ArrayList<>();
personList.add(new PersonModel(1L, "Susan");
personList.add(new PersonModel(2L, "Jerry");
personList.add(new PersonModel(3L, "Tom");
return personList;
}
}
And then finally my service test class
#RunWith(SpringRunner.class)
#Import(TestAppConfiguration.class)
public class PersonServiceTests {
//We won't test web socket functionality in this test class.
#Autowired
private PersonService personService;
#MockBean //want to overwrite in the test only when specified in the test, otherwise, use default TestPersonDaoImpl bean.
private PersonDao personDao;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void getAllPersons() {
assert(personService.getAllTests().size() > 0);
}
#Test
public void getPersonById() {
assert(personService.getPersonById(1L).getName().equals("Susan"));
}
#Test
public void getAllPersons_NoPersons(){
//Mock the DAO call since it will have all of the test data by default
doReturn(new ArrayList<Person>()).when(personDao).findAll();
//when(personDao.findAll()).thenReturn(new ArrayList<>()); <-- this also doesn't work
assert(personService.getAllPersons().size() == 0);
}
Expected would be that all tests pass, and the DAO call would get overwritten when called in the service implementation. The actual result is that the first two tests pass, and the third test fails because it does not overwrite the dao call.
With #MockBean you will get an injected mock instance.
With #Spy your dao will not get injected in the service
You need #SpyBean.. you will get the injection and all methods invoked as they are implemented by default.
I have a class ManageUser as below:
public class ManageUser {
private static UserBO gUserBO = new UserBO();
public String method1() {
gUserBO.callSomeFunction();
gUserBO.callSomeOtherFunction();
}
}
Now, I have a test class where I want to test method1() and since the methods callSomeFunction() and callSomeOtherFunction() end up making database calls I want to mock the calls to those methods. I am unable to do that by using mock since the object in ManageUser is static. How do I proceed? I am new to Junit and Mockito and can't seem to find relevant answers.
Try using Power Mockito:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ManageUser.class})
public class ClassInTest {
#Test
public void testStatic() {
ManageUser mUser = new ManageUser();
Field field = PowerMockito.field(ManageUser.class, "gUserBO");
field.set(ManageUser.class, mock(UserBO.class));
...
}
}
You are "unable to do that by using mock" because your class is badly designed. As a workaround, you could use PowerMock (as #S.K. suggested) to mock the static field but that will only suppress the real problem of your class.
Better take the chance and improve the code for better testability and evolvability:
Step 1: Create an interface for your class UserBO and let it implement it.
public interface UserService {
void callSomeFunction();
void callSomeOtherFunction();
}
public class UserBO implements UserService { ... }
Step 2: Change your class ManageUser to get any implementation of UserService through a constructor.
public class ManageUser {
private final UserService userService;
public ManageUser(UserService userService) {
this.userService = userService;
}
public String method1() {
userService.callSomeFunction();
userService.callSomeOtherFunction();
}
}
Step 3: Change the calling side of your class ManageUser to provide a UserService.
So instead of
ManageUser manager = new ManageUser();
use
ManageUser manager = new ManageUser(new UserBO());
Step 4: Now you can easily mock a UserService in your test and construct a ManageUser with the mock.
This design also enables DI frameworks (e.g. Spring) to inject (or autowire) the components.
Here I am trying to mock autowire fields ServiceHelper of Service class TestServiceImpl , I am not able to call method through mock object of ServiceHelper class.
This is my class files:
#Service
public class TestServiceImpl implements TestService {
#Autowired
private TestDAO testDAO;
#Autowired
private ServiceHelper serviceHelper;
#Override
public ResultsModel getResults(Map<String, Object> map) throws WebServiceException_Exception {
return serviceHelper.getResults(map);
}
2nd Class:
#Repository
public class ServiceHelper {
private static Logger logger = Logger.getLogger(ServiceHelper.class.getName());
#Autowired
ResponseHeader responseHeader;
public void setResponseHeader(ResponseHeader responseHeader) {
this.responseHeader = responseHeader;
}
public ResultsModel getResults(Map<String, Object> map) throws WebServiceException_Exception {
....
}
And Test class:
#RunWith(MockitoJUnitRunner.class)
public class MockitoTester {
#InjectMocks
private TestServiceImpl serviceImpl = new TestServiceImpl();
#Mock
private TestDAO testDAO;
#Mock
private ServiceHelper sHelper;
#Before
public void initMocks(){
MockitoAnnotations.initMocks(this);
}
#Test
public void testResult() throws Exception {
Map<String, Object> map = new HashMap<>();
map.put("TestId", "test123");
map.put("lang", "en");
map.put("cntry", "USA");
ResultsModel results = new ResultsModel();
when(sHelper.getResults(map)).thenReturn(results);
results = serviceImpl.getResults(map);
Assert.assertEquals(results.getStatus(), "Success");
}
Here in my test class:
results = serviceImpl.getResults(map);
It goes to TestServiceImpl class to method :
public ResultsModel getResults(Map<String, Object> map) throws webServiceException_Exception {
return serviceHelper.getResults(map);
}
but at point :
serviceHelper.getResults(map);
it is not going inside serviceHelper.getResults(map) and return all values as Null.
Please suggest where I need to do changes.
You have three choices here:
Do actual Spring autowiring in your tests
Use injection methods that can legitimately be performed by your tests (constructor parameters, public setters, public fields - in order of preference)
Use reflection to inject your mocks
Option 1 is really integration testing -- you can annotate your test class with #RunWith(SpringRunner.class) and use more Spring annotations to control dependency injection. It's too big a subject to cover in a SO answer, but there are plenty of examples if you Google for "spring integration test mockito".
But for unit testing, I think it's better not to involve Spring. A good Spring bean doesn't need Spring to function. Option 2 just says, write your class so that unit tests (and anything else) can inject the dependency (be it a mock, or anything else) through normal Java means.
Constructor injection is cleanest in my opinion:
private final ServiceHelper serviceHelper; // note: not annotated
#Autowired
public TestService(ServiceHelper serviceHelper) {
this.serviceHelper = serviceHelper;
}
But you can also do this with a public void setServiceHelper(ServiceHelper helper) -- this is less good because the field can't be final.
Or by making the field public -- I assume you know the reasons this is bad.
If you're determined to have a private field that's not set by a public constructor or setter, you could use Spring's ReflectionUtils.setField() from within your test:
#Mock
private ServiceHelper serviceHelper;
private TestService service;
#Before
public void configureService() {
service = new TestService();
Field field = ReflectionUtils.findField(TestService.class, "serviceHelper");
ReflectionUtils.setField(field, service, serviceHelper);
}
(Or, equally, use JDK's reflection classes directly, or reflection utils from elsewhere)
This is explicitly using reflection to subvert the access rules you've coded into the class. I thoroughly recommend option 2.
I think the issue may be that you are stubbing your method to return the same object which you then assign the result of the method under test. i.e. (the results object here):
ResultsModel results = new ResultsModel();
when(sHelper.getResults(map)).thenReturn(results);
results = serviceImpl.getResults(map);
This will probably cause some sort of cyclic confusion when it tries to stub the method in Mockito, and it certainly won't make your assertation pass:
Assert.assertEquals(results.getStatus(), "Success");
Since the status on results is never set anywhere.
I think you need to make separate objects for your stubbing and your returned value from the method under test and make sure you set your stubbed one to have a status of "Success":
ResultsModel results = new ResultsModel();
results.setStatus("Success");
when(sHelper.getResults(map)).thenReturn(results);
ResultsModel returnedResults = serviceImpl.getResults(map);
Assert.assertEquals(returnedResults.getStatus(), "Success");
Try using constructor injection it'd be easier to mock the classes for testing... here's an example on how I would structure my classes to get you going. When you write your tests you now have to pass the Mocked object into the instance you're creating of these classes:
#Service
public class TestServiceImpl implements TestService {
private TestDao testDao;
private ServiceHelper serviceHelper;
#Autowired
public TestServiceImpl(TestDAO testDAO, ServiceHelper serviceHelper) {
this.testDAO = testDAO;
this.serviceHelper = serviceHelper;
}
}
#Repository
public class ServiceHelper {
private ResponseHeader responseHeader;
#Autowired
public ServiceHelper(ResponseHeader responseHeader) {
this.responseHeader = responseHeader
}
}