Not able to Mock Java method call using Spock - java

I am trying to mock my following java method using Spock.
public List<DiffEntry> listDifferences(String oldCommit, String newCommit, Git git)
throws GitAPIException, RevisionSyntaxException, AmbiguousObjectException,
IncorrectObjectTypeException, IOException {
logger.info(
"Inside DiffCommits#listDifferences to calculating difference commits refs {} and {} ",
oldCommit, newCommit);
ObjectId oldTree = git.getRepository().resolve(oldCommit);
ObjectId newTree = git.getRepository().resolve(newCommit);
if (oldTree == null || newTree == null) {
logger.warn(
"Could not resolve either old {} or new commits {}, difference cant not be calculated",
oldCommit, newCommit);
throw new RefNotFoundException("Unable to resolve tag reference. Invalid tag provided");
}
ObjectReader reader = git.getRepository().newObjectReader();
CanonicalTreeParser oldTreeIter = new CanonicalTreeParser();
oldTreeIter.reset(reader, oldTree);
CanonicalTreeParser newTreeIter = new CanonicalTreeParser();
newTreeIter.reset(reader, newTree);
DiffFormatter df = new DiffFormatter(new ByteArrayOutputStream());
df.setRepository(git.getRepository());
List<DiffEntry> entries;
entries = df.scan(newTreeIter, oldTreeIter);
df.close();
if (logger.isDebugEnabled()) {
for (int i = 0; i < entries.size(); i++) {
logger.debug("Entry: " + entries.get(i));
}
}
return entries;
}
Everything is working fine but the mocking of following code get failed
DiffFormatter df = new DiffFormatter(new ByteArrayOutputStream());
And getting error at
df.setRepository(git.getRepository());
The error that I am getting is
> org.eclipse.jgit.lib.StoredConfig$$EnhancerByCGLIB$$9a2f8398 cannot be
> cast to org.eclipse.jgit.diff.DiffConfig java.lang.ClassCastException:
> org.eclipse.jgit.lib.StoredConfig$$EnhancerByCGLIB$$9a2f8398 cannot be
> cast to org.eclipse.jgit.diff.DiffConfig at
> org.eclipse.jgit.diff.DiffFormatter.setReader(DiffFormatter.java:201)
> at
> org.eclipse.jgit.diff.DiffFormatter.setRepository(DiffFormatter.java:180)
> at
> com.sf.bt.mdm.subscription.scmdiff.DiffCommits.listDifferences(DiffCommits.java:65)
> at com.sf.bt.mdm.subscription.service.DiffCommitSpec.test list
> differences(DiffCommitSpec.groovy:59)
Any kind of help will be appreciated

DiffFormatter df = new DiffFormatter(new ByteArrayOutputStream());
This code is problematic from the point of view of unit testing. Its not a dependency so it can't be injected in the class. So basically you're trying to mock a constructor (new) object.
Technically the only way you can try is using Global Mocks and create spies wrapping the real object: Read the documentation. These are not regular spock's Mocks and Stubs.
However I believe there are other approaches. After all the link I've sent you starts with "think twice before using..." for a reason ;)
You've by yourself decided to put this object creation inside the method, which is ok. However this means that you do not treat this DiffFormatter as a real dependency. So maybe even in unit test you should let it run, concentrating on mocking its input parameters themselves? Just the same holds also for ByteArrayOutputStream. This plays nicely in general with the concept that the code should be written in a way that unit tests should not really care about the internal implementation of the methods. Always prefer blackbox testing to white-box testing.
Alternatively if you think its a dependency, then maybe you should make it a data field. Then You could inject it into the test with a regular mocks. If this class should be created per method invocation, you can inject Supplier<DiffFormatter> and in method just call it's get method, it will act as a factory.

Related

If you want to use assertThrows while testing, should you do that with stubs or mocks?

