Foo2.java

本文介绍了一个简单的Java类Foo2,该类包含一个私有成员变量name及构造方法与显示name的方法showName。通过实例化Foo2并调用showName方法可以打印出对象的名称。

public class Foo2{

  private String name;

 

  public Foo2(Sring name){

    this.name=name;

  }

 

  public void showName(){

    System.out.println("Foo2 名称: "+name);

  }

修改后报org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Misplaced argument matcher detected here: -> at com.htsc.service.FundCostStatisticsV2ServiceTest.testFindStockcostListByCycle_JdbcTemplateThrowsDataAccessException(FundCostStatisticsV2ServiceTest.java:731) 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")) 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. at com.htsc.service.FundCostStatisticsV2ServiceTest.testFindStockcostListByCycle_JdbcTemplateThrowsDataAccessException(FundCostStatisticsV2ServiceTest.java:732) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37) at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38) at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:231) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
最新发布
10-17
你遇到的这个异常: ``` org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Misplaced argument matcher detected here: -> at com.htsc.service.FundCostStatisticsV2ServiceTest.testFindStockcostListByCycle_JdbcTemplateThrowsDataAccessException(FundCostStatisticsV2ServiceTest.java:731) You cannot use argument matchers outside of verification or stubbing. ``` 是 **Mockito 使用参数匹配器(Argument Matchers)时的经典错误**。 --- ## ❌ 错误原因 你在 `doThrow()` 或 `when()` 中 **混合使用了 Mockito 的 matcher 和字面量(literal values)**,或者 **matcher 被错误地放在了非 stubbing/verification 上下文中**。 最常见的情况是:**你在调用真实方法时用了 `anyString()` 等 matcher,但没有全部使用 matcher** —— Mockito 要求:**要么全用 matcher,要么全用固定值。** --- ### 🔍 举个例子说明问题 假设你的代码是这样写的(这是典型错误): ```java doThrow(new BadSqlGrammarException("...", "sql", new SQLException())) .when(jdbcTemplate).queryForList(anyString(), eq("someFixedValue")); // ❌ 混合使用 anyXxx 和 eq ``` 或者更严重的是: ```java // ❌ 错误写法:在非 stubbing 场景使用 anyString() String sql = "SELECT * FROM table WHERE date = " + anyString(); // 这是绝对错误! jdbcTemplate.queryForList(sql); // 不会触发 mock ``` 但根据你的堆栈信息和上下文,最大的可能是这一行: ```java doThrow(...).when(jdbcTemplate).queryForList(anyString()); ``` 看起来没问题,但如果在这之前或之后有其他非 matcher 参数参与表达式计算,就会报错。 --- ## ✅ 正确修复方式 ### ✅ 修复 1:确保 `anyXxx()` 只出现在 `when()` 或 `verify()` 内部 不要这样写: ```java // ❌ 错误示例 String arg = anyString(); when(jdbcTemplate.queryForList(arg)).thenThrow(...); ``` 也不要这样: ```java // ❌ 错误示例 when(jdbcTemplate.queryForList(anyString())).thenThrow(...); List result = jdbcTemplate.queryForList("SELECT ..."); // 必须也用 same 方法调用才会命中 ``` ✅ 正确做法是: ```java // ✅ 正确:在 when 中使用 anyString() when(jdbcTemplate.queryForList(anyString())).thenThrow( new BadSqlGrammarException("Simulated error", "SELECT ...", new SQLException()) ); ``` 并且你要确保: - `getFundCost(...)` 方法内部确实调用了 `jdbcTemplate.queryForList(String sql)` - 调用时传入的是一个字符串参数(不是多个参数) > ⚠️ 注意:`queryForList` 有很多重载方法,比如: > > - `queryForList(String sql)` > - `queryForList(String sql, Object... args)` > - `queryForList(String sql, Class<T> elementType)` > > 如果你 mock 的是单参版本,但实际调用的是带参数的多参版本,则 **不会触发异常**,也不会报错,只会“静默跳过”。 --- ### ✅ 修复 2:如果你不确定 SQL 是什么,可以捕获实际调用并验证 #### 方案一:先确认到底调用了哪个 `queryForList` 方法? 你可以先不抛异常,而是验证是否被调用: ```java // 先运行一次正常流程(无异常),看看是否调用了 queryForList fundCostStatisticsV2ServiceUnderTest.findStockcostListByCycle(stockcostsListByCycle, fundcostsVO); // 验证 queryForList 是否被调用 verify(jdbcTemplate).queryForList(anyString()); // 或更具体的匹配 ``` 如果报错说 “Wanted but not invoked”,说明 `getFundCost` 没有真正调用 `jdbcTemplate`,可能是: - `getFundCost` 是 private 方法 - 或者它被缓存、条件判断绕过了 - 或者你自己实现了逻辑而没走 DAO 层 --- ### ✅ 修复 3:推荐使用 `ArgumentCaptor` 捕获 SQL 并调试 ```java @Test public void testFindStockcostListByCycle_DebugSQL() { // setup... // 正常执行 fundCostStatisticsV2ServiceUnderTest.findStockcostListByCycle(stockcostsListByCycle, fundcostsVO); // 捕获 SQL ArgumentCaptor<String> sqlCaptor = ArgumentCaptor.forClass(String.class); verify(jdbcTemplate).queryForList(sqlCaptor.capture()); System.out.println("Executed SQL: " + sqlCaptor.getValue()); } ``` 然后再基于真实的 SQL 去做模拟: ```java when(jdbcTemplate.queryForList(eq("your_actual_sql_here"))).thenThrow( new BadSqlGrammarException("DB Error", "your_actual_sql_here", new SQLException()) ); ``` --- ### ✅ 最终修复后的测试代码(正确版) ```java @Test public void testFindStockcostListByCycle_JdbcTemplateThrowsDataAccessException() { // Setup VO 对象(略),注意 cycle 应为 "001" / "002" / "003" StockcostsVO stockcostsVO = new StockcostsVO(); stockcostsVO.setCycle("001"); // 必须匹配 if 条件 stockcostsVO.setBeginDate(new GregorianCalendar(2020, Calendar.JANUARY, 1).getTime()); stockcostsVO.setEndDate(new GregorianCalendar(2020, Calendar.DECEMBER, 31).getTime()); stockcostsVO.setSumType("zjyy"); List<StockcostsVO> list = new ArrayList<>(Arrays.asList(stockcostsVO)); StockcostsVO fundcostsVO = new StockcostsVO(); fundcostsVO.setCycle("001"); fundcostsVO.setBeginDate(stockcostsVO.getBeginDate()); fundcostsVO.setEndDate(stockcostsVO.getEndDate()); fundcostsVO.setSumType("zjyy"); // ✅ 关键:使用 anyString() 必须只在 when/verify 内部,并且整个参数列表都用 matcher when(jdbcTemplate.queryForList(anyString())).thenThrow( new BadSqlGrammarException( "Simulated DB exception", "SELECT /* fake */ 1", new SQLException("Table not found") ) ); // 执行并断言异常 assertThatThrownBy(() -> fundCostStatisticsV2ServiceUnderTest.findStockcostListByCycle(list, fundcostsVO) ).isInstanceOf(DataAccessException.class) .hasRootCauseInstanceOf(SQLException.class); } ``` --- ## ✅ 如何避免此类错误? | 规则 | 建议 | |------|------| | 🚫 不要在 `when` 外使用 `anyXxx()` | 如 `String s = anyString();` | | ✅ 所有参数统一风格 | 要么全用 `eq("value")`, `anyInt()` 等,要么全用字面量 | | ✅ 使用 `eq()` 包裹普通值 | 当和其他 matcher 混合时必须用 `eq("abc")` | | ✅ 查看真实调用签名 | 确保 mock 的方法签名与实际一致 | | ✅ 使用 `verify()` + `ArgumentCaptor` 调试 | 看看是否真的调用了目标方法 | --- ## 💡 特别提醒:关于 `private` 方法的问题 如果你的 `this.getFundCost(...)` 是 `private` 方法,那么 Mockito **无法拦截其调用**,即使你 mock 了该方法也没用! 因为 Java 的限制:`private` 方法是静态绑定的,不会走动态代理。 👉 解决办法: 1. 改成 `protected` 或包级可见 2. 抽取到独立的 `@Service` 或 `DAO` 类中,通过依赖注入交给 Spring 管理,然后 mock 它 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值