Java Mockito useConstructor withSettings,错误无效使用参数匹配器

Java Mockito useConstructor withSettings, error Invalid use of argument matchers

我正在尝试使用事务管理器模拟以下代码。接收错误如下。我该如何解决?

代码:

DefaultTransactionDefinition paramTransactionDefinition = new DefaultTransactionDefinition();
PlatformTransactionManager transactionManager = new DataSourceTransactionManager(namedParameterJdbcTemplate.getJdbcTemplate().getDataSource());
TransactionStatus status = transactionManager.getTransaction(paramTransactionDefinition);
  

测试:

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

错误:

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

正在尝试使用此资源:

我觉得你在嘲笑的时候有点太急切了。 给出的应该是这样的:

//method with parameter
given(aMock.aSingleCall(anArgumentMatcher)).willReturn(aValue);
//method without parameter
given(aMock.aSingleCall()).willReturn(aValue);

您在此处使用的链式调用:

given(namedParameterJdbcTemplate.getJdbcTemplate().getDataSource()).willAnswer(a -> dataSource);

应该分成两部分,像这样:

given(namedParameterJdbcTemplate.getJdbcTemplate()).willReturn(jdbcTemplate);
given(jdbcTemplate.getDataSource()).willReturn(dataSource);

在构造函数调用中,您遇到了存根时未实际使用模拟的问题。 Mockito 将无法用模拟神奇地替换您的构造函数调用(也不应该)。您应该简单地使用类似以下场景之一的东西。

A) 如果你只想模拟 dataSource

更改您的代码,您可以像这样设置 dataSource 模拟:

PlatformTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);

可以通过构造函数,也可以setter,无所谓。

B) 如果你也想模拟 transactionManager

更改您的代码以避免直接调用构造函数,接受 transactionManager 作为依赖项并设置您已经创建的模拟(名为 platformTransactionManager 的字段)。

注意:调用any(aClass)将return执行any(aClass)时类型的默认值。在你的例子中,这是一个 null 所以当你用它调用真正的构造函数时,你只是在写 new DataSourceTransactionManager(null) 额外的步骤。

更新#1:如何“以某种方式更改您的代码”?

这取决于您的其余代码,但假设这是在单个方法中:

DefaultTransactionDefinition paramTransactionDefinition = new DefaultTransactionDefinition();
PlatformTransactionManager transactionManager = new DataSourceTransactionManager(namedParameterJdbcTemplate.getJdbcTemplate().getDataSource());
TransactionStatus status = transactionManager.getTransaction(paramTransactionDefinition);

您可以使用 PlatformTransactionManager transactionManager 的参数并避免调用构造函数(提取参数),或者您至少可以提取对方法的构造函数调用,然后可以使用 Mockito 间谍来制作该方法 return 模拟而不是真实对象。

这会起作用:

@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();

资源: https://rieckpil.de/mock-java-constructors-and-their-object-creation-with-mockito/