<dependency>
<groupId>org.testnggroupId>
<artifactId>testngartifactId>
<version>6.8version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mockitogroupId>
<artifactId>mockito-allartifactId>
<version>1.10.19 version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.powermockgroupId>
<artifactId>powermock-api-mockitoartifactId>
<version>1.6.5version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.powermockgroupId>
<artifactId>powermock-module-testngartifactId>
<version>1.6.5version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.powermockgroupId>
<artifactId>powermock-coreartifactId>
<version>1.6.5version>
<scope>testscope>
dependency>
或者,在任意项目右键运行可以看到(debug as 也有可用于调试)
TestNG是一个设计用来简化广泛的测试需求的测试框架,提供从单元测试到集成测试的支持(以下来自官网介绍)
@BeforeSuite 运行suite中所有测试之前执行。
@AfterSuite 运行suite中所有测试之后执行。
@BeforeClass 运行当前类中所有测试之前执行。
@AfterClass 运行当前类中所有测试之后执行。
@BeforeTest 运行
标签内的所有测试之前运行。
@AfterTest 运行
标签内的所有测试之后运行。
@BeforeGroups 组内所有测试之前执行。
@AfterGroups 组内所有测试之后执行。
@BeforeMethod 每个测试方法之前执行。
@AfterMethod 每个测试方法之后执行。
@DataProvider 提供数据的一个测试方法。注解的方法必须返回一个Object[] []
,其中每个对象[]都可以做为测试方法的参数。
@Factory 作为一个工厂,返回TestNG的测试类的对象将被用于标记的方法。该方法必须返回Object[]
。
@Listeners 定义一个测试类的监听器。
@Parameters 介绍如何将参数传递给@Test方法。
@Test 标记一个类或方法作为测试的一部分。
Mock(模拟,不真实的)测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。
import static org.mockito.Mockito.*;
import java.util.List;
import org.testng.Assert;
import org.testng.annotations.Test;
public class SimpleMockTest {
@Test
public void testSimple(){
//创建mock对象,参数可以是类,也可以是接口
List<String> list = mock(List.class);
//设置方法的预期返回值
when(list.get(0)).thenReturn("hello mock");
String result = list.get(0);
//验证方法调用(是否调用了get(0))
verify(list).get(0);
//测试
Assert.assertEquals("hello mock", result);
}
}
Stubbing(打桩):人为介入,为Mock对象指定行为或行为发生的结果
@Test
public void testMockStubbing() {
// You can mock concrete classes, not just interfaces
LinkedList mockedList = mock(LinkedList.class);
// stubbing
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
// 连续stubbing
when(mockedList.get(0)).thenReturn("first").thenReturn("second").thenReturn("Third");
// following prints "first"
System.out.println(mockedList.get(0));
// following throws runtime exception
System.out.println(mockedList.get(1));
// following prints "null" because get(999) was not stubbed
// 默认情况下,对于所有有返回值且没有stub过的方法,mockito会返回相应的默认值。
// 对于内置类型会返回默认值,如int会返回0,布尔值返回false。对于其他type会返回null。
System.out.println(mockedList.get(999));
// Although it is possible to verify a stubbed invocation, usually it's just redundant
// If your code cares what get(0) returns, then something else breaks (often even before verify() gets
// executed).
// If your code doesn't care what get(0) returns, then it should not be stubbed. Not convinced? See here.
verify(mockedList).get(0);
// 重复stub两次,则以第二次为准。如下将返回"second":
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(0)).thenReturn("second");
// 如果是下面这种形式,则表示第一次调用时返回“first”,第二次调用时返回“second”。可以写n多个.
when(mockedList.get(0)).thenReturn("first").thenReturn("second");
// 但是,如果实际调用的次数超过了stub过的次数,则会一直返回最后一次stub的值。
// 如上例,第三次调用get(0)时,则会返回"second".
}
@Test
public void testArgumentMatcher() {
List mockedList = mock(List.class);
// stubbing using built-in anyInt() argument matcher
when(mockedList.get(anyInt())).thenReturn("element");
// stubbing using custom matcher (let's say MyMatcher() returns your own matcher implementation):
when(mockedList.contains(argThat(new MyMatcher()))).thenReturn(true);
mockedList.contains("element");
// following prints "element"
System.out.println(mockedList.get(999));
// you can also verify using an argument matcher
verify(mockedList).get(anyInt());
// argument matchers can also be written as Java 8 Lambdas
// verify(mockedList).add(someString -> someString.length() > 5);
File mock = mock(File.class); // 首先mock File类。
// 注意new AnyFiles()并不是一个matcher,需要调用argThat(new IsAnyFiles()))才返回一个matcher。
// 下句中stub:当调用renameTo方法时,返回false。该方法参数可以是任意file对象。
when(mock.renameTo(argThat(new AnyFiles()))).thenReturn(false);
mock.renameTo(new File("test"));
// 下句verify renameTo方法被调用了一次,同时输入参数是任意file。
verify(mock).renameTo(argThat(new AnyFiles()));
}
class MyMatcher extends ArgumentMatcher<Object> {
public boolean matches(Object argument) {
if (argument != null && "element".equals(argument.toString())) {
return true;
}
return false;
}
}
class AnyFiles extends ArgumentMatcher<File> {
public boolean matches(Object file) {
return file.getClass() == File.class;
}
}
其他ArgumentMatcher
除了anyInt()之外,还有很多可匹配的参数,如
详细资料
注意事项
如果在调用方法时需要传入多个参数,其中一个参数使用了argument matcher,那么所有的参数必须都是matcher。不可以matcher和实际的参数混着用
verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
//above is correct - eq() is also an argument matcher
verify(mock).someMethod(anyInt(), anyString(), "third argument");
//above is incorrect - exception will be thrown because third argument is given without an argument matcher
doThrow(), doAnswer(), doNothing(), doReturn() and doCallRealMethod()
@Test(expectedExceptions = RuntimeException.class)
public void testDoWhen() {
List mockedList = mock(List.class);
doThrow(new RuntimeException()).when(mockedList).clear();
// following throws RuntimeException:
mockedList.clear();
}
@Test(expectedExceptions = RuntimeException.class)
public void testThrowException(){
List mockedList = mock(List.class);
doThrow(new RuntimeException()).when(mockedList).clear();
//following throws RuntimeException:
mockedList.clear();
}
模拟void方法、静态方法、私有方法、final方法、屏蔽静态块运行、屏蔽类加载器加载特定类,忽略异常(PowerMockIgnore),使用构造方法Mock对象(whenNew)
package xxx;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import xxx.dao.CodeDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
public class SimpleClass {
private static final Logger LOGGER = LoggerFactory.getLogger(CustLevelServiceImpl.class);
private HttpServletRequest httpServletRequest;
@Autowired
private CodeDao codeDao;
/**
* 调用dao层处理业务
*
* @param code key
* @return code实体toString
*/
public String queryCodeString(String code) throws Exception {
// mock httpServletRequest,模拟返回
String req = httpServletRequest.getParameter(code);
// 2个分支都需要覆盖
if (req == null || req.length() == 0) {
return null;
}
// mock 带有返回值的静态方法
String configValue = PropertyUtil.getConfig("1", "1");
if ("1".equals(configValue)) {
return "0000";
}
// mock codeStringDao,模拟返回
String value = codeDao.queryCode(code, code).toString();
// mock void返回值的静态方法
try {
PropertyUtil.setConfig(code, value);
} catch (Exception e) {
throw e;
}
return value;
}
}
package xxx;
public class PropertyUtil {
/**
* 静态块
*/
static {
System.out.println("do some init");
// 阻止其运行
System.out.println(1 / 0);
}
public static String getConfig(String key, String defaultValue) {
return "1";
}
public static void setConfig(String code, String value) throws Exception {
if (code == null) {
throw new Exception();
}
}
}
package xxx;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import xxx.dao.CodeDao;
import xxxx.CodeEntity;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
import org.testng.IObjectFactory;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.ObjectFactory;
import org.testng.annotations.Test;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
@PrepareForTest(PropertyUtil.class)
@SuppressStaticInitializationFor("xxx.PropertyUtil")
@PowerMockIgnore("java.lang.*")
public class MockTest {
@InjectMocks
SimpleClass simpleClass;
@Mock
CodeDao codeDao;
@Mock
HttpServletRequest httpServletRequest;
@Mock
Properties properties;
@BeforeClass
public void beforeClass() {
MockitoAnnotations.initMocks(this);
}
@ObjectFactory
public IObjectFactory getObjectFactory() {
return new org.powermock.modules.testng.PowerMockObjectFactory();
}
@Test
public void testQueryCode() {
// mock httpServletRequest 获取参数
when(httpServletRequest.getParameter(anyString())).thenReturn(null).thenReturn("req");
// mock静态方法,有返回值
PowerMockito.mockStatic(PropertyUtil.class);
PowerMockito.when(PropertyUtil.getConfig(eq("1"), eq("1"))).thenReturn("1").thenReturn("0");
// mock dao,模拟数据库返回
when(codeDao.queryCode(anyString(), anyString())).thenReturn(new CodeEntity());
// mock静态方法,无返回值
try {
PowerMockito.doNothing()
.when(PropertyUtil.class, "setConfig", anyString(), anyString());
} catch (Exception e) {
// 忽略
}
// return null
try {
simpleClass.queryCodeString("code");
} catch (Exception e) {
}
// return "0000"
try {
simpleClass.queryCodeString("code");
} catch (Exception e) {
}
// return null
try {
simpleClass.queryCodeString("code");
} catch (Exception e) {
}
// return value
try {
simpleClass.queryCodeString("code");
} catch (Exception e) {
}
}
@Test
public void testQueryCodeStringExp() {
PowerMockito.mockStatic(PropertyUtil.class);
// mock静态方法,抛出异常
try {
PowerMockito.doThrow(new RuntimeException())
.when(PropertyUtil.class, "setConfig", anyString(), anyString());
} catch (Exception e) {
e.printStackTrace();
}
// return null runtimeException
try {
simpleClass.queryCodeString("code");
} catch (Exception e) {
}
}
}
生成单测的自动化工具
TestNG官网
Mockito官网
Power GitHub源码地址
IBM 学习资料