-
Notifications
You must be signed in to change notification settings - Fork 5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
使用 Espresso 和 Mockito 测试 MVP #1666
Conversation
申请校对 |
@lovexiaov 好的。 |
|
||
As software developers, we try our best to do what is right and make sure that we are not incompetent, and try to have others and our employers trust in the code we write. We all try to follow best practices and apply good architecture patterns, but sometimes many of us find it difficult to actually test what we code. | ||
作为软件开发者,我们尽最大努力做正确的事情确保我们并非无能,并且让其他同事以及领导信任我们所写的代码。我们遵守最好的变成习惯、使用好的架构模型,但是有时发现要确切的测试我们所写的代码很难。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我们遵守最好的变成习惯
错别字“编程习惯”
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
架构模型
架构模式
|
||
Personally, I have seen a few open-source projects where the developers are great at building awesome products—and can build any application you can think of—but for some reason lack at writing the proper tests, if any at all. | ||
就个人而言,我发现一些开源项目的开发者非常善于打造令人惊叹的产品(可以打造任何你可以想象的应用),但是由于某些原因缺乏编写正确测试的能力,如果有的话。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
但是由于某些原因缺乏编写正确测试的能力,如果有的话。
但是,由于某些原因没有写适当的测试,甚至一点都没有。
|
||
![724E8fE.png](https://i2.wp.com/www.andevcon.com/hubfs/EVENTS_ASSETS/ANDEVCON/Images/Article_Images/MVP%20Mockito/724E8fE.png) | ||
|
||
For the sake of this article, let’s imagine that this is a multi-million-dollar product, and that the way it is now is the way it should be for a very long time. And if it were to ever change, we should be notified immediately. | ||
为了本文价值,我们假设这是一个价值数百万的产品,并且它现在的样子将会持续很长时间。如果一旦发生变化,我们需要立刻知晓。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
如果一旦发生变化
“如果”和“一旦”有点重复的意思,建议改为:
一旦它发生变化
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
为了本文价值
出于文章的需要
|
||
So first, here’s the complete code used to test the TooBar design. If you have no idea what is going on here, don’t worry: We will walk through it together. | ||
如下是测试 ToolBar 的完整代码。如果你看不懂这到底是什么鬼,也没关系,后面我们一起过一下。 | ||
|
||
``` | ||
@RunWith (AndroidJUnit4.class) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里的代码中有乱码的部分,@根号三 替换一下吧
@RunWith (AndroidJUnit4.class)
public class MainActivityTest {
@Rule
public ActivityTestRule<MainActivity> activityTestRule =
new ActivityTestRule<>(MainActivity.class);
@Test
public void testToolbarDesign() {
// Check that the tool ba is displayed
onView(withId(R.id.toolbar)).check(matches(isDisplayed()));
// Check that the toolbar title is set to "TestingMVP"
onView(withText(R.string.app_name)).check(matches(withParent(withId(R.id.toolbar))));
// Verify the toolbar background matches the primary color
onView(withId(R.id.toolbar)).check(matches(withToolbarBackGroundColor()));
}
private Matcher<? super View> withToolbarBackGroundColor() {
return new BoundedMatcher<View, View>(View.class) {
@Override
public boolean matchesSafely(View view) {
// Get the toolbar background as a ColorDrawable
final ColorDrawable buttonColor = (ColorDrawable) view.getBackground();
// Match the toolbar background to the apps primary color
return ContextCompat
.getColor(activityTestRule.getActivity(), R.color.colorPrimary) ==
buttonColor.getColor();
}
@Override
public void describeTo(Description description) {
// do nothing, we could technically add some additional text to the description
// here if wanted
}
};
}
}
@@ -76,47 +76,49 @@ public class MainActivityTest { | |||
} | |||
``` | |||
|
|||
First things first: We need to tell JUnit what sort of test we are running. This is what the first line does (@RunWith (AndroidJUnit4.class)). It says. “Hey, listen, I want to run an Android test that uses JUnit4 on an actual connected device.” | |||
首先,我们需要告诉 JUnit 我们进行测试的类型。具体内容对应第一行内容 (@RunWith (AndroidJUnit4.class))。它这样声明,“嘿,听着,我将在真机上使用 JUnit4 进行 Android 测试”。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
So what exactly is an Android test? An Android test is a test that runs on the device instead of locally on the [Java Virtual Machine (JVM)](https://en.wikipedia.org/wiki/Java_virtual_machine) on your computer. This means that a device needs to be connected to your computer in order to run the test. This gives the test code access to functional Android framework APIs. | ||
那么 Android 测试到底是什么呢?Android 测试是在 Android 设备上而非电脑上的 [Java 虚拟机 (JVM)](https://en.wikipedia.org/wiki/Java_virtual_machine) 的测试。这就意味着 Android 设备需要连接到电脑以便运行测试。这就使得测试可以访问 Android 框架功能性 APIs。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Android 测试是指在 Android 设备而非本地 Java 虚拟机 (JVM) 上执行的测试。这意味着为了执行 Android 测试,需要将一台 Android 设备连接到你的电脑。这样测试代码就能访问 Android 框架功能 API 了。
中文里是没有英文中复词加 “s” 这样的语法的。
申请校对 @sqrthree |
@GangsterHyj 好的 |
|
||
As software developers, we try our best to do what is right and make sure that we are not incompetent, and try to have others and our employers trust in the code we write. We all try to follow best practices and apply good architecture patterns, but sometimes many of us find it difficult to actually test what we code. | ||
作为软件开发者,我们尽最大努力做正确的事情确保我们并非无能,并且让其他同事以及领导信任我们所写的代码。我们遵守最好的变成习惯、使用好的架构模型,但是有时发现要确切的测试我们所写的代码很难。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
架构模型
架构模式
|
||
![724E8fE.png](https://i2.wp.com/www.andevcon.com/hubfs/EVENTS_ASSETS/ANDEVCON/Images/Article_Images/MVP%20Mockito/724E8fE.png) | ||
|
||
For the sake of this article, let’s imagine that this is a multi-million-dollar product, and that the way it is now is the way it should be for a very long time. And if it were to ever change, we should be notified immediately. | ||
为了本文价值,我们假设这是一个价值数百万的产品,并且它现在的样子将会持续很长时间。如果一旦发生变化,我们需要立刻知晓。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
为了本文价值
出于文章的需要
``` | ||
|
||
This one says “Hey, lets see if there is some text that equals ‘R.string.app_name’ and has a parent whose id is R.id.toolbar.” | ||
这行是说,“嘿,让我们看看是 R.id.toolbar 的孩子是否文本和 ‘R.string.app_name’ 相同的”。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
让我们看看是 R.id.toolbar 的孩子是否文本和 ‘R.string.app_name’ 相同的
依照代码的逻辑,我觉得应该这么翻译:
让我们看看是否有文本内容为 R.string.app_name 的 textView ,并且看看它的父 View 的 id 是否为 R.id.toolbar 。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
让我们看看在 id 为
R.id.toolbar
的父 View 中有没有文本与R.string.app_name
相同的子 View。
做下参考吧。
|
||
The last line in this test is a bit more involved. So basically what it is trying to do is to make sure that the background of the toolbar is equal to the app’s primary color. | ||
最后一行的测试更有趣一些。它是要确认 toolbar 的背景色是否和应用的首要颜色一致。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
首要颜色..文中好多地方都用到了..
primary color 印象中是在 manifest.xml 里通过 style/AppTheme 设置的?我觉得翻译成 主题颜色 更好一些..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我也感觉“首要”不太好。primary color 一般用作背景色,我觉得翻译为 主要背景色 比较好,或者可以问一下搞设计的同学有没有标准翻译。
|
||
Since the “Hide” button was clicked, we need to make sure the TextView is actually hidden now. We do this by adding a “not()” in front of the isDisplayed() method we used previously, and the button text has been changed to “Show”. This is the same as doing “!=” in plain ol’ java. | ||
因为点击了 “Hide” 按钮,我们需要验证 TextView 是否真的隐藏了。具体做法是在 diDisplayed() 方法前置一个 “not()”,并且按钮文案变为 “Show”。其实这就和 java 中的 “!=” 操作符一样。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dDisplayed() => isDisplayed()
英文拼写错误
按钮文案 => 按钮文字 | 按钮文本
|
||
Our reverseViewVisibility() method calls into what we call (for this tutorial at least) our “model” to set the proper visibility on the view passed in. | ||
reverseViewVisibility() 方法调用 “model” 对传入的 view 进行相对的可见性设置。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
相对的可见性....
把 相对的 删掉吧....而且 proper 不是 适当的 意思吗
|
||
This is how we mock using Mockito | ||
下面是使用 Mockito 的模拟 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
下面展示了如何使用 Mockito 进行模拟
@@ -364,7 +366,7 @@ public class MainPresenterImplTest { | |||
} | |||
``` | |||
|
|||
The first actual test we want to write is called “testReverseViewVisibilityFromVisibleToGone”. As the name says, we are going to make sure that the presenter sets the proper visibility when the view passed in to the reverseViewVisibility() method is visible. | |||
我们要写的第一个测试是 “testReverseViewVisibilityFromVisibleToGone”。顾名思义,我们验证的是当 view 可见时,调用 presenter 的 reverseViewVisibility() 方法结果正确。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我们验证的是当 view 可见时,调用 presenter 的 reverseViewVisibility() 方法结果正确。
我们将要验证的是,当可见的 View 被传入 presenter 的 reverseViewVisibility() 方法时,presenter 能正确地设置 View 的可见性。
...仅供参考
@skyar2009 @sqrthree 校对完成~ |
@lovexiaov 校对完了吗? |
@skyar2009 看起来两位校对者都已经校对好了~ 可以来根据校对意见进行调整了哈 ┏ (゜ω゜)=☞ |
|
||
These tests go in the androidTest directory. | ||
测试代码存放位置为 androidTest 目录。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
测试代码存放在 androidTest 目录。
``` | ||
|
||
This one says “Hey, lets see if there is some text that equals ‘R.string.app_name’ and has a parent whose id is R.id.toolbar.” | ||
这行是说,“嘿,让我们看看是 R.id.toolbar 的孩子是否文本和 ‘R.string.app_name’ 相同的”。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
让我们看看在 id 为
R.id.toolbar
的父 View 中有没有文本与R.string.app_name
相同的子 View。
做下参考吧。
|
||
The last line in this test is a bit more involved. So basically what it is trying to do is to make sure that the background of the toolbar is equal to the app’s primary color. | ||
最后一行的测试更有趣一些。它是要确认 toolbar 的背景色是否和应用的首要颜色一致。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我也感觉“首要”不太好。primary color 一般用作背景色,我觉得翻译为 主要背景色 比较好,或者可以问一下搞设计的同学有没有标准翻译。
|
||
``` | ||
onView(withId(R.id.toolbar)).check(matches(withToolbarBackGroundColor())); | ||
``` | ||
|
||
So by default, Espresso does not provide a straightforward way to do this, so we need to create what is called a [Matcher](https://developer.android.com/reference/android/support/test/espresso/matcher/package-summary.html). A Matcher is exactly what we have been using previously to match some view property to another. In this case, we want to match the primary color to the toolbars background. | ||
Espresso 默认是不支持简单直接的方式进行类似测试,因此我们需要创建 [Matcher](https://developer.android.com/reference/android/support/test/espresso/matcher/package-summary.html)。Matcher 确切的说是我们前面使用的判断 view 属性是否与预期一致的工具。这里,我们需要匹配首要颜色是否与 toolbar 背景一致。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Espresso 默认是不支持简单直接的方式进行类似测试
Espresso 没有提供直接的方式来做此校验
|
||
What we do is we create a [Matcher](https://developer.android.com/reference/android/support/test/espresso/matcher/BoundedMatcher.html) and override the matchesSafely() method. The code inside this method is pretty easy to understand. First we get the toolbar’s background, then we compare it to the app’s primary color. If it is equal, it returns true; false otherwise. | ||
我们需要创建一个 [Matcher](https://developer.android.com/reference/android/support/test/espresso/matcher/BoundedMatcher.html) 并覆盖 matchesSafely() 方法。该方法里面的代码十分易懂。首先我们获取 toolbar 背景色,然后将至与应用首要颜色对比。如果相等,返回 true 否则返回 false。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
然后将至与应用首要颜色对比。
“将至” 错别字,个人认为可以省略掉。
|
||
Then we check that the button is also displayed properly, and that the text on the button (by default) is set to “Hide”. | ||
然后检查按钮也是显示状态,并且其文案显示为 “Hide”。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里的 by default 建议翻译出来,因为这个 button 中的文字是会改变的。
并且其文案显示为 “Hide”
并且其文案(默认)显示为 “Hide”
|
||
Here is the full UI test code: | ||
如下是全部的 UI 测试代码: | ||
|
||
``` | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
代码乱码。
@RunWith (AndroidJUnit4.class)
public class MainActivityTest {
@Rule
public ActivityTestRule<MainActivity> activityTestRule =
new ActivityTestRule<>(MainActivity.class);
@Test
public void testToolbarDesign() {
// Check that the tool ba is displayed
onView(withId(R.id.toolbar)).check(matches(isDisplayed()));
// Check that the toolbar title is set to "TestingMVP"
onView(withText(R.string.app_name)).check(matches(withParent(withId(R.id.toolbar))));
// Verify the toolbar background matches the primary color
onView(withId(R.id.toolbar)).check(matches(withToolbarBackGroundColor()));
}
@Test
public void testHideShowTextView() {
// Check the TextView is displayed with the right text
onView(withId(R.id.tv_to_show_hide)).check(matches(isDisplayed()));
onView(withId(R.id.tv_to_show_hide)).check(matches(withText("Hello World!")));
// Check the button is displayed with the right initial text
onView(withId(R.id.btn_change_visibility)).check(matches(isDisplayed()));
onView(withId(R.id.btn_change_visibility)).check(matches(withText("Hide")));
// Click on the button
onView(withId(R.id.btn_change_visibility)).perform(click());
// Check that the TextView is now hidden
onView(withId(R.id.tv_to_show_hide)).check(matches(not(isDisplayed())));
// Check that the button has the proper text
onView(withId(R.id.btn_change_visibility)).check(matches(withText("Show")));
// Click on the button
onView(withId(R.id.btn_change_visibility)).perform(click());
// Check the TextView is displayed again with the right text
onView(withId(R.id.tv_to_show_hide)).check(matches(isDisplayed()));
onView(withId(R.id.tv_to_show_hide)).check(matches(withText("Hello World!")));
// Check that the button has the proper text
onView(withId(R.id.btn_change_visibility)).check(matches(isDisplayed()));
onView(withId(R.id.btn_change_visibility)).check(matches(withText("Hide")));
}
private Matcher<? super View> withToolbarBackGroundColor() {
return new BoundedMatcher<View, View>(View.class) {
@Override
public boolean matchesSafely(View view) {
// Get the toolbar background as a ColorDrawable
final ColorDrawable buttonColor = (ColorDrawable) view.getBackground();
// Match the toolbar background to the apps primary color
return ContextCompat
.getColor(activityTestRule.getActivity(), R.color.colorPrimary) ==
buttonColor.getColor();
}
@Override
public void describeTo(Description description) {
// do nothing, we could technically add some additional text to the description
// here if wanted
}
};
}
}
|
||
The great thing about unit tests—as opposed to Android tests—is that they run locally on your machine on the JVM. No need to have a device attached, and the tests run so much faster. The downside is that they do not have access to functional Android framework APIs. Overall, when testing anything other then UI you should try your best to write unit tests instead of Android/Instrumentation tests. The faster the tests, the better. | ||
单元测试最大特点是在本机的 JVM 环境上运行(与 Android 测试不同)。无需连接设备,测试跑的也更快。缺点就是无法访问 Android 框架 API。总之进行 UI 之外的测试时,尽量使用单元测试而非 Android/Instrumentation 测试。测试内容运行的越快越好。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
测试内容运行的越快越好。
测试运行的越快越好。
|
||
Let start start testing our presenter first. When it comes to a presenter—ANY presenter—we need to make sure that the view is attached to the presenter. Note: We are NOT testing the view. We just need to make sure that the view is attached so that we can verify the proper view methods are being made in the right time. Keep this in mind as this is very important. | ||
我们首先测试 presenter。当涉及 presenter (任何 presenter)我们需要确保 view 已与之关联。注意:我们并不测试 view。我们只需要确保 view 的绑定以便确认是否在正确的时间调用了正确的 view 方法。记住,这很重要。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
当涉及 presenter (任何 presenter)我们需要确保 view 已与之关联。
when it comes to -->当提到...时,结合本文语境,建议翻译为:
当使用 presenter (任何 presenter)时,我们需要确保 view 已与之关联。
@@ -379,11 +381,11 @@ The first actual test we want to write is called “testReverseViewVisibilityFro | |||
} | |||
``` | |||
|
|||
Let’s step through this together. What is actually going on here? Well, when the isShown() method is called on the view that is passed in to the presenter, we want to say yes, the view is shown, because we are testing from visible to gone, so we need to start at visible. Then, we call the presenters reverseViewVisibility() method by passing in the mocked view. Now we need to verify that the mocked views .setVisibility() was called at least once, and it was set to View.GONE. Afterwards, we need to verify that the presenters view setButtonText() method was called. Not that hard right? | |||
我们一起看下,这里具体做了什么?由于我们要测试的是 view 从可见到不可见的操作,我们需要 view 一开始是可见的,因此我们希望一开始调用 view 的 isShown() 方法返回是 true。接着,以模拟的 view 作为入参调用 presenter 的 reverseViewVisibility() 方法。现在我们需要确认 view 最近被调用的方法是 setVisibility(),并且设置为 GONE。然后,我们需要确认 presenter view 的 setButtonText() 方法是否调用。并不难吧? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我们需要确认 presenter view 的 setButtonText() 方法是否调用。
结合代码 presenters view 应该翻译为
与 presenter 绑定的 view
@sqrthree @skyar2009 校对完毕,说一下建议:
--以上 |
@skyar2009 两位校对者都已经校对好了~ 可以来根据校对意见进行调整了哈 ┏ (゜ω゜)=☞ |
@sqrthree 已修改 |
@skyar2009 已经 merge 啦~ 快快麻溜发布到掘金专栏然后给我发下链接,方便及时添加积分哟。 |
No description provided.