什么时Mock
Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。
为什么要用Mockito
Mockito 是Java 单元测试 Mock 框架。
我们做Junit单元测试时,难免会用到一些Android库,那么就会抛一个异常:
java.lang.RuntimeException: Method isEmpty in android.text.TextUtils not mocked
或者一个数据可能出现的情况很多种,如何去模拟N多种数据
如果我们想越过这个错误或者模拟数据,达到测试我们代码的目的的话,就需要用到Mockito。
使用
操作符
Mock创建
@Mock
List list;
@Before
public void init() throws Exception {
MockitoAnnotations.initMocks(this);
}
验证行为
@Mock
List list;
/**
* 行为验证
*/
@Test
public void test5() {
list.add("5555");
list.clear();
verify(list).add("5555");
verify(list).clear();
}
stubbing
/**
* stubbing
*/
@Test
public void test6() {
when(list.get(0)).thenReturn("first");
when(list.get(1)).thenThrow(new RuntimeException());
System.out.println(list.get(0));
// System.out.println(list.get(1));
verify(list).get(0);
}
注:
- 一旦 stub,该方法将始终返回一个 stub 的值,无论它被调用多少次。
- Stubbing 可以被覆盖。
- stubbing 的顺序是重要的。
参数匹配器
/**
* 参数匹配器
*/
@Test
public void test7() {
when(list.get(anyInt())).thenReturn("hahahah");
System.out.println(list.get(999));
}
调用次数验证
/**
* 调用次数验证
*/
@Test
public void test8() {
list.add("one");
list.add("two");
list.add("three");
verify(list,never()).add("four");
verify(list,atMost(1)).add("one");
verify(list,atLeast(2)).add("one");
}
异常方法处理
/**
* 异常处理
*/
@Test
public void test9() {
doThrow(new RuntimeException()).when(list).get(0);
System.out.println(list.get(0));
}
。。。。
还有很多的方法可以参考官方文档:
https://static.javadoc.io/org.mockito/mockito-core/2.9.0/org/mockito/Mockito.html
使用场景一
简单写个bean类:
public class AndroidBean implements Parcelable {
private String name;
public AndroidBean() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
private String age;
protected AndroidBean(Parcel in) {
name = in.readString();
age = in.readString();
}
public static final Creator CREATOR = new Creator() {
@Override
public AndroidBean createFromParcel(Parcel in) {
return new AndroidBean(in);
}
@Override
public AndroidBean[] newArray(int size) {
return new AndroidBean[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(age);
}
@Override
public boolean equals(Object obj) {
return TextUtils.equals(name,((AndroidBean)obj).getName());
}
}
待测试方法:
public static String test4(AndroidBean bean) {
return bean.getName();
}
测试代码:
@Test
public void test4() {
when(bean.getName()).thenReturn("aaa");
System.out.println(Utils.test4(bean));
Assert.assertEquals(Utils.test4(bean),"aaa");
}
使用场景二
写了一个SharedPreferences类。
public class SharedPreferencesHelper {
// Keys for saving values in SharedPreferences.
static final String KEY_NAME = "key_name";
static final String KEY_DOB = "key_dob_millis";
static final String KEY_EMAIL = "key_email";
// The injected SharedPreferences implementation to use for persistence.
private final SharedPreferences mSharedPreferences;
/**
* Constructor with dependency injection.
*
* @param sharedPreferences The {@link SharedPreferences} that will be used in this DAO.
*/
public SharedPreferencesHelper(SharedPreferences sharedPreferences) {
mSharedPreferences = sharedPreferences;
}
/**
* Saves the given {@link SharedPreferenceEntry} that contains the user's settings to
* {@link SharedPreferences}.
*
* @param sharedPreferenceEntry contains data to save to {@link SharedPreferences}.
* @return {@code true} if writing to {@link SharedPreferences} succeeded. {@code false}
* otherwise.
*/
public boolean savePersonalInfo(SharedPreferenceEntry sharedPreferenceEntry){
// Start a SharedPreferences transaction.
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(KEY_NAME, sharedPreferenceEntry.getName());
editor.putLong(KEY_DOB, sharedPreferenceEntry.getDateOfBirth().getTimeInMillis());
editor.putString(KEY_EMAIL, sharedPreferenceEntry.getEmail());
// Commit changes to SharedPreferences.
return editor.commit();
}
/**
* Retrieves the {@link SharedPreferenceEntry} containing the user's personal information from
* {@link SharedPreferences}.
*
* @return the Retrieved {@link SharedPreferenceEntry}.
*/
public SharedPreferenceEntry getPersonalInfo() {
// Get data from the SharedPreferences.
String name = mSharedPreferences.getString(KEY_NAME, "");
Long dobMillis =
mSharedPreferences.getLong(KEY_DOB, Calendar.getInstance().getTimeInMillis());
Calendar dateOfBirth = Calendar.getInstance();
dateOfBirth.setTimeInMillis(dobMillis);
String email = mSharedPreferences.getString(KEY_EMAIL, "");
// Create and fill a SharedPreferenceEntry model object.
return new SharedPreferenceEntry(name, dateOfBirth, email);
}
}
测试代码:
@RunWith(MockitoJUnitRunner.class)
public class SharedPreferencesHelperTest {
private static final String TEST_NAME = "Test name";
private static final String TEST_EMAIL = "[email protected]";
private static final Calendar TEST_DATE_OF_BIRTH = Calendar.getInstance();
static {
TEST_DATE_OF_BIRTH.set(1980, 1, 1);
}
private SharedPreferenceEntry mSharedPreferenceEntry;
private SharedPreferencesHelper mMockSharedPreferencesHelper;
private SharedPreferencesHelper mMockBrokenSharedPreferencesHelper;
@Mock
SharedPreferences mMockSharedPreferences;
@Mock
SharedPreferences mMockBrokenSharedPreferences;
@Mock
SharedPreferences.Editor mMockEditor;
@Mock
SharedPreferences.Editor mMockBrokenEditor;
@Before
public void initMocks() {
// Create SharedPreferenceEntry to persist.
mSharedPreferenceEntry = new SharedPreferenceEntry(TEST_NAME, TEST_DATE_OF_BIRTH,
TEST_EMAIL);
// Create a mocked SharedPreferences.
mMockSharedPreferencesHelper = createMockSharedPreference();
// Create a mocked SharedPreferences that fails at saving data.
mMockBrokenSharedPreferencesHelper = createBrokenMockSharedPreference();
}
@Test
public void sharedPreferencesHelper_SaveAndReadPersonalInformation() {
// Save the personal information to SharedPreferences
boolean success = mMockSharedPreferencesHelper.savePersonalInfo(mSharedPreferenceEntry);
assertThat("Checking that SharedPreferenceEntry.save... returns true",
success, is(true));
// Read personal information from SharedPreferences
SharedPreferenceEntry savedSharedPreferenceEntry =
mMockSharedPreferencesHelper.getPersonalInfo();
// Make sure both written and retrieved personal information are equal.
assertThat("Checking that SharedPreferenceEntry.name has been persisted and read correctly",
mSharedPreferenceEntry.getName(),
is(equalTo(savedSharedPreferenceEntry.getName())));
assertThat("Checking that SharedPreferenceEntry.dateOfBirth has been persisted and read "
+ "correctly",
mSharedPreferenceEntry.getDateOfBirth(),
is(equalTo(savedSharedPreferenceEntry.getDateOfBirth())));
assertThat("Checking that SharedPreferenceEntry.email has been persisted and read "
+ "correctly",
mSharedPreferenceEntry.getEmail(),
is(equalTo(savedSharedPreferenceEntry.getEmail())));
}
@Test
public void sharedPreferencesHelper_SavePersonalInformationFailed_ReturnsFalse() {
// Read personal information from a broken SharedPreferencesHelper
boolean success =
mMockBrokenSharedPreferencesHelper.savePersonalInfo(mSharedPreferenceEntry);
assertThat("Makes sure writing to a broken SharedPreferencesHelper returns false", success,
is(false));
}
/**
* Creates a mocked SharedPreferences.
*/
private SharedPreferencesHelper createMockSharedPreference() {
// Mocking reading the SharedPreferences as if mMockSharedPreferences was previously written
// correctly.
when(mMockSharedPreferences.getString(eq(SharedPreferencesHelper.KEY_NAME), anyString()))
.thenReturn(mSharedPreferenceEntry.getName());
when(mMockSharedPreferences.getString(eq(SharedPreferencesHelper.KEY_EMAIL), anyString()))
.thenReturn(mSharedPreferenceEntry.getEmail());
when(mMockSharedPreferences.getLong(eq(SharedPreferencesHelper.KEY_DOB), anyLong()))
.thenReturn(mSharedPreferenceEntry.getDateOfBirth().getTimeInMillis());
// Mocking a successful commit.
when(mMockEditor.commit()).thenReturn(true);
// Return the MockEditor when requesting it.
when(mMockSharedPreferences.edit()).thenReturn(mMockEditor);
return new SharedPreferencesHelper(mMockSharedPreferences);
}
/**
* Creates a mocked SharedPreferences that fails when writing.
*/
private SharedPreferencesHelper createBrokenMockSharedPreference() {
// Mocking a commit that fails.
when(mMockBrokenEditor.commit()).thenReturn(false);
// Return the broken MockEditor when requesting it.
when(mMockBrokenSharedPreferences.edit()).thenReturn(mMockBrokenEditor);
return new SharedPreferencesHelper(mMockBrokenSharedPreferences);
}
}
限制
- 不能 mock 静态方法
- 不能 mock 私有方法
- 不能 mock 构造方法
- 不能 mock equals() & hashCode()
吐槽几句,这几条约束其实已经足够搞死人了。