Using a `when` as verification - java

I have a utility method used in hundreds of tests to mock the return value from a customised randomiser. Here's a (highly artificial) model of my code:
interface CardRandomiser{
Card getCard(Suit suit);
}
void mockCard(Suit suit, Face face) {
CardRandomiser rand = mock(CardRandomiser.class);
when(rand.getCard(suit)).thenReturn(new Card(suit, face));
Game.setCardRandomiser(rand);
}
This can then be used as:
mockCard(Suit.CLUBS, Face.QUEEN);
Card card = pickACardAnyCard();
assertThat(card.getFace(), is(Face.QUEEN));
However this makes some bugs a bit hard to pick up. If the method under test incorrectly asks for Suit.HEARTS then the mock returns null and the assertion correctly fails. But it's impossible to tell through the error message what was passed to the mock.
Clearly I could handle this with a verify. I could pass the mock back out of mockCard function and then separately verify that is was called with the correct value. But that really clutters up the tests assertions that are not really related to what's being tested. Given every time this method is called I am specifying an expected argument value I'd prefer to put the assertion in one place. Note that this all occurs before the method under test is called.
Ideally I'd like the when statement to throw an exception if it's called with an unexpected value. Something like:
when(rand.getCard(suit)).thenReturn(new Card(suit, face));
when(rand.getCard(intThat(not(is(suit))))).thenThrow(new IllegalArgumentException());
This works and stops the test when getCard is called which is better. But it still doesn't allow me to show what the incorrect argument was.
I also tried it using an ArgumentCaptor and then checking the captured value. But it's clear they are for verify statements rather than when statements.
Is there a standard Mockito way of doing this, or do I need to clutter my tests with verify statements?

You can configure mockito answer using thenAnswer, e.g.
private CardRandomiser mockCard(final Suit suit, final Face face) {
CardRandomiser rand = mock(CardRandomiser.class);
when(rand.getCard(any(Suit.class))).thenAnswer(new Answer<Card>() {
#Override
public Card answer(InvocationOnMock invocation) throws Throwable {
if(!suit.equals(invocation.getArguments()[0])) {
throw new IllegalArgumentException(
String.format("Value %s passed, but mocked for %s", invocation.getArguments()[0], suit));
}
return new Card(suit, face);
}
});
return rand;
}

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);
});
}

Mockito: No such instance method

I'm writing testcases for below given method.
Method:
#Override
public void removeAllConnections(String uuid, String userName, String oimId) {
customLogger.debug(Thread.currentThread().getStackTrace()[1].getMethodName(), userName, null, null, accessProviderBuilder.getUserName(), accessProviderBuilder.getUuid());
UserAccessBean userAccessBean = new UserAccessBean(userName);
userAccessBean.setOimid(oimId);
userAccessBean.setToken("");
log.info("removeAllConnections:oimid:"+userAccessBean.getOimId());
UserProfileDetailBean userProfileDetail = accessClient.getAccess(userAccessBean,applicationForCsr);
Set<AccountAccess> accountAccesses = userProfileDetail.getAccountAccessList();
try {
removeAllConnectionsExceptPrimary(oimId, userName, accountAccesses);
removePrimaryConnection(oimId, userName, accountAccesses);
} catch (ConnectionStateException e) {
throw new ConnectionStateException(ConnectionNameNotRemoved, CONNECTION_REMOVAL_FAILED_MSG);
} catch (InternalServerErrorException e) {
throw new InternalServerErrorException(INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR_MSG);
}
}
Below snippet is test case for given method.
Testcase:
#Test
public void testRemoveAllConnections() {
UserAccessBean userAccessBean = new UserAccessBean(userName);
when(accessClient.getAccess(userAccessBean,"CSR")).thenReturn(userProfileDetail);
when(userProfileDetail.getAccountAccessList()).thenReturn(accountAccesses);
String applicaionForCSR = "CSR";
ReflectionTestUtils.setField(service, "applicationForCsr", applicaionForCSR);
service.removeAllConnections(uuid, userName, oimId);
}
While debugging the code, my execution is failing at below given line as the value of userProfileDetail is null.
Set<AccountAccess> accountAccesses = userProfileDetail.getAccountAccessList();
While doing inspect element on accessClient.getAccess(userAccessBean,applicationForCsr) it is throwing below error. Pretty sure it is some silly mistake but unable to trace it.
Error:
No such instance method: 'UserProfileDetailBean
v1.AccessService$$EnhancerByMockitoWithCGLIB$$a852895d.getAccess
(UserAccessBean)'
Application: Spring Boot 1.5.0
Library: Mockito 2.7.X
I can suggest three possible solutions (or more like 2.5):
a) Override the equals method of UserAccessBean, so that two UserAccessBeans are equal if and only if their names are equal. Of course, this might interfere with your productive code and I would not change the equals method only for testing.
b) Since the username doesn't actually play a vital role in your test (the tests itself defines what the username is), you can simply ignore the details with...
when(accessClient.getAccess(Mockito.any(UserAccessBean.class),Mockito.eq("CSR"))).thenReturn(userProfileDetail);
This way, the userProfileDetail will be returned for any value of the first parameter. Of course, you lose detail here, so for example, the test would be correct if the username was somehow wrong, but chances are that this isn't possible in your test anyway.
Mockito.any(...) is a so called matcher that tells Mockito to "use" this rule no matter what value is given for the parameter in question. Anything you put there is ok for Mockito. Mockito.eq("CSR") tells it, that this parameter must be equal to "CSR". So, the whole rule is...
If someone calls accessClient.getAccess, no matter what the first parameter ist, but the 2nd must be equal to "CSR", then return userProfileDetail.
So, with this, the first parameter can be anything. So, for example, the following to calls will be accepted:
accessClient.getAccess(new UserAccessBean("correct name"), "CSR");
accessClient.getAccess(new UserAccessBean("totally wrong name"), "CSR");
...because it does not matter what the first parameter is, ANY value will be accepted. So, what you "lose" there is the ability to check if the UserAccessBean is the correct one (because any is accepted). But in your case, since you only define those UserAccessBeans in the test anyway, this should not be a problem.
But if it is, I can offer two workarounds...
c) Use either a customer Matcher (that checks the name of the UserAccessBean) or use the Mockito.any(...) as above and an ArgumentCaptor to check if the name was correct in the end...
ArgumentCaptor<UserAccessBean> captor = ArgumentCaptor.forClass(UserAccessBean.class);
Mockito.verify(accessClient).getAccess(captor.capture(),Mockito.eq("CSR"));
assertEquals(captor.getValue().getName(), "myName");