I have this Method that throws an IllegalArgumentException when somebody tries to call it with value 0.
I want to write several stub and mock tests - for example - for the method getFrequentRenterPoints.
I coudn't figure out any "when" or "verify" statements which are used in mocks so I mixed parts of mocks and parts of stubs together and came up with this:
#Test
public void methodGetFrequentRenterPointsShouldThrowIllegalArgumentException() {
//given
Movie movieMock = mock(Movie.class);
//when
movieMock.getFrequentRenterPoints(0);
//then
assertThrows(IllegalArgumentException.class, () -> {
movieMock.getFrequentRenterPoints(0);
});
}
Is it okay to have in a class with other Mocks, or if I want to use assertThrows should I change this into a stub? Or can I use assertThrows with mocks?
The answer from Benjamin Eckardt is correct.
But I try to approach this question from another point of view: when to use mocking? This is one of my favourite answers to that question.
So in practise:
Say your code is like (just guessing all the business objects & names...):
List<RenterPoints> getFrequentRenterPoints(int renterId) {
if(p <= 0) {
throw new IllegalArgumentException();
}
// this is just the rest of code in which your test does not enter because
// of thrown exception
return somethingToReturn();
}
For this you do not need and you should not want to mock anything here.
But when things get more complicated like your method would be like:
List<RenterPoints> getFrequentRenterPoints(int renterId) {
if(p <= 0) {
throw new IllegalArgumentException();
}
// What is this?
// It is injected in the Movie - say - like
//
// #Resource
// private RenterPointService renterPointService;
List<RenterPoints> unfiltered = renterPointService.getRenterPoints(renterId);
return filterToFrequent(unfiltered);
}
Now if you test renterId >= 1 what about this renterPointService how do you instantiate it to not get NPE? Say if it is injected and requires to pull up heavy framework for testing or it requires very heavy construction or so? You do not, you mock it.
You are testing the class Movie not the class RenterPointService so you should not bother to think how RenterPointService works but what it returns when used in the class Movie. Still: you do not mock the class Movie which you are testing.
Assuming using you are using Mockito and using annotations the mocking would be then done in your test class like:
#Mock
private RenterPointService renterPointService;
#InjectMocks
private Movie movie;
Then you would do mocking of methods for renterPointService like:
when(renterPointService.getRenterPoints(anyInt))
.thenReturn(someListContaineingMockRenterPointsForThisTest);
Usually you expect the tested production method to throw and not the mock or stub. I drafted it by using new Movie().
Furthermore in that case it does not really make sense to separate the calls into when and then because if movieMock.getFrequentRenterPoints(0); throws, assertThrows(...) will never be executed.
To apply the given/when/then structure with the assertThrows API you could extract the passed lambda in some way, but I personally don't see much benefit in it.
#Test
public void methodGetFrequentRenterPointsShouldThrowIllegalArgumentException() {
// given
Movie movieMock = new Movie();
// when/then
assertThrows(IllegalArgumentException.class, () -> {
movieMock.getFrequentRenterPoints(0);
});
}

How can I test in this situation in java?

I'm making a test for a service with a mock.
The problem is to create and inject instance directly from the class to test.
The source is shown below.
public OrderOutDTO createOrder(OrderSessionDTO orderSessionDTO) {
Order order = orderRepository.save(new Order(orderSessionDTO));
CreateOrderResDTO callServiceOrder = callService.createOrder(new CreateOrderReqDTO(order));
CreateOrderReqDTO createOrderReqDTO = mock(CreateOrderReqDTO.class);
createTrace(order, callServiceOrder.getData().getReceipt().getTransactionHash(), Trace.PUBLIC);
return new OrderOutDTO(order, null);
}
and test source is shown below.
#Test
public void createOrder() {
// given
CallService callService = mock(CallService.class);
CreateOrderResDataDTO createOrderResDataDTO = mock(CreateOrderResDataDTO.class);
// when
when(callService.createOrder(createOrderReqDTO)).thenReturn(createOrderResDTO);
OrderOutDTO order = orderService.createOrder(orderSessionDTO);
// then
assertThat(order, is(Matchers.notNullValue()));
assertThat(order.getOrder(), is(Matchers.notNullValue()));
assertThat(order.getOrder().getReceiver().getName(), is("test"));
}
I thought this test would finish well. But in the code below, it returned null and failed.
// callService.createOrder(new CreateOrderReqDTO(order)) return null
CreateOrderResDTO callServiceOrder = callService.createOrder(new CreateOrderReqDTO(order));
It doesn't seem to recognize it because the service injects a new instance. I want the mock data returned. What should I do?
In the following line you're mocking behavior on createOrderReqDTO as param:
when(callService.createOrder(createOrderReqDTO)).thenReturn(createOrderResDTO);
whereas further, you're passing some other object:
OrderOutDTO order = orderService.createOrder(orderSessionDTO);
This behavior is not recognized, you would have to pass the same thing you mocked before.
I found it myself!
I use argumentMatchers.
when(callService.createOrder(createOrderReqDTO)).thenReturn(createOrderResDTO);
to
when(callService.createOrder(any())).thenReturn(createOrderResDTO);
thank you.

