Mockito and string objects - java

I've started playing around with mockito today and I've encountered a problem. This is the class I'm trying to create test cases on:
#Path("search")
public class SearchWebService {
private static final Logger logger = Logger.getLogger(SearchWebService.class);
#EJB
UserServiceInterface userService;
#GET
#Path("/json/{searchstring}")
#Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
#RolesAllowed("User")
public List getJSONFromSearchResults(#PathParam("searchstring") String searchString, #Context HttpServletRequest request) {
logger.info("getJSONFromSearchResults called");
//Users own email
String ownemail = request.getRemoteUser();
if (searchString.contains(" ")) {
//Split String in two at first space
String names[] = searchString.split("\\s+", 2);
List userList = userService.searchByFullName(names[0], names[1], ownemail);
if (userList.size() > 0) {
return userList;
} //Check for cases where the last name contains spaces
else {
return userService.searchByLastName(searchString, ownemail);
}
}
return userService.searchBySingleName(searchString, ownemail);
}
}
I'm at searchString.contains(" ") and I'm trying to invoke "when(...).thenReturn(...)" But mockito throws an exception saying that "Cannot mock/spy class java.lang.String" I'm not sure I'm doing it correctly when testing this web service. Maybe there is some otherway to do this? Here is my test class:
public class SearchWebServiceTest {
#Mock
UserServiceInterface mockedUserService;
#Mock
Logger mockedLogger;
#Mock
HttpServletRequest mockedRequest;
#Mock
String mockedString;
#Mock
List<SearchResultsContainer> mockedUserList;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testGetJSONFromSearchResultsSpace() throws Exception {
when(mockedRequest.getRemoteUser()).thenReturn("email");
when("StringWithSpace".contains(" ")).thenReturn(true);
when("StringWitchSpace".split("\\s+", 2)).thenReturn(null);
when(mockedUserService.searchByFullName("name1", "name2", "email")).thenReturn(mockedUserList);
assertTrue(mockedUserList.size() > 0);
}

you cannot mock final classes (like String). This is a known limitation of the framework.
You can refer this link.
Mockito Verification Not Failing
I hope it helps !!!

If you need to invoke your service with a String that has a space, then just pass it a string that has a space. And don't mock the class that you are trying to test. You should mock as little as possible in unit tests. Simply provide real input data that meets the particular conditions of your particular test. Only mock collaborators, and only when you need to. If you need a String (either as a parameter or as a return value of a collaborator) that meets certain conditions, then just provide such an example String.

so if you need to test some method on string then best way is use reflection method to assign value to your string variable.
I used apache common library for it
FieldUtils.writeField(yourClass, "variableName", "value", true);

Related

How do I mock local OAuth2RestTemplate restTemplate?

I have a method:
public void putObj(Doc doc) {
for (int i = 0; i < 3; i++) {
try {
OAuth2RestTemplate restTemplate = something.thatReturnsOAuth2RestTemplate(props);
restTemplate.postForEntity(somethingElse.getUrl(), doc.toJSONString(), String.class);
break;
} catch (HttpClientErrorException | HttpServerErrorException e) {
//do stuff in here
}
}
}
And my test class:
#RunWith(MockitoJUnitRunner.class)
#PrepareForTest(OkHttp3TemplateUtil.class)
public class TestingClass {
#InjectMocks
private static MyService myService;
#Mock
private static Something something;
#Mock
private static Props props;
#Mock
private static OAuth2RestTemplate restTemplate;
#Test
public void testExceptionCaughtWhenThrownByRestTemplate(){
PowerMockito.mockStatic(OkHttp3TemplateUtil.class);
Doc doc = new Doc.docBuilder().setSomething("");
when(something.thatReturnsOAuth2RestTemplate(props)).thenReturn(restTemplate);
when(restTemplate.postForEntity("http://dummy.com", String.class, String.class)).thenThrow(HttpClientErrorException.class);
myService.putObj(doc);
}
}
No matter what I do, thenThrow never throws an exception. The test passes never providing coverage for code after catch. What am I missing here, I'm going mad!
It looks like you need to use matchers from Mockito.
In your case the 3 args for restTemplate are a bit confusing. The 1st is a String value so use anyString() to match it and mock out somethingElse.getUrl(), that code isn't in the example so not sure what it does but it must return a String and not be null. It looks like you want to match any string for the 2nd, with Mockito you need to use anyString() or any() if it is not a String to accomplish that. The 3rd is the actual value of String.class so again use eq(). Note, if any params are null it will not match. Also, it is easy to end up mocking out a different overloaded postForEntity if you're not careful.
For the something.thatReturnsOAuth2RestTemplate, you might be OK without a matcher. If the Props class has equals defined and both the test and production code values are equal. However, the example doesn't show this info, so I just added the any(Props.class) for that too.
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
#Test
public void testExceptionCaughtWhenThrownByRestTemplate(){
PowerMockito.mockStatic(OkHttp3TemplateUtil.class);
Doc doc = new Doc.docBuilder().setSomething("");
when(something.thatReturnsOAuth2RestTemplate(any(Props.class))).thenReturn(restTemplate);
when(restTemplate.postForEntity(anyString(), any(), eq(String.class))).thenReturn(response);
}

How to deal with side effects of mocked method with JUnit/Mockito

This might be a dumb question or something obvious to figure out, but nevertheless, I'm really struggling to solve this.
Let's say we have the following logic within a method under test:
#Service
public class ServiceToTest() {
#Autowired
private SomeService someService;
public String testMethod() {
Map<String, String> parameters = new HashMap<String, String>();
// eventParameters will be populated inside someService
String result = someService.doLogic(parameters);
// do something with the result here that doesn't really matter for this example
String name = parameters.get("name").toLowerCase();
return name;
}
}
Inside SomeService the parameters map is populated with some values, like the "name" in this example. I would like to mock this service in my unit test.
Consider the following unit test snippet:
#RunWith(SpringRunner.class)
public class ServiceToTestTest {
#TestConfiguration
static class ServiceToTestConfiguration {
#Bean
public ServiceToTest serviceToTest() {
return new ServiceToTest();
}
#Autowired
private ServiceToTest serviceToTest;
#MockBean
private SomeService someService;
#Test
public void testShouldReturnJimmy() {
given(someService.doLogic(Mockito.anyMap())).willReturn("whatever");
String name = serviceToTest.testMethod();
assertThat(name).isEqualTo("jimmy");
}
}
When I execute this test I get a NullPointerException on this line:
String name = parameters.get("name").toLowerCase();, which makes sense as the method that should populate this map is mocked and parameters.get("name") is null. Let's also assume that I really want to have a String returned from doLogic(parameters), so it cannot be the parameters map.
Is there a way to somehow instruct the mock object to populate the parameters map, or to mock the map object itself?
(The code examples here were written for this post on the fly, so please forgive me if there are any stupid mistakes that I haven't noticed while writing them ;-) )
Can be done using the controversial thenAnswer method.
https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/stubbing/OngoingStubbing.html#thenAnswer-org.mockito.stubbing.Answer-
But JB's comment is correct. This is not a great idea.

Mockito verify unit test - Wanted but not invoked. Actually, there were zero interactions with this mock

At first I want to sorry for my english.
I started to make some unit tests (i've never done this before, i'm a new guy in programming).
I have to test simple adding product to database (DynamoDB) method using mockito.verify but I have
"Wanted but not invoked. Actually, there were zero interactions with this mock."
Error and I don't know what to do.
This is my method code (in KitchenService class):
public Product addProduct(Product content) {
ObjectMapper objectMapper = new ObjectMapper();
String mediaJSON = null;
String authorJSON = null;
String productKindsJSON = null;
try {
mediaJSON = objectMapper.writeValueAsString(content.getMedia());
authorJSON = objectMapper.writeValueAsString(content.getAuthor());
productKindsJSON = objectMapper.writeValueAsString(content.getProductKinds());
} catch (JsonProcessingException e) {
logger.log(e.getMessage());
}
Item item = new Item()
.withPrimaryKey("id", UUID.randomUUID().toString())
.with("name", content.getName())
.with("calories", content.getCalories())
.with("fat", content.getFat())
.with("carbo", content.getCarbo())
.with("protein", content.getProtein())
.with("productKinds", productKindsJSON)
.with("author", authorJSON)
.with("media", mediaJSON)
.with("approved", content.getApproved());
Item save = databaseController.saveProduct(PRODUCT_TABLE, item);
logger.log(save + " created");
return content;
}
And this is test code:
#Test
public void addProduct() throws Exception {
KitchenService instance = mock(KitchenService.class);
Product expectedProduct = new Product();
expectedProduct.setName("kaszanka");
expectedProduct.setCalories(1000);
expectedProduct.setFat(40.00);
expectedProduct.setCarbo(20.00);
expectedProduct.setProtein(40.00);
expectedProduct.setProductKinds(Collections.singletonList(ProductKind.MEAT));
expectedProduct.setApproved(false);
Author expectedAuthor = new Author();
expectedAuthor.setId("testID");
expectedAuthor.setName("Endrju Golota");
expectedProduct.setAuthor(expectedAuthor);
Media expectedMedia = new Media();
expectedMedia.setMediaType(MediaType.IMAGE);
expectedMedia.setName("dupajasia");
expectedMedia.setUrl("http://blabla.pl");
expectedProduct.setMedia(expectedMedia);
verify(instance, times(1)).addProduct(expectedProduct);
}
This is what I got after test:
Wanted but not invoked:
kitchenService.addProduct(
model.kitchen.Product#a0136253
);
-> at service.kitchen.KitchenServiceTest.addProduct(KitchenServiceTest.java:80)
Actually, there were zero interactions with this mock.
Can someone tell me what im doing wrong?
What you should mock and verify is the databaseController dependency:
#Test
public void addProduct() throws Exception {
KitchenService instance = new KitchenService(); // you should create the class under test
DatabaseController controllerMock = mock(DatabaseController.class); // mock the controller
instance.setController(controller); // inject the mock
...
// Act
instance.addProduct(expectedProduct);
// Assert
verify(controller).saveProduct(Mockito.eq(PRODUCT_TABLE), Mockito.any(Item.class));
}
You should verify that the database is called within the service.. checking that it was invoked with any Item object should be enough.
Mocking is a tool that you only use for dependencies of the class that is being tested.
It appears that your test does not care about the Author, Media, and Product objects,
these are just dependencies of the method you want to test;
mock them.
Organization will greatly help your test;
do something like this:
public class TestKitchenService
{
private static String VALUE_PRODUCT_NAME = "VALUE_PRODUCT_NAME";
... use constants for other values as well. The value of the constant does not matter.
#InjectMocks
private KitchenService classToTest;
private InOrder inOrder;
#Mock
private Author mockAuthor;
#Mock
private DatabaseController mockDatabaseController;
#Mock
private Logger mockLogger;
#Mock
private Media mockMedia;
#Mock
private Product mockProduct;
#After
public void afterTest()
{
inOrder.verifyNoMoreInteractions();
verifyNoMoreInteractions(mockAuthor);
verifyNoMoreInteractions(mockDatabaseController);
verifyNoMoreInteractions(mockLogger);
verifyNoMoreInteractions(mockMedia);
verifyNoMoreInteractions(mockProduct);
}
#Before
public void beforeTest()
{
MockitoAnnotations.initMocks(this);
doReturn(mockAuthor).when(mockProduct).getAuthor();
doReturn(mockMedia).when(mockProduct).getMedia();
doReturn(VALUE_PRODUCT_NAME).when(mockProduct).getName();
doReturn(Collections.singletonList(ProductKind.MEAT)).when(mockProduct).getProductKinds();
... doReturns for the other product values.
inOrder = inOrder(
mockAuthor,
mockDatabaseController,
mockLogger,
mockMedia,
mockProduct);
ReflectionTestUtils.setField(
classToTest,
"databaseController",
mockDatabaseController);
ReflectionTestUtils.setField(
classToTest,
"logger",
mockLogger);
}
#Test
public void addProduct_success()
{
final Product actualResult;
actualResult = classToTest.addProduct(mockProduct);
assertEquals(
mockProduct,
actualResult);
inOrder.verify(mockProduct).getMedia();
inOrder.verify(mockProduct).getAuthor();
inOrder.verify(mockProduct).getProductKinds();
inOrder.verify(mockProduct).getName();
... inOrder.verify for the other product values.
inOrder.verify(mockDatabaseController).saveProduct(
eq(PRODUCT_TABLE),
any(Item.class));
}
}
The only things that should be mocked -- if anything -- are the ObjectMapper and databaseController. One only mocks collaborator objects, and almost never the system/class under test (very rare cases exist for "spying" on the SUT). And depending on what the ObjectMapper is and how transparent it's operation is, you may not really want to even mock that. Furthermore, as your implementation code is written instantiating the ObjectMapper by directly calling a constructor, you can't even mock it.
While I love the use of Mockito and mock objects, sometimes it is worthwhile to simply test with as many real objects as possible. This is especially true when your collaborators are simple, straightforward, have no side effects, and don't require complex initialization or setup. Only use mocks when it simplifies the test setup or verification.

How to Correctly mock an object with Mockito

I am trying to test the ClientDetails class below and trying to learn JUnit and Mockito at the same time.
// this is the class I'm trying to test
public class ClientDetails {
#Autowired private IApplicantService<Applicant> applicantService;
#Autowired private ClientDetailsHelpers helpers;
public FullClientDetails getClientDetails(String businessId, boolean isNewClient) {
FullClientDetails fcd = new FullClientDetails();
ClientParams params = new ClientParams();
params.setBusinessId(businessId);
ApplicantId ai = applicantService.retrieveApplicantIdFromBusinessId(params);
Long applicantId = ai.getApplicantId();
params.setApplicantId(applicantId);
Applicant applicant = applicantService.retrieveApplicantDetailsByHerdNumber(params);
helpers.validateRetrievedApplicant(applicant, isNewClient, businessId);
fcd.setApplicant(applicant);
// more method calls that get and assign objects to the fcd object
return fcd;
}
}
// ClientDetailsHelpers.java method that throws exception
public void validateRetrievedApplicant(Applicant applicant, boolean isNewClient, String businessId) {
if (applicant.getApplicantId() == null && !isNewClient) {
throw new ValidationSearchException(businessId);
}
}
// test class
#RunWith(MockitoJUnitRunner.class)
public class ClientDetailsTest {
private final static String BUSINESS_ID = "A1234567";
private final static Long APPLICANT_ID = null;
#InjectMocks
ClientDetails clientDetails;
#Mock ApplicantServiceImpl applicantService;
#Mock ClientDetailsHelpers helpers;
private ApplicantParams params;
private Applicant applicantWithInvalidId = new Applicant();
ApplicantId applicantId = new ApplicantId(APPLICANT_ID, BUSINESS_ID);
#Before
public void before(){
applicantWithInvalidId.setApplicantId(null);
}
#Test(expected = ValidationSearchException.class)
public void testGetFullApplicationDetails(){
when(applicantService.retrieveApplicantIdFromBusinessId(Mockito.any(ApplicantParams.class))).thenReturn(applicantId);
when(applicantService.retrieveApplicantDetailsByHerdNumber(Mockito.any(ApplicantParams.class))).thenReturn(applicantWithInvalidId);
FullClientDetails fcd = clientDetails.getFullClientDetails(BUSINESS_ID , false);
}
}
In my test class I create some mock objects, including an ApplicantId object to be returned when applicantService.retrieveApplicantIdFromBusinessId() is called and Applicant object with is applicantId attribute set to null to be return when applicantService.retrieveApplicantDetailsByHerdNumber() is called.
The function ClientDetailsHelper.validateRetrievedApplicant() should throw an exception if Applicant.getApplicantId() returns a null and if the boolean isNewClient is set to false however it doesn't seem to be happening in the test, it throws no exception and the #Test fails.
My best guess is that I am not using when().thenReturn() to correctly return the Applicant and ApplicantId objects I have created and instead another Applicant object is getting passed to validateRetrievedApplicant() and returning and applicantId of 0 when it gets to the validation method.
Any suggestions? Thanks!
Your code is not throwing an exception because there is nowhere in the code you are testing that throws that exception. I assume your exception is thrown within the ClientDetailsHelpers class but you are mocking this class so it will not call the actual code and so no exception will be thrown.
You need to think about what you want to test. Do you want to test the ClientDetails class in isolation as a unit? In which case you don't need to worry about the exception being thrown since its not part of the functionality of that class.
The second option is that you want to do more of an integration test where you pull in an actual ClientDetailsHelpers class but in order to do this you will need to include some configuration in your test to make sure that this bean is available to the test Spring context. You can do this using a Spring4JunitRunner instead of the Mockito one and then pulling in a configuration class with a component scan for your ClientDetailsHelpers class using the #ContextConfiguration(MyConfig.class) annotation on your test class where MyConfig is the relevant Spring config class.

how to unit test method using "Spring Data JPA" Specifications

I was playing with org.springframework.data.jpa.domain.Specifications, it's just a basic search :
public Optional<List<Article>> rechercheArticle(String code, String libelle) {
List<Article> result = null;
if(StringUtils.isNotEmpty(code) && StringUtils.isNotEmpty(libelle)){
result = articleRepository.findAll(Specifications.where(ArticleSpecifications.egaliteCode(code)).and(ArticleSpecifications.egaliteLibelle(libelle)));
}else{
if(StringUtils.isNotEmpty(code)){
result= articleRepository.findAll(Specifications.where(ArticleSpecifications.egaliteCode(code)));
}else{
result = articleRepository.findAll(Specifications.where(ArticleSpecifications.egaliteLibelle(libelle)));
}
}
if(result.isEmpty()){
return Optional.empty();
}else{
return Optional.of(result);
}
}
And that's actually working fine but I'd like to write unit tests for this method and I can't figure out how to check specifications passed to my articleRepository.findAll()
At the moment my unit test looks like :
#Test
public void rechercheArticle_okTousCriteres() throws FacturationServiceException {
String code = "code";
String libelle = "libelle";
List<Article> articles = new ArrayList<>();
Article a1 = new Article();
articles.add(a1);
Mockito.when(articleRepository.findAll(Mockito.any(Specifications.class))).thenReturn(articles);
Optional<List<Article>> result = articleManager.rechercheArticle(code, libelle);
Assert.assertTrue(result.isPresent());
//ArgumentCaptor<Specifications> argument = ArgumentCaptor.forClass(Specifications.class);
Mockito.verify(articleRepository).findAll(Specifications.where(ArticleSpecifications.egaliteCode(code)).and(ArticleSpecifications.egaliteLibelle(libelle)));
//argument.getValue().toPredicate(root, query, builder);
}
Any idea?
I was having almost the same problems as you had, and I changed my class that contains Specifications to be an object instead of just one class with static methods. This way I can easily mock it, use dependency injection to pass it, and test which methods were called (without using PowerMockito to mock static methods).
If you wanna do like I did, I recommend you to test the correctness of specifications with integration tests, and for the rest, just if the right method was called.
For example:
public class CdrSpecs {
public Specification<Cdr> calledBetween(LocalDateTime start, LocalDateTime end) {
return (root, query, cb) -> cb.between(root.get(Cdr_.callDate), start, end);
}
}
Then you have an integration test for this method, which will test whether the method is right or not:
#RunWith(SpringRunner.class)
#DataJpaTest
#Sql("/cdr-test-data.sql")
public class CdrIntegrationTest {
#Autowired
private CdrRepository cdrRepository;
private CdrSpecs specs = new CdrSpecs();
#Test
public void findByPeriod() throws Exception {
LocalDateTime today = LocalDateTime.now();
LocalDateTime firstDayOfMonth = today.with(TemporalAdjusters.firstDayOfMonth());
LocalDateTime lastDayOfMonth = today.with(TemporalAdjusters.lastDayOfMonth());
List<Cdr> cdrList = cdrRepository.findAll(specs.calledBetween(firstDayOfMonth, lastDayOfMonth));
assertThat(cdrList).isNotEmpty().hasSize(2);
}
And now when you wanna unit test other components, you can test like this, for example:
#RunWith(JUnit4.class)
public class CdrSearchServiceTest {
#Mock
private CdrSpecs specs;
#Mock
private CdrRepository repo;
private CdrSearchService searchService;
#Before
public void setUp() throws Exception {
initMocks(this);
searchService = new CdrSearchService(repo, specs);
}
#Test
public void testSearch() throws Exception {
// some code here that interact with searchService
verify(specs).calledBetween(any(LocalDateTime.class), any(LocalDateTime.class));
// and you can verify any other method of specs that should have been called
}
And of course, inside the Service you can still use the where and and static methods of Specifications class.
I hope this can help you.
If you are writing Unit Tests then you should probably mock the call to findAll() method of articleRepository Class using a mocking framework like Mockito or PowerMock.
There is a method verify() using which you can check if the mock is invoked for the particular parameters.
For Example, if you are mocking the findAll() method of articleRepository Class and want to know if this method is called with particular arguments then you can do something like:
Mokito.verify(mymock, Mockito.times(1)).findAll(/* Provide Arguments */);
This will fail the test if mock has not been called for the arguments that you provided.
Your problem is that you are doing too many things within that one method. You should have three different methods that work on articleRepository.
Then you can use mocking as the others suggest:
setup your mocks so that you know which call on articleRepository should be made
verify that exactly the expected calls are happening
Please note: these three methods should be internal; the main point there is: you can't test this method with ONE call from the outside; as it is doing more than one thing, depending on the input that you provide. Thus you need to create at least one test method for each of the potential paths in your code. And that becomes easier (from a conceptual point of view) when you separate your code into different methods.

Categories

Resources