以下是一份MRE:
package com.example.mockitomre;
public interface DocumentedEndpoint {
EndpointDetails getDetails();
}
package com.example.mockitomre;
public interface EndpointDetails {
String getPath();
}
package com.example.mockitomre;
public interface EndpointSieve {
boolean isAllowed(DocumentedEndpoint endpoint);
}
package com.example.mockitomre;
import org.springframework.util.AntPathMatcher;
public class EndpointSieveConfig {
public EndpointSieve errorPathEndpointSieve(GatewayMeta gatewayMeta, AntPathMatcher antPathMatcher) {
return endpoint -> gatewayMeta.getIgnoredPatterns().stream()
.noneMatch(ignoredPattern -> antPathMatcher.match(ignoredPattern, endpoint.getDetails().getPath()));
}
}
package com.example.mockitomre;
import lombok.Getter;
import java.util.List;
@Getter
public final class GatewayMeta {
private List<String> ignoredPatterns;
}
package com.example.mockitomre;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.util.AntPathMatcher;
import java.util.List;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class ErrorPathEndpointSieveTest {
private final EndpointSieveConfig endpointSieveConfig = new EndpointSieveConfig();
@Mock
private GatewayMeta gatewayMetaMock;
@Mock
private AntPathMatcher antPathMatcherMock;
private EndpointSieve errorPathEndpointSieve;
@Test
void doesntAllowIgnoredPatterns() {
String ignoredPattern = "/ignored-path/**";
String anotherIgnoredPattern = "/*/another-ignored-path";
when(gatewayMetaMock.getIgnoredPatterns()).thenReturn(List.of(
ignoredPattern, anotherIgnoredPattern
));
String pathToExclude = "/ignored-path";
String anotherPathToExclude = "/it-is/another-ignored-path";
when(antPathMatcherMock.match(ignoredPattern, pathToExclude)).thenReturn(true);
when(antPathMatcherMock.match(anotherIgnoredPattern, anotherPathToExclude)).thenReturn(true);
DocumentedEndpoint endpointToExclude = mock(DocumentedEndpoint.class, RETURNS_DEEP_STUBS);
when(endpointToExclude.getDetails().getPath()).thenReturn(pathToExclude);
DocumentedEndpoint anotherEndpointToExclude = mock(DocumentedEndpoint.class, RETURNS_DEEP_STUBS);
when(anotherEndpointToExclude.getDetails().getPath()).thenReturn(anotherPathToExclude);
String okPath = "/another-ignored-path/on-second-thought-it-is-not";
DocumentedEndpoint endpointToKeep = mock(DocumentedEndpoint.class, RETURNS_DEEP_STUBS);
when(endpointToKeep.getDetails().getPath()).thenReturn(okPath);
errorPathEndpointSieve = endpointSieveConfig.errorPathEndpointSieve(gatewayMetaMock, antPathMatcherMock);
assertThat(errorPathEndpointSieve.isAllowed(endpointToExclude)).isFalse();
assertThat(errorPathEndpointSieve.isAllowed(anotherEndpointToExclude)).isFalse();
assertThat(errorPathEndpointSieve.isAllowed(endpointToKeep)).isTrue();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>mockito-mre</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mockito-mre</name>
<description>mockito-mre</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
所以问题来了:一旦我运行了测试,我就会得到
org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
- this invocation of 'match' method:
antPathMatcherMock.match(
"/ignored-path/**",
"/it-is/another-ignored-path"
);
-> at com.example.mockitomre.EndpointSieveConfig.lambda$errorPathEndpointSieve$0(EndpointSieveConfig.java:8)
- has following stubbing(s) with different arguments:
1. antPathMatcherMock.match(
"/*/another-ignored-path",
"/it-is/another-ignored-path"
);
-> at com.example.mockitomre.ErrorPathEndpointSieveTest.doesntAllowIgnoredPatterns(ErrorPathEndpointSieveTest.java:37)
Typically, stubbing argument mismatch indicates user mistake when writing tests.
Mockito fails early so that you can debug potential problem easily.
However, there are legit scenarios when this exception generates false negative signal:
- stubbing the same method multiple times using 'given().will()' or 'when().then()' API
Please use 'will().given()' or 'doReturn().when()' API for stubbing.
- stubbed method is intentionally invoked with different arguments by code under test
Please use default or 'silent' JUnit Rule (equivalent of Strictness.LENIENT).
For more information see javadoc for PotentialStubbingProblem class.
at org.springframework.util.AntPathMatcher.match(AntPathMatcher.java:195)
at com.example.mockitomre.EndpointSieveConfig.lambda$errorPathEndpointSieve$0(EndpointSieveConfig.java:8)
我不知道Mockito到底想从我这里得到什么,但我做了一些实验,这不是关于
- 用不同的论据嘲弄相同的方法;
- 事实上,该方法也是使用任何存根过程中不涉及的参数集调用的.
// here, Mockito is fine with calling match("/ignored-path/**", "/it-is/another-ignored-path")
@Test
void doesntAllowIgnoredPatterns() {
when(antPathMatcherMock.match("/ignored-path/**", "/ignored-path")).thenReturn(true);
when(antPathMatcherMock.match("/*/another-ignored-path", "/it-is/another-ignored-path")).thenReturn(true);
antPathMatcherMock.match("/ignored-path/**", "/ignored-path");
antPathMatcherMock.match("/*/another-ignored-path", "/it-is/another-ignored-path");
antPathMatcherMock.match("/ignored-path/**", "/it-is/another-ignored-path");
}
你知道有什么帮助(除了放弃Mockito的扩展,我讨厌它,它带来了更多的问题,它解决了)?使EndpointSieveConfig
成为ErrorPathEndpointSieveTest
的嵌套类,如下所示:
package com.example.mockitomre;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.util.AntPathMatcher;
import java.util.List;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class ErrorPathEndpointSieveTest {
private final EndpointSieveConfig endpointSieveConfig = new EndpointSieveConfig();
@Mock
private GatewayMeta gatewayMetaMock;
@Mock
private AntPathMatcher antPathMatcherMock;
private EndpointSieve errorPathEndpointSieve;
@Test
void doesntAllowIgnoredPatterns() {
String ignoredPattern = "/ignored-path/**";
String anotherIgnoredPattern = "/*/another-ignored-path";
when(gatewayMetaMock.getIgnoredPatterns()).thenReturn(List.of(
ignoredPattern, anotherIgnoredPattern
));
String pathToExclude = "/ignored-path";
String anotherPathToExclude = "/it-is/another-ignored-path";
when(antPathMatcherMock.match(ignoredPattern, pathToExclude)).thenReturn(true);
when(antPathMatcherMock.match(anotherIgnoredPattern, anotherPathToExclude)).thenReturn(true);
DocumentedEndpoint endpointToExclude = mock(DocumentedEndpoint.class, RETURNS_DEEP_STUBS);
when(endpointToExclude.getDetails().getPath()).thenReturn(pathToExclude);
DocumentedEndpoint anotherEndpointToExclude = mock(DocumentedEndpoint.class, RETURNS_DEEP_STUBS);
when(anotherEndpointToExclude.getDetails().getPath()).thenReturn(anotherPathToExclude);
String okPath = "/another-ignored-path/on-second-thought-it-is-not";
DocumentedEndpoint endpointToKeep = mock(DocumentedEndpoint.class, RETURNS_DEEP_STUBS);
when(endpointToKeep.getDetails().getPath()).thenReturn(okPath);
errorPathEndpointSieve = endpointSieveConfig.errorPathEndpointSieve(gatewayMetaMock, antPathMatcherMock);
assertThat(errorPathEndpointSieve.isAllowed(endpointToExclude)).isFalse();
assertThat(errorPathEndpointSieve.isAllowed(anotherEndpointToExclude)).isFalse();
assertThat(errorPathEndpointSieve.isAllowed(endpointToKeep)).isTrue();
}
public class EndpointSieveConfig {
public EndpointSieve errorPathEndpointSieve(GatewayMeta gatewayMeta, AntPathMatcher antPathMatcher) {
return endpoint -> gatewayMeta.getIgnoredPatterns().stream()
.noneMatch(ignoredPattern -> antPathMatcher.match(ignoredPattern, endpoint.getDetails().getPath()));
}
}
}
现在它过go 了!这些问题包括:
- 莫奇托想从我这里得到什么?
- 为什么做出这种奇怪的改变会让Mockito高兴呢?
不,this question不管用
Anything that doesn't address moving the class is not an answer to this question. Stop the "duplicate" nonsense please and actually read the question
对于那些建议Javadoc人的人:这不是一个可靠的信息来源.我确实从那里粘贴了代码,它doesn't触发PotentialStubbingProblem
,它触发UnnecessaryStubbingException
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.BDDMockito.given;
@ExtendWith(MockitoExtension.class)
public class MockitoTest {
@Mock
SomeClass mock;
@Test
void test() {
//test method:
Something something = new Something();
given(mock.getSomething(100)).willReturn(something);
//code under test:
Something something2 = mock.getSomething(50); // <-- stubbing argument mismatch
}
abstract class SomeClass {
abstract Something getSomething(int arg);
}
class Something {}
}