我正在尝试为我的android
应用程序的验证器编写Unit Test
。验证器接受参数EditText
,因此我需要模拟它。但是,模拟不起作用,迫使Test
在调用when()
方法时崩溃,但有以下例外:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
我的代码是:
@RunWith(MockitoJUnitRunner.class)
public class MyUnitTest
{
@Mock
Context mMockContext;
@Test
public void validateIsCorrect() {
final EditText input = Mockito.mock(EditText.class);
when(input.getText()).thenReturn(Editable.Factory.getInstance().newEditable("123"));
...
}
}
build.gradle
文件中的依赖项包括:
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19'
EditText
的方法getText()
不是private
也不是final
。我做错了什么?可以这样嘲笑EditText
吗?如何?
当你运行单元测试时,你使用的是标准的JVM上下文,而不是Android的上下文,这就是它崩溃的原因:Editable.Factory
类及其方法(如getInstance()
)不在类路径中。他们也没有被嘲笑。我要做的是创建一个与private
成员implements Editable
的类来保存字符串引用并使用它来模拟getText()
方法。像这样:
class MockEditable implements Editable {
private String str;
public MockEditable(String str) {
this.str = str;
}
@Override @NonNull
public String toString() {
return str;
}
@Override
public int length() {
return str.length();
}
@Override
public char charAt(int i) {
return str.charAt(i);
}
@Override
public CharSequence subSequence(int i, int i1) {
return str.subSequence(i, i1);
}
@Override
public Editable replace(int i, int i1, CharSequence charSequence, int i2, int i3) {
return this;
}
@Override
public Editable replace(int i, int i1, CharSequence charSequence) {
return this;
}
@Override
public Editable insert(int i, CharSequence charSequence, int i1, int i2) {
return this;
}
@Override
public Editable insert(int i, CharSequence charSequence) {
return this;
}
@Override
public Editable delete(int i, int i1) {
return this;
}
@Override
public Editable append(CharSequence charSequence) {
return this;
}
@Override
public Editable append(CharSequence charSequence, int i, int i1) {
return this;
}
@Override
public Editable append(char c) {
return this;
}
@Override
public void clear() {
}
@Override
public void clearSpans() {
}
@Override
public void setFilters(InputFilter[] inputFilters) {
}
@Override
public InputFilter[] getFilters() {
return new InputFilter[0];
}
@Override
public void getChars(int i, int i1, char[] chars, int i2) {
}
@Override
public void setSpan(Object o, int i, int i1, int i2) {
}
@Override
public void removeSpan(Object o) {
}
@Override
public <T> T[] getSpans(int i, int i1, Class<T> aClass) {
return null;
}
@Override
public int getSpanStart(Object o) {
return 0;
}
@Override
public int getSpanEnd(Object o) {
return 0;
}
@Override
public int getSpanFlags(Object o) {
return 0;
}
@Override
public int nextSpanTransition(int i, int i1, Class aClass) {
return 0;
}
}
然后,您可以使用此类
Mockito.when(input.getText()).thenReturn(new MockEditable("123"));
从更远的地方看这个;我在问自己:为什么你的验证者需要了解Android特定的类?
我的意思是:我假设您的验证器(最终)必须检查字符串或类似内容的属性?
因此,我建议在这里集中精力分离关注点:
- 创建一个从编辑文本中获取字符串的组件
- 创建处理此类字符串的验证器
那么你首先不需要对你的验证者进行任何特定的模拟!
final EditText editText = Mockito.mock(EditText.class);
final ArgumentCaptor<Editable> captor = ArgumentCaptor.forClass(Editable.class);
Mockito.doNothing().when(editText).setText(captor.capture());
Mockito.when(editText.getText()).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) {
return captor.getValue();
}
});
这家伙呢,它对我有用:请不要忘记添加 MockitoAnnotations.init(this);并使用
@Mock private EditTextView passwordField;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
when(rootView.findViewById(R.id.button_logout)).thenReturn(buttonLogout);
when(rootView.findViewById(R.id.button_unlock)).thenReturn(buttonUnlock);
when(rootView.findViewById(R.id.ScreenLock_PasswordTextField)).thenReturn(passwordField);
when(passwordField.getText()).thenReturn(Editable.Factory.getInstance().newEditable("asd"));
when(application.getPassword()).thenReturn("asd");
sut = new ScreenLockPresenterImpl(application, rootView, screenLockListener,
logoutButtonClickListener);
}
@Test
public void testOnClickWhenOk() {
sut.onClick(null);
verify(passwordField).getText();
verify(screenLockListener).unLock();
}
我认为这就是您要找的:when(passwordField.getText()).thenReturn(Editable.Factory.getInstance().newEditable("asd"));