我尝试使用Mockito模拟类的行为。 使用Mockito 1.x可以正常工作。迁移到JUnit 5和Mockito 2似乎不再起作用。
TimeDistributed
期望的是,模拟的TestClass可以显示测试方法中测试的行为。
我得到的错误是:
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import TimeDistributed
from keras.layers import LSTM
# prepare sequence
length = 5
seq = array([i/float(length) for i in range(length)])
X = seq.reshape(1, length, 1)
y = seq.reshape(1, length, 1)
# define LSTM configuration
n_neurons = length
n_batch = 1
n_epoch = 1000
# create LSTM
model = Sequential()
model.add(LSTM(n_neurons, input_shape=(length, 1), return_sequences=True))
model.add(TimeDistributed(Dense(1)))
model.compile(loss='mean_squared_error', optimizer='adam')
print(model.summary())
# train LSTM
model.fit(X, y, epochs=n_epoch, batch_size=n_batch, verbose=2)
# evaluate
result = model.predict(X, batch_size=n_batch, verbose=0)
for value in result[0,:,0]:
print('%.1f' % value)
在两种情况下,参数@ExtendWith(MockitoExtension.class)
public class MockitoExample {
static abstract class TestClass {
public abstract int booleanMethod(boolean arg);
}
@Mock
TestClass testClass;
@BeforeEach
public void beforeEach() {
when(testClass.booleanMethod(eq(true))).thenReturn(1);
when(testClass.booleanMethod(eq(false))).thenReturn(2);
}
@Test
public void test() {
assertEquals(1,testClass.booleanMethod(true));
assertEquals(2,testClass.booleanMethod(false));
}
}
似乎都匹配,尽管我显然与org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
- this invocation of 'booleanMethod' method:
testClass.booleanMethod(false);
-> at org.oneandone.ejbcdiunit.mockito_example.MockitoExample.beforeEach(MockitoExample.java:30)
- has following stubbing(s) with different arguments:
1. testClass.booleanMethod(false);
-> at org.oneandone.ejbcdiunit.mockito_example.MockitoExample.beforeEach(MockitoExample.java:29)
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.
匹配。
是Mockito 2.17中的错误还是误解。我应该如何使用Mockito 2.x模拟具有不同布尔参数的调用?
example也可以在github上找到。但是surefire仅使用
可以开始测试false
使用Mockito 2.21执行测试会得到相同的结果。
答案 0 :(得分:10)
使用严格的存根(Mockito的默认行为),在同一方法上调用多个when
将重置该模拟。解决方案是调用when
一次,并将逻辑包含在Answer
中:
@BeforeEach
public void beforeEach() {
when(testClass.booleanMethod(anyBoolean())).thenAnswer(invocationOnMock -> {
if ((boolean) invocationOnMock.getArguments()[0]) {
return 1;
}
return 2;
});
}
或者,您可以使用宽容模拟,但这并不总是一个好主意-宽容模拟允许多余的存根,并使您更容易在测试中犯错误,这可能会导致“生产”代码中的错误:
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class MockitoExample {
答案 1 :(得分:6)
自Mockito 2.20起,也可以在本地添加lenient()
@ExtendWith(MockitoExtension.class)
public class MockitoExample {
static abstract class TestClass {
public abstract int booleanMethod(boolean arg);
}
@Mock
TestClass testClass;
@BeforeEach
public void beforeEach() {
lenient().when(testClass.booleanMethod(eq(true))).thenReturn(1);
lenient().when(testClass.booleanMethod(eq(false))).thenReturn(2);
}
@Test
public void test() {
assertEquals(1,testClass.booleanMethod(true));
assertEquals(2,testClass.booleanMethod(false));
}
}
答案 2 :(得分:2)
Mockito 1和2的“严格度”等级不同。
除了通过将Mockito 2与JUnit 4或5结合使用外,默认级别仍然不同。
总结:
3个严格级别:
LENIENT
:最低严格度WARN
:向控制台发出额外的警告STRICT_STUBS
:如果可能的滥用会抛出异常,从而确保干净的测试,但也会产生误报。根据使用的API的默认有效级别:
LENIENT
WARN
MockitoExtension.class
)的Mockito 2:STRICT_STUBS
STRICT_STUBS
。更多详细信息
实际的Mockito文档对此非常清楚:
在模拟会话期间配置Mockito的“严格性”。 会话通常映射到单个测试方法调用。严格 推动更清洁的测试和更高的生产率。 利用增强的严格性使用了Mockito的JUnit支持 (MockitoRule或MockitoJUnitRunner)。如果无法使用JUnit支持 MockitoSession是必经之路。
严格程度如何影响测试的行为(模拟) 会话)?
1。
Strictness.LENIENT
-无添加行为。Mockito1.x的默认设置。仅在无法使用STRICT_STUBS或WARN时才建议使用。2。
Strictness.WARN
-帮助保持测试整洁并提高可调试性。报告有关未使用的存根和存根的控制台警告 参数不匹配(请参见org.mockito.quality.MockitoHint)。默认值 使用JUnitRule或MockitoJUnitRunner时Mockito 2.x的行为。 如果您无法使用STRICT_STUBS,则推荐使用此功能。3。
Strictness.STRICT_STUBS
-确保干净的测试,减少测试代码重复,提高可调试性。灵活性的最佳组合 和生产力。强烈推荐,默认为Mockito计划 v3。有关详细信息,请参见STRICT_STUBS。
但是无论与消息相关的抛出异常
“正在使用不同的参数进行存根”
似乎是一个过于严格的检查。 异常消息以某种方式证明了这一点:
但是,在某些情况下,当此异常生成false时, 负信号:
...
被测试的代码故意使用不同参数调用
- stubbed方法
因此默认情况下禁用它似乎太多了。
因此,如果您使用JUnit 5作为STRICT_STUBS
的替代方案,则可以使用WARNING
,但通常希望避免使用LENIENT
太安静。
除了MockitoExtension
外,mockito-junit-jupiter
库还提供
@MockitoSettings
可以在方法级别以及类级别使用。
这里是一个示例:
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
@ExtendWith(MockitoExtension.class)
public class FooTest {
@MockitoSettings(strictness = Strictness.WARN)
@Test
void foo() throws Exception {
List<String> strings = Mockito.mock(List.class);
Mockito.when(strings.add("a"))
.thenReturn(true);
Mockito.when(strings.add("b"))
.thenReturn(false);
}
@Test
void fooKo() throws Exception {
List<String> strings = Mockito.mock(List.class);
Mockito.when(strings.add("a"))
.thenReturn(true);
Mockito.when(strings.add("b"))
.thenReturn(false);
}
}
fooKo()
会在foo()
成功时抛出滥用Mockito异常,但会提供有用的警告:
[MockitoHint] FooTest (see javadoc for MockitoHint): [MockitoHint] 1. Unused -> at FooTest.foo(FooTest.java:19) [MockitoHint] 2. Unused -> at FooTest.foo(FooTest.java:21)
作为其他选择,您也可以使用Mockito.lenient()
为特定的调用应用宽松的严格性。
您还可以在模拟实例化中将每个模拟调用设置为宽松:
@Test
void foo() throws Exception {
List<String> strings = Mockito.mock(List.class, Mockito.withSettings()
.lenient());
....
}
答案 3 :(得分:-2)
由于第一个答案出乎意料,所以我检查了以下内容:
interface Poops {
String get(boolean is);
}
@Test
void test1() {
Poops a = mock(Poops.class);
when(a.get(eq(true))).thenReturn("1");
when(a.get(eq(false))).thenReturn("2");
Assertions.assertEquals("1", a.get(true));
Assertions.assertEquals("2", a.get(false));
}
它与Mockito 2.21.0兼容。
更新:
问题似乎出在Jupiter Mockito扩展程序,它将默认设置更改为Strictness.STRICT_STUBS
。