Test method that returns void

I have a void method and I want to test it. How do I do that?
Here's the method:
public void updateCustomerTagCount() {
List<String> fileList = ImportTagJob.fetchData();
try {
for (String tag : fileList) {
Long tagNo = Long.parseLong(tag);
Customer customer = DatabaseInterface.getCustomer(tagNo);
customer.incrementNoOfTimesRecycled();
DatabaseInterface.UpdateCustomer(customer);
}
} catch(IllegalArgumentException ex) {
ex.printStackTrace();
}
}
when the method returns void, you can't test the method output. Instead, you must test what are the expected consequences of that method. For example:
public class Echo {
String x;
public static void main(String[] args){
testVoidMethod();
}
private static void testVoidMethod() {
Echo e = new Echo();
//x == null
e.voidMethod("xyz");
System.out.println("xyz".equals(e.x)); //true expected
}
private void voidMethod(String s) {
x = s;
}
}
It might not be always true, but basic concept of unit test is to check if function works as expected and properly handling errors when unexpected parameters/situation is given.
So basically unit test is against the functions that takes input parameters and return some output so we can write those unit test.
The code like yours, however, includes some other dependency (database call) and that's something you can't execute unless you write integration-test code or real database connection related one and actually that's not recommended for unit test.
So what you need to do might be introducing unit test framework, especially Mockto/Powermock or some other stuff that provides object mocking feature. With those test framework, you can simulate database operation or other function call that is going to be happening outside of your test unit code.
Also, about how do I test void function, there is nothing you can with Assert feature to compare output since it returns nothing as you mentioned.
But still, there is a way for unit test.
Just call updateCustomerTagCount() to make sure function works. Even with just calling the function, those unit test can raise your unit test coverage.
Of course for your case, you need to mock
ImportTagJob.fetchData();
and
DatabaseInterface.getCustomer(tagNo);
and have to.
Let mocked
ImportTagJob.fetchData();
throw empty list as well as non-empty list and check if your code works as you expected. Add exception handling if necessary. In your code, there are two condition depends on whether fieList are null or non-null, you need to test it.
Also, mock those objects and let them throw IllegalArgumentException where you expect it to be thrown, and write an unit test if the function throws a exception. In Junit, it should be like
#Test(expected = IllegalArgumentException.class)
public void updateCustomerTagCountTest(){
// mock the objects
xxxxx.updateCustomerTagCount();
}
That way, you can ensure that function will throw exception properly when it has to.

Correct helper method exception throwing