junit test case for my api

i am new to writing junits.I have my below java api which gets a unique value every time from database.It contains just a single query.I need to write junit for below api.can anybody give some suggestions how should i approach??
public static int getUniqueDBCSequence() throws Exception
{
int w_seq = 0;
QueryData w_ps = null;
ResultSet w_rs = null;
try
{
w_ps = new QueryData("SELECT GETUNIQUENUMBER.NEXTVAL FROM DUAL");
w_rs = SQLService.executeQuery(w_ps);
while ( w_rs.next() )
{
w_seq = w_rs.getInt(1);
}
}
catch (Exception a_ex)
{
LOGGER.fatal("Error occured : " + a_ex.getMessage());
}
finally
{
SQLService.closeResultSet(w_rs);
}
return w_seq;
}
You are using only static methods : in the class under test but also in the dependencies of it.
It is really not a testable code with JUnit.
Besides, what you do you want to test unitary ?
Your test has no substantive logic.
You could make SQLService.executeQuery() a method instance to be able to mock it. But really which interest to mock it ?
To assert that the result is returned w_seq = w_rs.getInt(1); ?
It looks like technical assertions that have few value and maintaining unit tests with few value should be avoided.
Now, you could test with DBunit or tools to populate a in memory database and executes the code against.
But the query executed have a strong coupling with Oracle sequences.
So, you could have some difficulties to do it.

How to write a Junit test case for a method in springboot without hardcoding

New to junit test case and want to know the test case for my code here
without hardcoding values.
My code is
public JSONObject getListOfAllForms() {
List<Forms> forms = FormName.getInstance().getformList();
int totalNumberOfforms = 0;
List<String> formIds = new ArrayList<String>();
try {
for (int i = 0; i < forms.size(); i++) {
form formOb = forms.get(i);
formIds.add(formOb.getformId());
totalNumberOfforms = forms.size();
}
} catch (Exception e) {
e.printStackTrace();
}
JSONObject formsListObject = new JSONObject();
formsListObject.put("formIds", formIds);
formsListObject.put("totalNumberOfforms", totalNumberOfforms);
return formsListObject;
}
My controller code is:
#RequestMapping(value = "/new/getforms/{formid}", method = RequestMethod.GET)
public JSONObject getFormByFormId(#PathVariable("formid") String formid) {
return newFormName.getformByformId(formid);
}
If you want to test getListOfAllForms Your problem is basically this line...
List<Forms> forms = FormName.getInstance().getformList();
This line couples your FormName hard into your method. This is bad.
A better way would be to provide the FormName instance when instantiating your class, for example, let's assume the name of your class was MyClass..
private FormName formName;
public MyClass(FormName formName) {
this.formName = formName;
}
This can be used via Spring or manually. Via Spring your FormName instance just needs to be a bean, then you can add #Autowired to the constructor if MyClass itself is a bean.
What is the advantage now? Easy, for a test case, you can now do the simple thing...
public void someGreatTestName() {
FormName formName = ...; // either create a new, fake one or mock one (see below)
JSONObject object = new MyClass(formName).getListOfAllForms();
// now test that the object is correct
}
Doing this means you can inject a fake or mock FormName there, which will return the data you want for the test and so remove the need to have an actual "live" FormName there. In other words, you can fake/mock the dependency to the FormName. This way, your test uses test data and you don't have to hardcode the live values there.
Mocking can be done easily via Mockito, for example, I suggest giving it a try, but in this case, simply creating a new "fake" FormName may suffice.

