二.Android单元测试 Mockito的更多用法(1)
三.Android单元测试 PowerMock给私有变量赋值
四.Android单元测试 PowerMock mock静态方法
基于以下开源框架
1.Mockito
2.PowerMock
本篇文章主要讲Android开发时,如何使用以上开源框架来进行单元测试。
正文:
创建一个工程,引入一下dependency
testCompile 'junit:junit:4.12'
testCompile ('org.powermock:powermock-api-mockito:1.6.2') {
exclude module: 'hamcrest-core'
exclude module: 'objenesis'
}
testCompile ('org.powermock:powermock-module-junit4:1.6.2') {
exclude module: 'hamcrest-core'
exclude module: 'objenesis'
}
testCompile "org.mockito:mockito-core:1.10.19"
这个程序是我之前做过的一个视频会议App,这里拿来做例子的是会议界面的代码。这里我们关注的主要是被测试类MeetingPresenterImp,所以这个被测试的类所依赖的类(MeetingAudioManager,MeetingController)的具体功能代码并不需要关注.
事实是有了Mokito来mock这些类之后,也并不需要关注这些依赖类的方法具体实现,我们只关注这些依赖类的是否在某些情况下被调用,或者指定它的方法的返回值.
我们这里程序使用的是MVP架构,即MODEL,VIEW,PRESENTER,我们为了测试轻量级,Presenter一定要尽量写成纯java类,这样才能方便测试,如果引入了过多的Android类,会非常难mock,测起来十分麻烦,那么就需要使用Robolectric来测试了,这里我们暂时不讲它的用法.
MainActivity.java
进行按钮的初始化,及点击事件绑定
点击cameraButton,会让MeetingPresenter进行摄像头的开关操作
点击speakerButton,会让MeetingPresenter进行扬声器的开关操作
public class MainActivity extends AppCompatActivity implements MeetingContract.MeetingView, View.OnClickListener {
Button cameraButton;
Button speakerButton;
MeetingContract.MeetingPresenter meetingPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
meetingPresenter = new MeetingPresenterImp(new MeetingController(), new MeetingAudioManager());
initViews();
}
private void initViews() {
cameraButton = (Button) findViewById(R.id.camera_button);
speakerButton = (Button) findViewById(R.id.camera_button);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.camera_button:
meetingPresenter.toggleCamera();
break;
case R.id.speaker_button:
meetingPresenter.toggleSpeaker();
break;
}
}
}
MeetingContract.java
接口定义
public class MeetingContract {
interface MeetingView {
}
interface MeetingPresenter {
void toggleCamera();
void toggleSpeaker();
}
}
MeetingPresenterImp.java
MeetingPresenter的实现类,负责逻辑的处理.
MeetingContract.MeetingPresenter {
private MeetingController meetingController;
private MeetingAudioManager audioManager;
public MeetingPresenterImp(MeetingController meetingController, MeetingAudioManager audioManager) {
this.meetingController = meetingController;
this.audioManager = audioManager;
}
@Override
public void toggleCamera() {
meetingController.toggleCamera();
}
@Override
public void toggleSpeaker() {
if (audioManager.isSpeakerOn()) {
audioManager.turnOffSpeaker();
} else {
audioManager.turnOnSpeaker();
}
}
}
MeetingAudioManager.java
音频管理类
public class MeetingAudioManager {
public boolean isSpeakerOn() {
//判断是否扬声器开启
return false;
}
public void turnOffSpeaker() {
//关闭扬声器
}
public void turnOnSpeaker() {
//开启扬声器
}
}
会议Controller,与C++层通信的类
MeetingController.java
package sample.mars.com.androidutsample;
public class MeetingController {
public void toggleCamera() {
//请求C++层切换摄像头
toggleCameraNative();
}
private native void toggleCameraNative();
}
我们现在要测试的就是上面的MeetingPresenterImp类,在此分享一个mac中AS的快捷键,快速新建test类
选中要测试的方法,并把测试类放到跟MeetingPresenter相同的包中
在新建好的测试类上方加上注解@RunWith(MockitoJUnitRunner.class)
我们要测试的是MeetingPresenterImp类,为了构造一个可测试的环境,让我们测试时不被一些它的依赖类的复杂情况所影响,可任意的控制它的依赖对象的状态或者方法的返回值,我们这里对MeetingPresenterImp的两个依赖类MeetingController和meetingAudioManager进行mock,在后面,我们就可以控制这两个类的方法的返回值,并可以检验这两个mock对象的某些方法是否被调用.
@RunWith(MockitoJUnitRunner.class)
public class MeetingPresenterImpTest {
@Mock
MeetingController meetingController;
@Mock
MeetingAudioManager meetingAudioManager;
MeetingPresenterImp meetingPresenterImp;
@Before
public void setUp() throws Exception {
meetingPresenterImp = new MeetingPresenterImp(meetingController, meetingAudioManager);
}
}
在下面的文章中,我们将真正开始写测试代码.
在前面我们已经写好了一个用来测试的例子,并创建好了测试类,现在开始写测试代码.
首先,我们从最简单的toggleCamera()方法开始测试,那么我们如何测试MeetingPresenter类的toggleCamera()方法运行正常呢?
我们只需要验证MeetingController类的toggleCamera()方法被执行即可,这里将用到Mockito的Verify()方法.
Mockito.verify()可以检验被mock的类的特定方法是否被调用,不过前提是在测试类中能访问到该方法,如果该方法是private的或者其他情况.
@Override
public void toggleCamera() {
meetingController.toggleCamera();
}
现在写出测试代码,但是我们这里故意将验证条件写错,去验证meetingAudioManager的turnOnSpeaker()是否执行,看会发生什么:
@Test
public void toggleCamera() throws Exception {
meetingPresenterImp.toggleCamera();
//Mockito.verify(meetingController).toggleCamera();
Mockito.verify(meetingAudioManager).turnOnSpeaker();
}
选中方法名,点击右键,选中 Run “toggleCamera()”,得到了如下错误提示:
也就是说,在meetingPresenterImp.toggleCamera()执行后,meetingAudioManager的turnOnSpeaker()并没有执行,所以测试失败.
现在在把测试代码改回验证meetingController的toggleCamera()是否执行.
然后选中方法名,点击右键,选中 Run “toggleCamera()”
@Test
public void toggleCamera() throws Exception {
meetingPresenterImp.toggleCamera();
Mockito.verify(meetingController).toggleCamera();
}
结果如下,我们的测试通过了,也就是说,在meetingPresenterImp.toggleCamera()执行后,MeetingController的toggleCamera()得到了执行.
那么,知道了我们这个测试通过了有什么用呢?举个栗子,如果在以后的代码重构中,某个开发人员不小心将meetingPresenterImp.toggleCamera()中meetingController.toggleCamera()这句代码误删了,那么我们再运行测试用例的时候,发现测试不通过,我们就知道原因是什么了,当然,真是开发中,情况不会这么简单.写测试的一个重要作用就是为了方便重构,每次改完代码准备提交到代码仓库中时,运行一下,检查是否所有测试用例都通过了,如果没有通过,就要查查是什么原因,并让它再次通过为止.
接下来,来验证MeetingPreImp.toggleSpeaker()方法
@Override
public void toggleSpeaker() {
if (audioManager.isSpeakerOn()) {
audioManager.turnOffSpeaker();
} else {
audioManager.turnOnSpeaker();
}
}
这里有两个路径,根据audioManager.isSpeakerOn()的结果来执行不同操作.
那么对应的,我们就应该写两个测试用例来测试了,话不多说,我们来看看代码:
这里同样的,我们故意将验证条件写错,实际应该验证meetingAudioManager的turnOffSpeaker()方法是否执行,但是写成验证turnOnSpeaker()是否执行.
@Test
public void should_turn_off_speaker() throws Exception {
Mockito.when(meetingAudioManager.isSpeakerOn()).thenReturn(true);
meetingPresenterImp.toggleSpeaker();
//Mockito.verify(meetingAudioManager).turnOffSpeaker();
Mockito.verify(meetingAudioManager).turnOnSpeaker();
}
得到的结果是:
错误提示告诉我们,也是告诉我们turnOnSpeaker()并没有执行,测试不通过.
接下来,把测试用例代码改回正确的验证条件,那么,测试也就能通过了.
那么这个例子对我们实际开发有什么帮助呢?如果我们重构代码时,不小心把if判断中的操作写反了,我们就能借助单元测试,快速的发现问题了.
@Test
public void should_turn_off_speaker() throws Exception { Mockito.when(meetingAudioManager.isSpeakerOn()).thenReturn(true);
meetingPresenterImp.toggleSpeaker();
Mockito.verify(meetingAudioManager).turnOffSpeaker();
}
在以上代码中,我们使用了Mockito的另一个方法when,它能够让mock对象在调用有返回值的方法时,返回我们指定的结果,我们就不需要去操心这个mock方法内部是如何运作的,可以快速的去测试某条路径.
有了上面的介绍,我们很快能写出以下的测试用例了,它的测试结果也是通过的.
@Test
public void should_turn_on_speaker() throws Exception {
Mockito.when(meetingAudioManager.isSpeakerOn()).thenReturn(false);
meetingPresenterImp.toggleSpeaker();
Mockito.verify(meetingAudioManager).turnOnSpeaker();
}
这篇文章,介绍了mockito的最基本的两种用法,在后面的文章中,将同样通过例子,介绍更复杂一些的用法.
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。