lesson 1
github code:https://github.com/CasterIO/Tasko
WHY
How to setup
Download Espresso
- Open your app’s build.gradle file. This is usually not the top-level build.gradle file but app/build.gradle.
- Add the following lines inside dependencies:
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:runner:0.5'
Set the instrumentation runner
- Add to the same build.gradle file the following line in android.defaultConfig:
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Example build.gradle file
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22"
defaultConfig {
applicationId "com.my.awesome.app"
minSdkVersion 10
targetSdkVersion 22.0.1
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
dependencies {
// App's dependencies, including test
compile 'com.android.support:support-annotations:22.2.0'
// Testing-only dependencies
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
}
android {
compileSdkVersion 22
buildToolsVersion "22"
defaultConfig {
applicationId "com.my.awesome.app"
minSdkVersion 10
targetSdkVersion 22.0.1
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
dependencies {
// App's dependencies, including test
compile 'com.android.support:support-annotations:22.2.0'
// Testing-only dependencies
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
}
Analytics
In order to make sure we are on the right track with each new release, the test runner collects analytics. More specifically, it uploads a hash of the package name of the application under test for each invocation. This allows us to measure both the count of unique packages using Espresso as well as the volume of usage.
If you do not wish to upload this data, you can opt out by passing the following argument to the test runner: disableAnalytics "true" (see how to pass custom arguments).
Add the first test
Android Studio creates tests by default in src/androidTest/java/com.example.package/
Example JUnit4 test using Rules:
@RunWith(AndroidJUnit4.class)
@LargeTest
public class HelloWorldEspressoTest {
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class);
@Test
public void listGoesOverTheFold() {
onView(withText("Hello world!")).check(matches(isDisplayed()));
}
}
@LargeTest
public class HelloWorldEspressoTest {
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class);
@Test
public void listGoesOverTheFold() {
onView(withText("Hello world!")).check(matches(isDisplayed()));
}
}
ViewMatchers,
ViewActions
ViewAssertions
TDD
Lesson 2
What View Matchers are and how to use them
What View Assertions are and how to use them
What View Actions are and how to use them
How to click on buttons with Espresso
How to enter text into EditText widgets with Espresso
How to write a test that validates different screens are loaded.
How to debug tests when view are not found with view matchers
How to use the UIAutomatorViewer to speed up test development
Best practice for finding views (use the ID not string literals!)
How databases are cleaned before each test run (using Realm).
git code:https://github.com/CasterIO/Tasko
@Test
public void ahouldBeAbleToAddTaskAndTheTaskShow(){
// click the new button
onView(withId(R.id.menu_main_new_task)).perform(click());
// click task name
// input Task1
onView(withId(R.id.new_task_task_name)).perform(click()).perform(typeText("Task1"));
// Click task Description
// Task1 Desc
onView(withId(R.id.new_task_task_desc)).perform(click()).perform(typeText("Task1 Desc"));
// CLick Add
onView(withId(R.id.new_task_add)).perform(click());
// Task1 appear
onView(withText("Task1")).check(matches(isDisplayed()));
}
|
Lesson 3 UI Testing with Espresso and RecyclerView (Part 2)
Espresso interect with RecyclerView
need to add this first
androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2.1') {
exclude module: 'support-annotations'
exclude module: 'support-v4'
exclude module: 'support-v13'
exclude module: 'recyclerview-v7'
}
|
if we want to scroll the recycle view
onView(withId(R.id.main_task_list)).perform(RealmRecyclerViewActions.scrollToPosition(12));
onView(withText("Task10")).check(matches(isDisplayed()));
|
@Test
public void ahouldBeAbleToAddTaskAndTheTaskShow(){
for(int i=0;i<15;i++){
// click the new button
onView(withId(R.id.menu_main_new_task)).perform(click());
// click task name
// input Task1
onView(withId(R.id.new_task_task_name)).perform(click()).perform(typeText("Task"+i));
// Click task Description
// Task1 Desc
onView(withId(R.id.new_task_task_desc)).perform(click()).perform(typeText("Task1"+i+" Desc"));
// CLick Add
onView(withId(R.id.new_task_add)).perform(click());
}
// Task1 appear
onView(withText("Task1")).check(matches(isDisplayed()));
// scroll to Tasl 10
// Task 10 appear
onView(withId(R.id.main_task_list)).perform(RealmRecyclerViewActions.scrollToPosition(12));
onView(withText("Task10")).check(matches(isDisplayed()));
}
|
Lesson4 Creating Custom ViewMatchers1
foe below
onView(withId(R.id.main_task_list)).perform(RealmRecyclerViewActions.scrollToPosition(12));
|
if i dont want to use the matcher scroll to a position,i want to scroll in to a specific Text, like “Task10”,
onView(withId(R.id.main_task_list)).perform(RealmRecyclerViewActions.scrollTo(withText("Task10")));
|
something like that,bu it is not success,because the Structure of the recycle view is define bu ourself,the program don’t know where to find the text
so we need to create a Custom Mathcer
create a class,tham create a static method
we need to check is the parent view(item) is null,
if not null; find the R.id.task_item_task_name(the Text View hold the “Task10”)
if can find ,and it’s string is not empty,
is “Task10”? return true
public static Matcher<View> withTaskName(final String expected){
return new TypeSafeMatcher<View>() {
@Override
protected boolean matchesSafely(View item) {
if(item==null)
return false;
else {
TextView taskName = (TextView) item.findViewById(R.id.task_item_task_name);
if(taskName!=null&& !TextUtils.isEmpty(taskName.getText())&&taskName.getText().toString().equals(expected)){
return true;
}else
return false;
}
}
@Override
public void describeTo(Description description) {
description.appendText("look for "+expected+" in the recycle view");
}
};
}
|
Lesson5 Creating Custom ViewMatchers2
TypeSafeMatcher and BoundedMatcher
BoundedMatcher为我们指定了一个针对目标类型的子类型进行匹配的匹配规则。比如,我们现在需要一个Matcher<Object>对象,但实际上我们需要考察的目标类型是SearchItem,而SearchItem又是Object的子类,因此,我们可以通过BoundedMatcher来构造这个Matcher<Object>对象,只不过我们实际上进行检查的转变成了SearchItem类型,只要采用如下写法:
we can specific what type we want.
for example,we want Drawable
public static Matcher<View> withImageDrawable(final int resourceId) {
return new BoundedMatcher<View, ImageView>(ImageView.class) {
@Override
public void describeTo(Description description) {
description.appendText("has image drawable resource " + resourceId);
}
@Override
public boolean matchesSafely(ImageView imageView) {
return sameBitmap(imageView.getContext(), imageView.getDrawable(), resourceId);
}
};
}
private static boolean sameBitmap(Context context, Drawable drawable, int resourceId) {
Drawable otherDrawable = context.getResources().getDrawable(resourceId);
if (drawable == null || otherDrawable == null) {
return false;
}
if (drawable instanceof StateListDrawable && otherDrawable instanceof StateListDrawable) {
drawable = drawable.getCurrent();
otherDrawable = otherDrawable.getCurrent();
}
if (drawable instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
Bitmap otherBitmap = ((BitmapDrawable) otherDrawable).getBitmap();
return bitmap.sameAs(otherBitmap);
}
return false;
}
|
waht is StateListDrawable
need get current,
The current drawable that will be used by this drawable. For simple drawables, this is just the drawable itself. For drawables that change state like StateListDrawable and LevelListDrawable this will be the child drawable currently in use.
|
becuase the StateListDrawable have different state