Mockito/Realm (when-then): how to pass arguments from method to its submethod

Trying to unit test a project that uses Realm. I stubbed realm methods to just test my own code and found a problem with RealmQuery. I want to test whether an object is (1) added to the Realm db; (2) can be retrieved; (3) if that object's set attribute matches what I expect. Here are parts of my setup() and Test.
How I stub a realm database (someList is global & List<>) in setup()
SomeRealmObject some1;
some1.setId(1);
some1.setName("some1");
SomeRealmObject some2;
some2.setId(2);
some2.setName("some2");
someList = new ArrayList<SomeRealmObject>();
someList.add(some1);
someList.add(some2);
How I stub copying to Realm (add function) in setup()
when(mockRealm.copyToRealm).then(new Answer() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
SomeRealmObject some = (SomeRealmObject) args[0];
userList.add(user);
return user;
}
});
How I stub RealmQuery (search function) in setup()
RealmQuery someRealmQuery = someRealmQuery(); //followed mockito example on github
when(mockRealm.where(SomeRealmObject.class)).thenReturn(someRealmQuery);
when(realmQuery.equalsTo(anyString, anyInt).thenReturn(someRealmQuery);
when(realmQuery.findFirst()).then(findFirstAnswer);
Problem starts here. I need realmQuery.equalsTo(...) to pass its arguments to the next method in the chain. I think it necessary (but I may be wrong) because I should test two methods that follow: findFirst() and findAll(). Any Ideas?
How I stub findFirst() in setup()
Answer findFirstAnswer = new Answer(){
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
String key = args[0].toString(); //Let's just use an Id search for
int id = (int) args[1]; //an example.
for(int count = 0; someList.get(count) != null; count++){
if(someList.get(count).getId == id){
return someList.get(count);
}
return null; //test will fail, someRealmObject not found
}
}
How I unit test my createSomeObject (e.g. createAccount)
#Test
public void create_someObj_test() {
String expectedReturnedName = "someName";
String actualReturnedName;
SomeRepositoryImpl manager; //Class with business logic (mvp pattern)
SomeRepositoryImpl.initialize();
manager = someRepositoryImpl.getInstance();
SomeRealmObject some = new SomeRealmObject();
some.setID(6);
some.setName(expectedReturnedName);
//mock adding user to realm, should actually add it to a list
mockRealm.beginTransaction();
mockRealm.copyToRealm(some);
mockRealm.commitTransaction();
actualReturnedName = mockRealm.where(SomeRealmObject.class).equalTo("id", some.getId()).findFirst().getName().toString();
//PASS if object exists and name matches
//FAIL if name does not match
//FAIL if nullPointerException because no match/object not found
assertEquals(expectedReturnedName, actualReturnedName );
}
This is not a direct answer to your question. A direct answer would involve a discussion of what findFirstAnswer is and what its name property contains.
Instead, though, I would ask: "what are you trying to test"? This is not quite a test of the Realm DB library (a good thing). It looks to me almost like a test of Mockito! If the test succeeds, you will know that Mockito Mocks can return an object with a certain name.
It is common practice to wrap a data layer in a very thin API, something like the Data Access Objects popular with Spring and the like. If you can mock the data API, you can test the heck out of your business layer. If your data API needs testing, you can also test things like "does this API call get translated into the proper query?" or "does the API crash if the result is empty?".
Sorry for the oblique answer but I think if you revisit the question of what you are trying to test, this entire problem might evaporate.

Categories

Resources