一.Android单元测试 Mockito的简单用法


二.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的最基本的两种用法,在后面的文章中,将同样通过例子,介绍更复杂一些的用法.

智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告