【Java】使用PowerMockito mock static方法/new对象/mock对象的public或private方法的简单示例
文章目录
1.针对方法打桩
1.1 打桩类的public static方法
测试用例中如果需要对public静态方法的打桩,针对测试类增加注解@RunWith(PowerMockRunner.class)同时针对静态方法所在的类增加注解@PrepareForTest({StaticMethod.class}),接着在测试用例调用方法之前增加
PowerMockito.mockStatic(StaticMethod.class);
PowerMockito.when(StaticMethod.getJavaVersion()).thenReturn(“1.8.0_92”); 或者用写法
PowerMockito.doReturn(“1.8.0_92”).when(StaticMethod.class, “getJavaVersion”);
另外对PowerMockito的调用最好在产品类ProductClass对象new之前进行,这样保障一切模拟数据就绪后再进行产品类的测试,保证测试用例一次性成功编写的几率。
import junit.framework.TestCase;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest({StaticMethod.class})
public class ProductClassTest extends TestCase {
public void test_getCustomNameByVersion() {
PowerMockito.mockStatic(StaticMethod.class);
PowerMockito.when(StaticMethod.getJavaVersion()).thenReturn("1.8.0_92");
ProductClass product = new ProductClass();
assertEquals("18VersionSeries", product.getCustomNameByVersion());
}
}
ProductClassTest.java
public class ProductClass {
public String getCustomNameByVersion() {
String javaVersion = StaticMethod.getJavaVersion();
if(javaVersion.split("_")[0].contains("1.8")) {
return "18VersionSeries";
}
else {
return "OTHER";
}
}
}
ProductClass.java产品代码调用其他类的静态方法
public class StaticMethod {
public static String getJavaVersion() {
throw new RuntimeException();
}
}
1.2 打桩类的private static方法
针对StaticMethod类中的private static方法打桩的时候,外部调用StaticMethod类的public方法仍然保持实际代码的调用,因此在模拟private static方法之前,增加一行
PowerMockito.spy(StaticMethod.class);或者
PowerMockito.when(StaticMethod.getJavaVersion()).thenCallRealMethod();
以此保证除了具体的某个方法打桩,其他的方法保持照旧。另外,针对private方法,因为正常是无法直接访问的,因此需要使用
下面的形式进行调用。这里要注意一个事情,doReturn when 和 when thenReturn的差异,后者是虽然做了打桩但是实际的代码还是会走一遍。
PowerMockito.doReturn(true).when(StaticMethod.class, “isProduct”);
import junit.framework.TestCase;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest({StaticMethod.class})
public class ProductClassTest extends TestCase {
public void test_getCustomNameByVersion_product() throws Exception {
PowerMockito.mockStatic(StaticMethod.class);
PowerMockito.spy(StaticMethod.class);
PowerMockito.doReturn(true).when(StaticMethod.class, "isProduct");
ProductClass product = new ProductClass();
assertEquals("18VersionSeries", product.getCustomNameByVersion());
}
}
ProductClassTest.java测试用例
public class StaticMethod {
public static String getJavaVersion() {
if (isProduct()) {
System.out.println("getJavaVersion()::product");
return System.getProperty("java.version");
}
System.out.println("getJavaVersion()::test");
throw new RuntimeException();
}
private static boolean isProduct() {
if (System.getenv().containsKey("isProduct=false")) {
System.out.println("isProduct()::false");
return false;
} else {
System.out.println("isProduct()::true");
return true;
}
}
}
StaticMethod.java
public class ProductClass {
public String getCustomNameByVersion() {
String javaVersion = StaticMethod.getJavaVersion();
if(javaVersion.split("_")[0].contains("1.8")) {
return "18VersionSeries";
}
else {
return "OTHER";
}
}
}
ProductClass.java
1.3 打桩类的public方法实现部分中使用的new对象
如果要对 StudentHandler.java的new对象进行mock,那么需要把StudentHandler对象所在的类StudentMng放到 @PrepareForTest({StudentMng.class})
需要特别注意,在PrepareForTest中放的类在统计代码覆盖率的时候,覆盖率是0。如果要针对该类进行代码测试覆盖,那需要以其他的方式进行模拟测试。
1 package training;
2
3 import junit.framework.TestCase;
4 import org.junit.runner.RunWith;
5 import org.powermock.api.mockito.PowerMockito;
6 import org.powermock.core.classloader.annotations.PrepareForTest;
7 import org.powermock.modules.junit4.PowerMockRunner;
8
9
10 import java.util.ArrayList;
11 import java.util.List;
12 @RunWith(PowerMockRunner.class)
13 @PrepareForTest({StudentMng.class})
14 public class StudentMngTest extends TestCase {
15
16
17 public void test_getSpecifiedStudents() throws Exception {
18 StudentHandler handler = PowerMockito.mock(StudentHandler.class);
19 PowerMockito.when(handler.getAllStudents()).thenReturn(mockStudents());
20 PowerMockito.whenNew(StudentHandler.class).withNoArguments().thenReturn(handler);
21 StudentMng mng = new StudentMng();
22 assertEquals(1, mng.getSpecifiedStudents(10).size());
23 }
24
25 private List<Student> mockStudents() {
26 List<Student> students = new ArrayList<>();
27 Student stu = new Student();
28 stu.setScore(new Score(80, 11));
29 students.add(stu);
30 return students;
31 }
32 }
StudentMngTest.java
1 package training;
2
3 import java.util.List;
4
5 public class StudentHandler {
6 public StudentHandler() {
7 System.out.println("invoke StudentHandler()");
8 throw new RuntimeException();
9 }
10 public List<Student> getAllStudents() {
11 System.out.println("invoke getAllStudents");
12 throw new RuntimeException();
13 }
14 }
StudentHandler.java 是一个不方便直接new的类
1 package training;
2
3 public class Student {
4 private String sno;
5 private String name;
6 private Score score = new Score(100, 1000);
7
8 public String getName() {
9 System.out.println("invoke getName");
10 return name;
11 }
12
13 public void setName(String name) {
14 System.out.println("invoke setName");
15 this.name = name;
16 }
17
18 public String getSno() {
19 System.out.println("invoke getSno");
20 return sno;
21 }
22
23 public void setSno(String sno) {
24 System.out.println("invoke setSno");
25 this.sno = sno;
26 }
27
28 public void setScore(Score score) {
29 System.out.println("invoke setScore");
30 this.score = score;
31 }
32
33 public Score getScore() {
34 System.out.println("invoke getScore");
35 return score;
36 }
37
38 public String getScoreRecord(int delta) {
39 System.out.println("invoke getScoreRecord");
40 return String.format("sno:%s,name:%s,value:%d,level:%d", sno, name, score.getValue() + delta, score.getLevel());
41 }
42 }
Student.java
1 package training;
2
3 public class Score {
4 private int value;
5 private int level;
6
7 public Score(int value, int level) {
8 this.value = value;
9 this.level = level;
10 }
11
12 public int getLevel() {
13 System.out.println("invoke getLevel");
14 return level;
15 }
16
17 public void setLevel(int level) {
18 System.out.println("invoke setLevel");
19 this.level = level;
20 }
21
22 public int getValue() {
23 System.out.println("invoke getValue");
24 return value;
25 }
26
27 public void setValue(int value) {
28 System.out.println("invoke setValue");
29 this.value = value;
30 }
31 }
Score.java
1.4打桩类的public方法
2种方式,方式一:PowerMockito.mock方式,对应StudentTest.java中的test_mock_public_method_powermock()测试用例
方式二:函数复写override方式,对应StudentTest.java中的test_mock_public_method_override()测试用例
两种方式比较,方式一代码看起来简洁。 方式二 测试用例运行时间效率很高。
1 package training;
2
3 import junit.framework.TestCase;
4 import org.powermock.api.mockito.PowerMockito;
5
6 public class StudentTest extends TestCase {
7 public void test_mock_public_method_powermock()
8 {
9 Student stu = PowerMockito.mock(Student.class);
10 PowerMockito.when(stu.getName()).thenReturn("test_name");
11 PowerMockito.when(stu.getNewName()).thenCallRealMethod();
12 assertEquals("new_test_name", stu.getNewName());
13 }
14
15 public void test_mock_public_method_override()
16 {
17 Student stu = new Student() {
18 @Override
19 public String getName() {
20 return "test_name";
21 }
22 };
23 assertEquals("new_test_name", stu.getNewName());
24 }
25 }
StudentTest.java
1 package training;
2
3 public class Student {
4 private String name;
5
6
7 public String getName() {
8 System.out.println("invoke getName");
9 throw new RuntimeException();
10 // return name;
11 }
12
13 public String getNewName() {
14 System.out.println("invoke getNewName");
15 return "new_" + getName();
16 }
17
18 }
Student.java
1.5 打桩类的private方法
Student.java类中getName方法为private的,因此需要使用PowerMockito.when(stu, “getName”).thenReturn(“test_name”); 方式进行mock。
被模拟的Student类需要在测试用例test类的开头增加以下两行。
@RunWith(PowerMockRunner.class)
@PrepareForTest({Student.class})
1 package training;
2
3 import junit.framework.TestCase;
4 import org.junit.runner.RunWith;
5 import org.powermock.api.mockito.PowerMockito;
6 import org.powermock.core.classloader.annotations.PrepareForTest;
7 import org.powermock.modules.junit4.PowerMockRunner;
8
9 @RunWith(PowerMockRunner.class)
10 @PrepareForTest({Student.class})
11 public class StudentTest extends TestCase {
12 public void test_mock_public_method_powermock()
13 {
14 Student stu = PowerMockito.mock(Student.class);
15 try {
16 PowerMockito.when(stu, "getName").thenReturn("test_name");
17 } catch (Exception e) {
18 e.printStackTrace();
19 }
20 PowerMockito.when(stu.getNewName()).thenCallRealMethod();
21 assertEquals("new_test_name", stu.getNewName());
22 }
23
24 }
StudentTest.java
1 package training;
2
3 public class Student {
4 private String sno;
5 private String name;
6 private Score score = new Score(100, 1000);
7
8 private String getName() {
9 System.out.println("invoke getName");
10 throw new RuntimeException();
11 // return name;
12 }
13
14 public String getNewName() {
15 System.out.println("invoke getNewName");
16 return "new_" + getName();
17 }
18
19 public void setName(String name) {
20 System.out.println("invoke setName");
21 this.name = name;
22 }
23
24 public String getSno() {
25 System.out.println("invoke getSno");
26 return sno;
27 }
28
29 public void setSno(String sno) {
30 System.out.println("invoke setSno");
31 this.sno = sno;
32 }
33
34 public void setScore(Score score) {
35 System.out.println("invoke setScore");
36 this.score = score;
37 }
38
39 public Score getScore() {
40 System.out.println("invoke getScore");
41 return score;
42 }
43
44 public String getScoreRecord(int delta) {
45 System.out.println("invoke getScoreRecord");
46 return String.format("sno:%s,name:%s,value:%d,level:%d", sno, name, score.getValue() + delta, score.getLevel());
47 }
48 }
Student.java
2.针对变量打桩
2.1 打桩类的private成员变量
方法一: Whitebox
来源StudentMngTest.java,关键模拟代码如下,Whitebox.setInternalState(mng, “handler”, handler);打桩设置对象的私有成员变量。
PowerMockito.when(mng.getSpecifiedStudents(10)).thenCallRealMethod();控制调用实际的方法。
StudentMng mng = PowerMockito.mock(StudentMng.class); 控制StudentMng对象的初始化过程,保证在对象初始化的时候不去做StudentHandler成员变量的初始化。
public void test_getSpecifiedStudents() {
StudentHandler handler = PowerMockito.mock(StudentHandler.class);
PowerMockito.when(handler.getAllStudents()).thenReturn(mockStudents());
StudentMng mng = PowerMockito.mock(StudentMng.class);
Whitebox.setInternalState(mng, “handler”, handler);
PowerMockito.when(mng.getSpecifiedStudents(10)).thenCallRealMethod();
assertEquals(1, mng.getSpecifiedStudents(10).size());
}
package training;
import junit.framework.TestCase;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.reflect.Whitebox;
import java.util.ArrayList;
import java.util.List;
public class StudentMngTest extends TestCase {
public void test_getSpecifiedStudents() {
StudentHandler handler = PowerMockito.mock(StudentHandler.class);
PowerMockito.when(handler.getAllStudents()).thenReturn(mockStudents());
StudentMng mng = PowerMockito.mock(StudentMng.class);
Whitebox.setInternalState(mng, "handler", handler);
PowerMockito.when(mng.getSpecifiedStudents(10)).thenCallRealMethod();
assertEquals(1, mng.getSpecifiedStudents(10).size());
}
private List<Student> mockStudents() {
List<Student> students = new ArrayList<>();
Student stu = new Student();
stu.setScore(new Score(80, 11));
students.add(stu);
return students;
}
}
StudentMngTest.java 打桩后 连初始化StudentMng对象的时候都模拟完成。
package training;
import java.util.List;
public class StudentHandler {
public StudentHandler() {
System.out.println("invoke StudentHandler()");
// throw new RuntimeException();
}
public List<Student> getAllStudents() {
System.out.println("invoke getAllStudents");
throw new RuntimeException();
}
}
StudentHandler.java
package training;
public class Student {
private String sno;
private String name;
private Score score = new Score(100, 1000);
public String getName() {
System.out.println("invoke getName");
return name;
}
public void setName(String name) {
System.out.println("invoke setName");
this.name = name;
}
public String getSno() {
System.out.println("invoke getSno");
return sno;
}
public void setSno(String sno) {
System.out.println("invoke setSno");
this.sno = sno;
}
public void setScore(Score score) {
System.out.println("invoke setScore");
this.score = score;
}
public Score getScore() {
System.out.println("invoke getScore");
return score;
}
public String getScoreRecord(int delta) {
System.out.println("invoke getScoreRecord");
return String.format("sno:%s,name:%s,value:%d,level:%d", sno, name, score.getValue() + delta, score.getLevel());
}
}
Student.java
package training;
public class Score {
private int value;
private int level;
public Score(int value, int level) {
this.value = value;
this.level = level;
}
public int getLevel() {
System.out.println("invoke getLevel");
return level;
}
public void setLevel(int level) {
System.out.println("invoke setLevel");
this.level = level;
}
public int getValue() {
System.out.println("invoke getValue");
return value;
}
public void setValue(int value) {
System.out.println("invoke setValue");
this.value = value;
}
}
Score.java
方法二(更加通用):用以下方式修改属性的访问权限,另外 @PrepareForTest({StudentMng.class}) 和 PowerMockito.whenNew(StudentHandler.class).withNoArguments().thenReturn(handler); 同时来达到mock new对象的目的。
Field handlerField = mng.getClass().getDeclaredField(“handler”);
handlerField.setAccessible(true);
handlerField.set(mng, handler);
1 package training;
2
3 import junit.framework.TestCase;
4 import org.junit.runner.RunWith;
5 import org.powermock.api.mockito.PowerMockito;
6 import org.powermock.core.classloader.annotations.PrepareForTest;
7 import org.powermock.modules.junit4.PowerMockRunner;
8
9 import java.lang.reflect.Field;
10 import java.util.ArrayList;
11 import java.util.List;
12
13 @RunWith(PowerMockRunner.class)
14 @PrepareForTest({StudentMng.class})
15 public class StudentMngTest extends TestCase {
16 public void test_getSpecifiedStudents() throws Exception {
17 StudentHandler handler = PowerMockito.mock(StudentHandler.class);
18 PowerMockito.when(handler.getAllStudents()).thenReturn(mockStudents());
19 PowerMockito.whenNew(StudentHandler.class).withNoArguments().thenReturn(handler);
20 StudentMng mng = new StudentMng();
21 Field handlerField = mng.getClass().getDeclaredField("handler");
22 handlerField.setAccessible(true);
23 handlerField.set(mng, handler);
24 assertEquals(1, mng.getSpecifiedStudents(10).size());
25 }
26
27 private List<Student> mockStudents() {
28 List<Student> students = new ArrayList<>();
29 Student stu = new Student();
30 stu.setScore(new Score(80, 11));
31 students.add(stu);
32 return students;
33 }
34 }
StudentMngTest.java
2.2 打桩类的public static变量或者private static变量
针对此类情况,建议对static变量赋值的部分进行mock模拟,以此来达到模拟变量值得目的。
3.测试用例执行效率简单说明
override方式模拟打桩方式的执行测试用例时间消耗
< 不带@RunWith(PowerMockRunner.class)和@PrepareForTest({StudentMng.class}) 方式的执行测试用例时间消耗
< 只带@RunWith(PowerMockRunner.class)方式的的执行测试用例时间消耗
< 都带@RunWith(PowerMockRunner.class)和@PrepareForTest({StudentMng.class}) 方式的的执行测试用例时间消耗
关于代码覆盖率:在PrepareForTest中放的类在统计代码覆盖率的时候,覆盖率是0。如果要针对该类进行代码测试覆盖,
那需要以其他的方式进行模拟测试。
关于编写产品代码:建议在编写产品代码的时候,尽量按照对象注入方式,同时减少静态方法的实现方式,
以此也能增加代码的灵活性,同时在给编写测试用例也能带来较大的便利。
更多推荐
所有评论(0)