I have functions like
class Chainable {
public Chainable doStuff(String test) {
return doSomething(test, true);
}
public Chainable doStuff(String test, String test2) {
String toUse = test + test2;
return doSomething(toUse, false);
}
private Chainable doSomething(String test, boolean version) {
// do something
if (somethingBadHappened) {
throw SpecialException.generate();
}
return this;
}
}
SpecialException is an exception the user is supposed to see. The message of the exception purposely contains the method that threw this exception. The user would call doSomething("x") and if it fails it would show "Method 'doSomething' failed with the parameters: 'test = x | version = true'".
But the user doesn't care about the method doSomething(String, boolean) and its parameters. He used doStuff(String) and wants to see the message for that function.
So what I do is:
public Chainable doStuff(String test) {
try {
return doSomething(test, true);
} catch (SpecialException e) {
throw SpecialException.generate(e);
}
}
which sets e as the cause of the new exception and correctly shows "Method 'doStuff' failed with the parameters: 'test = x'" (the user doesn't see the stacktrace, but if I need to debug I can see what exactly happened).
Now, it works, but I have to repeat myself every time I write a new function that delegates its work to helper functions. The problem is, I don't know how I should use a helper function for this, since the SpecialException finds the method name depending on where it is generated...
Is there another, better way to do this?
Here's what you should do: show the stack trace to the user. You can then use
throw new SpecialException();
without any concerns about what method will appear.
As far as I can see, and as far as I know, this might be a nice situation to handle using aspect-oriented programming or AOP. In Java you can use AspectJ for that.
You would start by defining a "pointcut" - a specific moment in the execution of your code, for example a method invocation. Next, you would write an "advice", something that needs to be done when the pointcut is hit. You put those two together in an "aspect" and have them weaven into your bytecode during build.
In this case, you would need a pointcut that intercepts call to public methods on your Chainable, but the call should not be done from within Chainable:
pointcut publicChainableMethod() : target(Chainable)
&& call(public * *(..))
pointcut firstPublicChainableMethod() : target(Chainable)
&& call(* *(..))
&& !cflowbelow(publicChainableMethod());
The first pointcut defines any call to a public method on Chainable, the second pointcut defines a call to a method within Chainable, except it should not be called while being in the control flow of the first pointcut.
Next to that, you would need an advice that generates the new SpecialException for you:
after() throwing (SpecialException e): firstPublicChainableMethod() {
throw SpecialException.generate(e);
}
Disclaimer: I'm no AOP or AspectJ expert, so this approach might not work out-of-the-box.

Simulate first call fails, second call succeeds

I want to use Mockito to test the (simplified) code below. I don't know how to tell Mockito to fail the first time, then succeed the second time.
for(int i = 1; i < 3; i++) {
String ret = myMock.doTheCall();
if("Success".equals(ret)) {
log.write("success");
} else if ( i < 3 ) {
log.write("failed, but I'll try again. attempt: " + i);
} else {
throw new FailedThreeTimesException();
}
}
I can setup the success test with:
Mockito.when(myMock).doTheCall().thenReturn("Success");
And the failure test with:
Mockito.when(myMock).doTheCall().thenReturn("you failed");
But how can I test that if it fails once (or twice) then succeeds, it's fine?
From the docs:
Sometimes we need to stub with different return value/exception for the same method call. Typical use case could be mocking iterators. Original version of Mockito did not have this feature to promote simple mocking. For example, instead of iterators one could use Iterable or simply collections. Those offer natural ways of stubbing (e.g. using real collections). In rare scenarios stubbing consecutive calls could be useful, though:
when(mock.someMethod("some arg"))
.thenThrow(new RuntimeException())
.thenReturn("foo");
//First call: throws runtime exception:
mock.someMethod("some arg");
//Second call: prints "foo"
System.out.println(mock.someMethod("some arg"));
So in your case, you'd want:
when(myMock.doTheCall())
.thenReturn("You failed")
.thenReturn("Success");
The shortest way to write what you want is
when(myMock.doTheCall()).thenReturn("Success", "you failed");
When you supply mutiple arguments to thenReturn like this, each argument will be used at most once, except for the very last argument, which is used as many times as necessary. For example, in this case, if you make the call 4 times, you'll get "Success", "you failed", "you failed", "you failed".
Since the comment that relates to this is hard to read, I'll add a formatted answer.
If you are trying to do this with a void function that just throws an exception, followed by a no behavior step, then you would do something like this:
Mockito.doThrow(new Exception("MESSAGE"))
.doNothing()
.when(mockService).method(eq());
I have a different situation, I wanted to mock a void function for the first call and run it normally at the second call.
This works for me:
Mockito.doThrow(new RuntimeException("random runtime exception"))
.doCallRealMethod()
.when(spy).someMethod(Mockito.any());
To add on to this and this answer, you can also use a loop to chain the mocked calls. This is useful if you need to mock the same thing several times, or mock in some pattern.
Eg (albeit a farfetched one):
import org.mockito.stubbing.Stubber;
Stubber stubber = doThrow(new Exception("Exception!"));
for (int i=0; i<10; i++) {
if (i%2 == 0) {
stubber.doNothing();
} else {
stubber.doThrow(new Exception("Exception"));
}
}
stubber.when(myMockObject).someMethod(anyString());
The shortest would be
doReturn("Fail", "Success").when(myMock).doTheCall();

Categories

Resources