We are using Testng 6.8.8 + Mockito 1.10.19 and, obviously we can't use MockitoJUnitRunner, but the validation framework still works!
In this thread I read that it should not be the case. Can someone explain? Is this because we still have #Before* callbacks and MockitoAnnotations.initMocks(this)?
My code:
public class MealTransformerTest {
MealTransformer mealTransformer = new MealTransformer();
#Test(expectedExceptions = NotImplementedException.class)
public void shouldThrowException() throws NotImplementedException {
mealTransformer.transform(any(),
any(),
any(),
any());
}
}
Nothing fails in this specific test, but when I run the suite, Mockito will tell me about incorrect use of matchers.
I can also do something like:
public class MealTransformerTest {
MealTransformer mealTransformer = new MealTransformer();
//I don't need it in the tests. But I need at least 1 #Mock or #Spy to trigger framework validation
#Mock
private CloneUtils cloneUtils;
#BeforeMethod
void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test(expectedExceptions = NotImplementedException.class)
public void shouldThrowException() throws NotImplementedException {
mealTransformer.transform(any(),
any(),
any(),
any());
}
#Test(expectedExceptions = NotImplementedException.class)
public void shouldThrowException123() throws NotImplementedException {
mealTransformer.transform(any(),
any(),
any(),
any());
}
}
I receive:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Misplaced argument matcher detected here:
....
Don't get me wrong, I really like how it works, but I was surprised to see it without #RunWith(MockitoJUnitRunner.class).
Matchers work via side-effects and static global state, so you can break this call into pieces to see what's happening.
MockitoAnnotations.initMocks(this);
// in #Before [1]
mealTransformer.transform(any(), any(), any(), any());
// [6] [2] [3] [4] [5]
After init [1], Java sees four calls to any [2-5] and one call to transform [6]. Mockito, however, only sees the four calls to any; Because mealTransformer is not a mock, Mockito can't see its calls. If you ran that test in isolation, Mockito would be left with four recorded matchers that are not consumed. The JVM would tear down the test framework, and the test would pass—unless you have a call to validateMockitoUsage in an #After method, which would catch exactly that case.
When you have two tests, however, they stack up like this:
MockitoAnnotations.initMocks(this);
// [1]
mealTransformer.transform(any(), any(), any(), any());
// [6] [2] [3] [4] [5]
// test passes! now the next test
MockitoAnnotations.initMocks(this);
// [7]
mealTransformer.transform(any(), any(), any(), any());
// [12] [8] [9] [10] [11]
Here, step 7 happens in the same test execution in the same JVM as steps 1-6, which means that initMocks is called when there are 4 unconsumed matchers on the stack. This should never happen, so initMocks takes its first opportunity to catch that error. One symptom would be if the misused matcher exception corresponds to a matcher outside of the test that fails: testB failing due to the misuse of a matcher in testA. With the use of validateMockitoUsage in an #After message, you'd have the appropriate test fail instead.
Related
I have fairly simple Test case below which is passing when I use the Mockito 1.19 version but failing when I switched to Mockito 3.x.
#Test(expected = DBMapperRetryableException.class)
public void testUpdateInventoryWithException() throws DBMapperRetryableException, DBMapperNonRetryableException {
when(baseDBOperations.partialUpdate(anyString(), any(PrimaryKey.class), anyString(), anyString(), any(ReturnValue.class), any(ValueMap.class))).thenThrow(new DBMapperRetryableException("Exception", new Throwable()));
assertNull(prebookInventoryDao.updateInventory(PrebookInventoryUpdateInfo.builder().hashKey(DUMMY).sortKey(123).incrementValue(1).build()));
// verify(baseDBOperations, times(1)).partialUpdate(anyString(), any(PrimaryKey.class), anyString(), anyString(), any(ReturnValue.class), any(ValueMap.class));
}
Above shows the below message when I run it on Intellij:
[MockitoHint] PrebookInventoryDaoTest.testUpdateInventoryWithException (see javadoc for MockitoHint):
[MockitoHint] 1. Unused... -> at com.amazon.pbxcoreservice.dao.PrebookInventoryDaoTest.testUpdateInventoryWithException(PrebookInventoryDaoTest.java:68)
[MockitoHint] ...args ok? -> at com.amazon.pbxcoreservice.dao.PrebookInventoryDao.updateInventory(PrebookInventoryDao.java:51)
Error shown: java.lang.AssertionError: Expected exception: com.amazon.pbxcoreservice.dynamodb.exception.DBMapperRetryableException
Here's the PrebookInventoryDao class. I've skipped a few methods like getUpdateExpression, getPrimaryKey.
public class PrebookInventoryDao {
private final BaseDBOperations baseDBOperations;
public PrebookInventoryDao(final BaseDBOperations baseDBOperations) {
this.baseDBOperations = baseDBOperations;
}
public UpdateItemOutcome updateInventory(PrebookInventoryUpdateInfo prebookInventoryUpdateInfo)
throws DBMapperRetryableException, DBMapperNonRetryableException {
ConditionalExpressionRequest conditionalExpressionRequest = prebookInventoryUpdateInfo.getConditionalExpressionRequest();
String conditionalExpression = conditionalExpressionRequest != null ? conditionalExpressionRequest.getQuery() : null;
return baseDBOperations.partialUpdate(DynamoDBConstants.PREBOOK_INVENTORY.TABLE,
getPrimaryKey(prebookInventoryUpdateInfo), getUpdateExpression(),
conditionalExpression, ReturnValue.UPDATED_NEW, getValueMap(prebookInventoryUpdateInfo, conditionalExpressionRequest));
}
Could anyone help as to why such discrepancy?
PS: Looks like when I use when(baseDBOperations.partialUpdate(any(), any(), any(), any(), any(), any())).thenThrow(new DBMapperRetryableException("Exception", new Throwable()));
, then the test passes. Not sure why when(baseDBOperations.partialUpdate(anyString(), any(PrimaryKey.class), anyString(), anyString(), any(ReturnValue.class), any(ValueMap.class))).thenThrow(new DBMapperRetryableException("Exception", new Throwable())); isn't working.
Trying mock void method using JUnit4 then getting below exception
Below is the class definition
#Mock
private TestService service;
#Mock
private DatabaseService mockDatabase;
#Before
public void setUpMock() throws Exception {
LOGGER.info("########### Moke setUp started ###########");
conn = Mockito.mock(Connection.class);
MockitoAnnotations.initMocks(this);
LOGGER.info("########### Moke setUp completed ###########");
}
#Test
public void testSuccess() {
try {
when(mockDatabase.getDBConnection()).thenReturn(conn);
Mockito.doNothing().when(service).deleteKeyInfo(mockDatabase.getDBConnection(), userBn, 1, "1");
} catch (Exception e) {
fail("### testDeviceIdNull ### Failed with following error: " + getStackTrace(e));
}
}
But getting below exception
java.lang.AssertionError: ### Failed with following error: org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, which is not supported
3. you are stubbing the behaviour of another mock inside before 'thenReturn' instruction is completed
Below is the void method
public void deleteKeyInfo(Connection conn, UserBn userBn, Integer smId, String dId) throws Exception {
// Deleting key
}
You're nesting mocking inside of mocking. You're calling getDBConnection(), which does some mocking, before you've finished the mocking for service.
Mockito doesn't like it when you do this.
Replace:
try {
when(mockDatabase.getDBConnection()).thenReturn(conn);
Mockito.doNothing().when(service).deleteKeyInfo(mockDatabase.getDBConnection(), userBn, 1, "1");
}
with:
try {
when(mockDatabase.getDBConnection()).thenReturn(conn);
some_variable_name = mockDatabase.getDBConnection();
Mockito.doNothing().when(service).deleteKeyInfo(some_variable_name, userBn, 1, "1");
}
Please check this : Unfinished Stubbing Detected in Mockito for more information
Following is the Test Class
#RunWith(MockitoJUnitRunner.class)
public class AuditServiceClientTest {
private MockMvc mockMvc;
#Mock
private RestTemplate restTemplate;
#Mock
AuditServiceClient auditServiceClient;
#Mock
ICommonDataService iCommonDataService;
private AuditServiceResponse auditServiceResponse;
private AuditServiceLog auditServiceLog;
private HttpEntity<AuditServiceLog> request;
#Before
public void setUp() throws Exception {
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("X-AGS-Client-Name", "test");
headers.add("X-AGS-Group-Name", "test");
headers.add("Content-Type", "application/json");
auditServiceClient = new AuditServiceClientImpl();
iCommonDataService = new CommonDataService();
auditServiceLog = new AuditServiceLog();
request = new HttpEntity<AuditServiceLog>(auditServiceLog, headers);
auditServiceResponse = new AuditServiceResponse();
auditServiceResponse.setStatus(String.valueOf(200));
auditServiceResponse.setTimestamp("1990-01-01 00:00:01");
auditServiceResponse.setDescription("Description");
Mockito.when(restTemplate.postForObject(Mockito.anyString(), any(HttpEntity.class), ArgumentMatchers.eq(AuditServiceResponse.class)))
.thenReturn(auditServiceResponse);
String a = "test";
ArrayList<Integer> mockedList = new ArrayList<Integer>();
Mockito.when(iCommonDataService.getEnvValue(Mockito.anyInt(), Mockito.anyInt(), Mockito.anyString()))
.thenReturn(a);
}
#Test
public void postTest() {
AuditServiceResponse response = null;
try {
response = auditServiceClient.post("endpoint", auditServiceLog, true);
} catch (Exception e) {
e.printStackTrace();
}
Assert.assertTrue(Integer.parseInt(response.getStatus() )== 200);
}
}
I am getting
InvalidUseOfMatchersException: Misplaced or misused argument matcher detected here
in the setUp() method on the following line:
Mockito.when(iCommonDataService.getEnvValue(Mockito.anyInt(), Mockito.anyInt(), Mockito.anyString()))
.thenReturn(a);
Following is the error:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Misplaced or misused argument matcher detected here:
-> at com.auditService.test.AuditServiceClientTest.setUp(AuditServiceClientTest.java:72)
You cannot use argument matchers outside of verification or stubbing.
Examples of correct usage of argument matchers:
when(mock.get(anyInt())).thenReturn(null);
doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
verify(mock).someMethod(contains("foo"))
This message may appear after an NullPointerException if the last
matcher is returning an object like any() but the stubbed method
signature expect a primitive argument, in this case, use primitive
alternatives.
when(mock.get(any())); // bad use, will raise NPE
when(mock.get(anyInt())); // correct usage use
Also, this error might show up because you use argument matchers with
methods that cannot be mocked. Following methods cannot be
stubbed/verified: final/private/equals()/hashCode(). Mocking methods
declared on non-public parent classes is not supported.
There are a lot of articles on this error around but none of them worked for me. Is there any issue with Mockito.anyInt() because Mockito.anyString() is being used in the previous line and that works fine. Any help is appreciated.
Look carefully at your test code:
iCommonDataService = new CommonDataService();
...
Mockito.when(iCommonDataService.getEnvValue(Mockito.anyInt(), Mockito.anyInt(), Mockito.anyString()))
.thenReturn(a);
You're mocking a method of an object wich is not a mock, that's the cause of the exception.
Since you already have a mock of this class declared wih #Mock annotation, you can simply remove this line iCommonDataService = new CommonDataService();. Another way could be providing a mock manually using Mockito.mock(CommonDataService.class).
But, if you want to mock a method of the original object, you should use Mockito.spy() instead.
I used #RunWith(SpringRunner.class)instead of #RunWith(MockitoJUnitRunner.class) which helps to resolve this issue.
#RunWith(PowerMockRunner.class)
#PrepareForTest( ClassWithPrivate.class )
public class Testy{
#Test
public void testSOmething(){
String response = "something";
ClassWithPrivate some = PowerMockito.spy(new ClassWithPrivate("someArg"));
PowerMockito.doReturn(response).when(some,PowerMockito.method(ClassWithPrivate.class,"privateMethod",String.class)).withArguments(anyString();
}
}
I'm not very familiar with PowerMockito, but is this normal that when the doReturn line runs it will actualy make a call to privateMethod.
For me the issue is that I want to mock the privateMethod, because without mocking it will throw a exception. Currently my test will be closed after the doReturn line, because an exception is thrown from the privateMethod.
Try to use:
PowerMockito.doReturn(response).when(some, "privateMethod", anyString());
Suppose that I have a class like;
public class FooBar {
public int getMethod(List<String> code){
if(code.size() > 100)
throw new Exception;
return 0;
}
}
and I have a test class like this;
#RunWith(PowerMockRunner.class)
#PrepareForTest(FooBar.class)
public class FooBarTest{
FooBar fooBarInstance;
#Before
public void setUp() {
//MockitoAnnotations.initMocks(this);
fooBarInstance = new FooBar();
}
#Test(expected = Exception.class)
public void testGetCorrelationListCodesParameter() {
List<String> codes = Mockito.spy(new ArrayList<String>());
Mockito.doReturn(150).when(codes).size();
fooBarInstance.getMethod(codes);
}
}
How can I make this test method to throw an exception ? I've dealing for hours to do this. Well thanks anyway.
Spying is not needed, mocking is enough. As #David said, also mocking is not needed and not recommended for value object.
Using #Test(expected = Exception.class) has many drawbacks, test can pass when exception is thrown from not expected places. Test is not working but is visible as green.
I prefer BDD style testing with catch-exception.
Reasons for using catch-exceptions
(...) in comparison to the use of try/catch blocks.
The test is more concise and easier to read.
The test cannot be corrupted by a missing assertion. Assume you forgot to type fail() behind the method call that is expected to throw an exception.
(...) in comparison to test runner-specific mechanisms that catch and verify exceptions.
A single test can verify more than one thrown exception.
The test can verify the properties of the thrown exception after the exception is caught.
The test can specify by which method call the exception must be thrown.
The test does not depend on a specific test runner (JUnit4, TestNG).
import static com.googlecode.catchexception.CatchException.caughtException;
import static com.googlecode.catchexception.apis.CatchExceptionAssertJ.*;
public class FooBarTest {
FooBar sut = new FooBar(); // System Under Test
#Test
public void shouldThrowExceptionWhenListHasTooManyElements() {
when(sut).getMethod(listWithSize(150));
then(caughtException()).isInstanceOf(Exception.class);
}
private List<String> listWithSize(int size) {
return new ArrayList<String>(Arrays.asList(new String[size]));
}
}
Full working code for this test: https://gist.github.com/mariuszs/8543918
Not recommended solution with expected and mocking.
#RunWith(MockitoJUnitRunner.class)
public class FooBarTest {
#Mock
List<String> codes;
FooBar fooBarInstance = new FooBar();
#Test(expected = Exception.class)
public void shouldThrowExceptionWhenListHasTooManyElements() throws Exception {
when(codes.size()).thenReturn(150);
fooBarInstance.getMethod(codes);
}
}
A list is a value object. It's not something we should mock. You can write this whole test without mocking anything, if you're prepared to build a list that has a size in excess of 100.
Also, I prefer to use JUnit's ExpectedException mechanism, because it lets you check which line of the test method threw the exception. This is better than passing an argument to the #Test annotation, which only lets you check that the exception was thrown somewhere within the method.
public class FooBarTest {
#Rule
public ExpectedException exceptionRule = ExpectedException.none();
private FooBar toTest = new FooBar();
#Test
public void getMethodThrowsException_whenListHasTooManyElements() {
List<String> listWith101Elements =
new ArrayList<String>(Arrays.asList(new String[101]));
exceptionRule.expect(Exception.class);
toTest.getMethod(listWith101Elements);
}
}