赞
踩
原则
1、分析需求场景
2、依据场景列出判断分支
3、依据分支编写测试用例,若方法具有多层调用,则依据逻辑,判断入参变化点,针对具有入参变化点的方法编写UT
4、编写实现逻辑
框架
本次使用Junit进行测试,基于Junit的模拟框架使用mockito配合powermock
Junit:每个测试都是一个方法,比较输入输出以实现代码验证
TestNG:测试被组织的类,与Junit相同,但是要运行TestNG,必须添加配置
基于Junit模拟框架:mockito、easymock、powermock
基础知识:(待补充。。。)
Mockito
Powermock
mock与spy的区别:
mock:类中所有的方法均被置空,均返回null,不真实执行方法内部逻辑
spy:类中所有方法均真实执行
下面将针对写单测时遇到的问题有针对性列举
1、针对类A方法doIt进行单测时,调用了其中的public static void getResult(String s),而且getResult中调用了工具类的private static int empty(String s)方法
目标是执行getResult进入方法内执行,而empty不真实执行
解决方法:
1.1首先分析
工具类需要@PrepareForTest进行声明;
static方法需要使用PowerMock进行mock;
进入类A的方法中真正执行,需要间谍spy
某些方法不真正执行,使用doReturn方式
1.2实战
被测类:
import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Scanner; public class A{ private static int empty() { System.out.println("enter empty"); return 1; } public static int getResult() { System.out.println("realprint"); int a = empty(); System.out.println(a); return 1; } public static int doIt() { return SumTwo.getResult(); } }
测试类:
import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import program.SumTwo; @RunWith(PowerMockRunner.class) @PrepareForTest(A.class) public class PowermockTest { @Test public void sumtwoTest() throws Exception { PowerMockito.mockStatic(A.class); PowerMockito.spy(A.class); PowerMockito.doReturn(2).when(A.class, "empty"); int result = A.doIt(); } }
该测试用例的运行结果符合预期,empty方法不执行,因此不会打印“123”
打印结果如下:
realprint
2
2、when中调用了类的方法,方法所在类必须先在prepare中声明出处,并且需要提前mock,若类中该方法为static则需要对该类用mockstatic,以便执行该方法
eg:
import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import program.SumTwo; @RunWith(PowerMockRunner.class) @PrepareForTest(SumTwo.class) public class PowermockTest { @Test public void sumtwoTest() throws Exception { PowerMockito.mockStatic(SumTwo.class); PowerMockito.doReturn(2).when(SumTwo.class, "empty"); int result = SumTwo.doIt(); } }
如上测试代码可以正常运行
当我们将mockstatic修改为mock,会出现如下报错:
java.lang.NullPointerException
at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.addAnswersForStubbing(PowerMockitoStubberImpl.java:68)
at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:43)
at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:111)
at PowermockTest.sumtwoTest(PowermockTest.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
3、对例1的扩展,模拟执行的private方法带入参
注意,若不想真正执行该private方法,除了使用doReturn外,需要模拟传入的入参与代码真正执行时的入参一致,否则仍然会执行该private方法。
import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import program.SumTwo; import static org.mockito.Matchers.any; @RunWith(PowerMockRunner.class) @PrepareForTest(SumTwo.class) public class PowermockTest { @Test public void sumtwoTest() throws Exception { PowerMockito.spy(SumTwo.class); PowerMockito.doReturn(2).when(SumTwo.class, "empty", 3); int result = SumTwo.doIt(); } }
运行上述调用代码,执行后,打印结果如下
realprint
enter empty
1
若我们修改empty的入参,为真实入参1则会出现如下打印结果
import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import program.SumTwo; import static org.mockito.Matchers.any; @RunWith(PowerMockRunner.class) @PrepareForTest(SumTwo.class) public class PowermockTest { @Test public void sumtwoTest() throws Exception { PowerMockito.spy(SumTwo.class); PowerMockito.doReturn(2).when(SumTwo.class, "empty", 1); int result = SumTwo.doIt(); } }
realprint
2
4、当对象创建在方法中进行,对该方法进行UT时,需要通过PowerMock的whenNew方法创建该对象,针对该对象进行打桩。
PowerMockito.whenNew(A.class).withArguments(arg1, arg2,… argn).thenReturn(result);
5、针对ENUM类型的单例,做UT时,使用白盒
public enum SingletonObject {
INSTANCE;
private int num;
protected void setNum(int num) {
this.num = num;
}
public int getNum() {
return num;
}
}
单例调用:
public class SingletonConsumer {
public String consumeSingletonObject() {
return String.valueOf(SingletonObject.INSTANCE.getNum());
}
}
UT:
import static org.junit.Assert.*; import static org.powermock.api.mockito.PowerMockito.*; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox; @RunWith(PowerMockRunner.class) @PrepareForTest({SingletonObject.class}) public class SingletonConsumerTest { @Test public void testConsumeSingletonObject() throws Exception { **// 真实执行单例类的某个方法使用spy,若不执行,使用mock(SingletonObject.class)** SingletonObject mockInstance = spy(SingletonObject.INSTANCE); Whitebox.setInternalState(SingletonObject.class, "INSTANCE", mockInstance); when(mockInstance.getNum()).thenReturn(42); assertEquals("42", new SingletonConsumer().consumeSingletonObject()); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。