I am trying to Mock this following code with transaction manager. Receiving error below. How can I resolve this?
Code:
DefaultTransactionDefinition paramTransactionDefinition = new DefaultTransactionDefinition();
PlatformTransactionManager transactionManager = new DataSourceTransactionManager(namedParameterJdbcTemplate.getJdbcTemplate().getDataSource());
TransactionStatus status = transactionManager.getTransaction(paramTransactionDefinition);
Test:
@Mock
private JdbcTemplate jdbcTemplate;
@Mock
private PlatformTransactionManager platformTransactionManager;
@Mock
private DataSource dataSource;
@Mock
private TransactionStatus transactionStatus;
given(namedParameterJdbcTemplate.getJdbcTemplate()).willAnswer(a -> jdbcTemplate);
given(namedParameterJdbcTemplate.getJdbcTemplate().getDataSource()).willAnswer(a -> dataSource);
platformTransactionManager = Mockito.mock(DataSourceTransactionManager.class, withSettings().useConstructor(dataSource));
given(platformTransactionManager.getTransaction(any())).willAnswer(a -> transactionStatus);
Error:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
0 matchers expected, 1 recorded:
-> at given(platformTransactionManager.getTransaction(any())).willAnswer(a -> transactionStatus);
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
trying to use this resource: https://stackoverflow.com/a/60429929/15435022
CodePudding user response:
I think you are taking things a bit too eagerly while mocking. Given should look like this:
//method with parameter
given(aMock.aSingleCall(anArgumentMatcher)).willReturn(aValue);
//method without parameter
given(aMock.aSingleCall()).willReturn(aValue);
The chained call you are using here:
given(namedParameterJdbcTemplate.getJdbcTemplate().getDataSource()).willAnswer(a -> dataSource);
Is supposed to be split into two pieces, like this:
given(namedParameterJdbcTemplate.getJdbcTemplate()).willReturn(jdbcTemplate);
given(jdbcTemplate.getDataSource()).willReturn(dataSource);
On the constructor call, you are facing the issue of not actually using the mock while stubbing. Mockito will not be able to magically replace your constructor call with a mocking (and it shouldn't either). You should be simply using something like one of the following scenarios.
A) If you want to mock only the dataSource
Change your code in a way you can set the dataSource
mock you have like this:
PlatformTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
It can be through a constructor or setter, does not matter.
B) If you want to mock the transactionManager
as well
Change your code to avoid directly calling the constructor, accept the transactionManager
as a dependency and set the mock you have already created (the field named platformTransactionManager
).
Note: Calling any(aClass)
will return the default value of the type when the any(aClass)
is executed. In your case, this is a null
so when you are calling the real constructor with it, you are just writing new DataSourceTransactionManager(null)
with extra steps.
Update#1: how do I "Change your code in a way"?
It depends on the rest of your code, but assuming that this is in a single method:
DefaultTransactionDefinition paramTransactionDefinition = new DefaultTransactionDefinition();
PlatformTransactionManager transactionManager = new DataSourceTransactionManager(namedParameterJdbcTemplate.getJdbcTemplate().getDataSource());
TransactionStatus status = transactionManager.getTransaction(paramTransactionDefinition);
you could either use a parameter of PlatformTransactionManager transactionManager
and avoid the constructor call (extract parameter) or you could at least extract the constructor call to a method and you can use a Mockito spy to make that method return a mock instead of the real object.
CodePudding user response:
This will work:
@Mock
private JdbcTemplate jdbcTemplate;
@Mock
private PlatformTransactionManager platformTransactionManager;
@Mock
private DataSource dataSource;
@Mock
private TransactionStatus transactionStatus;
given(namedParameterJdbcTemplate.getJdbcTemplate()).willAnswer(a -> jdbcTemplate);
given(namedParameterJdbcTemplate.getJdbcTemplate().getDataSource()).willAnswer(a -> dataSource);
MockedConstruction<DataSourceTransactionManager> mocked = Mockito.mockConstruction(DataSourceTransactionManager.class,
(mock, context) -> {
when(mock.getTransaction(any())).thenReturn(null);
});
given(platformTransactionManager.getTransaction(new DefaultTransactionDefinition())).willAnswer(a -> transactionStatus);
mocked.close();
Resource: https://rieckpil.de/mock-java-constructors-and-their-object-creation-with-mockito/