目录
@Mocked PersonService personService
@Captured PersonService personService
@Injectable PersonService personService
@Tested PersonService personService
使用Delegate在Expectation中定制result
Jmockit的两种Mock方式
1. 基于行为 Behavior-oriented 的测试(mock-record-replay-verify模型)
2. 基于状态 State-oriented 的测试(MockUp)
两种都十分重要,请慢慢看。
基于行为
mock-record-replay-verify模型
- mock: 声明被mock的对象
- record : 录制该mock对象将被调用的方法和返回值,它会类似aop一样将对应方法替换。
- replay:调用record中录制的被Mock的方法
- verify:行为验证
在record阶段实例化Expectations, 在verify阶段实例化Verifications。一个测试方法可以包括任意个(包括0)Expectation/Verification。
@Test
public void testMethod(Parameter p) {
//常规准备代码
//record
new Expectations(){};
//replay
//调用测试代码
//verify
new Verifications(){};
//其他验证代码
}
以下是一个简单案例,PersonService被mock了(mock阶段),其showAge方法本来应该返回10,但在Expectation中被改写为返回-1(record阶段),然后打印showAge(replay阶段)。
public class PersonService{
public int showAge(int age){
return 10;
}
}
@RunWith(JMockit.class)
public class MockTest {
// mock阶段
@Mocked
private PersonService personService;
@Test
public void testInstance(){
// record阶段
new Expectations(){
{
personService.showAge(anyInt);
result = -1; // 调用personService.showAge(anyInt) 返回-1
}
};
// replay截断
System.out.println(personService.showAge(5) == -1); // 返回true
// verify截断
}
}
如何Mock(mock阶段)
1. 如上图案例
@Mocked PersonService personService
仅针对PersonService这个类,可以创建一个假的PersonService对象,而原本showAge方法应该返回10,但由于使用了假的PersonService对象,所以会按照expectation所声明,返回-1。
注意事项
1. mocked注解可以在全局变量处声明,可以在方法参数处声明
2. 在单元测试内部自己创建一个新对象,一样会被mock
3. 单元测试中,被mocked的对象,所有方法都会被mock:在Expectations(预期结果)中显式声明的方法,会被拦截重写;没有声明的方法,被调用时也不会走原有逻辑,而是直接返回null或者基础类型默认值,若想让其走原有逻辑,应写成@Tested @Mocked PersonService personService
4. PersonService如果是个接口,其实现类为PersonServiceImpl,则无法mock其实现类此时需要求助于@Captured注解
public PersonService {
public int showAge(){
return 10;
}
public int showSex(){
return 1;
}
}
// 1. mock可以在全局变量处声明,可以在方法参数处声明
@Test
public void testMocked(@Mocked PersonService personService){
new Expectations(){
{
personService.showAge();
return = -1;
}
};
// 2. 在单元测试内部自己创建一个新对象,一样会被mock
PersonService personService2 = new PersonService();
Assert.assertTrue(personService2.showAge() == -1);
// 3. 未在expectation中声明的方法被调用,不会走原有逻辑,而是返回null或者基础类型默认值
Assert.assertTrule(personService.showSex() == 0);
}
// 4. PersonService如果是个接口,其实现类为PersonServiceImpl,则无法mock其实现类此时需要求助于@Captured注解
@Captured PersonService personService
1. 此时PersonService是个接口,Captured注解可以mock所有PersonService的实现类
2. 同样可以在全局变量处声明,可以在方法参数处声明
@Injectable PersonService personService
1. 跟Mocked注解一样无法实现接口实现类的mock
2. 同样可以在全局变量处声明,可以在方法参数处声明
3. 在单元测试内部自己创建一个新对象,不会被mock
@Test
public void testInjectable(@Injectable PersonService personService){
//自己new一个,并不受影响
PersonService another = new PersonService();
Assert.assertTrue(another.showAge == 10);
}
4. 多个同类型使用Injectable注解,personService1和personService2互不干扰
5. 可以注入非mock对象,如基础类型等,非mock对象除了使用@Injectable标记,还需要有明确初始值
public class SomeTest {
@Injectable PersonService personService1;
@Injectable PersonService personService2;
@Injectable int someIntegralProperty = 123;
@Test
public void someTestMethod(@Injectable("true") boolean flag) {
}
}
@Tested PersonService personService
Tested注解和Injectable注解是一对好基友,需要配合使用
如下图案例,PersonService内含SchoolService,而SchoolService的register方法被mock。
1. @Tested只能作用于具体类,而不能作用于接口
2. 和@Injectable配合使用,通过@Injectable注解对被测对象的构造函数进行改写。如果没有@Injectable,则采用原有代码进行初始化。
3. 如果@Tested的fullyInitialized=true,则被测对象的所有参数都需要被初始化。
@Service
public PersonService {
@Autowired
SchoolService schoolService;
public void goToSchool(){
System.out.println(schoolService.register());
}
}
@Service
public SchoolService(){
public Date register(){
return new Date();
}
}
public TestCase{
@Tested PersonService personService;
@Test
public void testGoToSchool(@Injectable SchoolService schoolService){
new Expectation(){
{
schoolService.register();
result = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2021-05-18 13:49:00");
}
};
personService.goToSchool();
}
}
如何拦截重写(record阶段)
Expection初探
PersonService被mock了,其showAge方法和getDefaultPerson方法的预期结果,在Expectation中被重写,类似于AOP的代理拦截重写返回。
所以单元测试中如果调用了personService.getAge,则不会返回原结果,而是返回expectation中重写的结果。
expectation可以返回单个结果,也可以通过returns(a,b,c...)返回多个结果,甚至可以中抛出异常。
@RunWith(JMockit.class)
public class MockTest {
@Mocked
private PersonService personService;
@Test
public void testInstance(){
new Expectations(){
{
personService.showAge(anyInt);
result = -1; // 调用personService.showAge(anyInt) 返回-1
personService.showName(anyString);
returns("onion","liyc290"); // personService.showName(anyString) 返回onion或liyc290,
// Assert.equals(personService.showName("hello"),"onion")返回true,
// Assert.equals(personService.showName("hello"),"liyc290")也返回true
personService.getDefaultPerson();
result = new RuntimeException(); // 调用personService.getDefaultPerson() 抛异常
}
};
// replay
// verify
System.out.println(personService.showAge(5) == -1); // 返回true
}
}
注意:
- 返回值可以使用returns()函数,异常值必须要使用result。
- 异常值需要在PersonService中通过try...catch...捕获,否则无法通过测试。
- 假设在StrictExpectations中录制了n个结果,在replay阶段并不强制要求调用n次该函数,调用1次即可。
拦截重写被Mock类的静态方法
@Test
public void testStaticMethod(){
new Expectations(CollectionUtils.class){{
CollectionUtils.isEmpty((Collection<?>) any);
result = true;
}};
List<Integer> list = Lists.newArrayList(1,2,3);
Assert.assertTrue(list.size() == 3);
Assert.assertTrue(CollectionUtils.isEmpty(list));
}
拦截重写被mock对象的无参方法
示例如下
@Test
public void TestMethod(@Mocked final Dependency dependency) throws Exception {
new NonStrictExpectations() {{
dependency.intReturnMethod();
returns(1, 2, 3);
}};
Dependency dependency1 = new Dependency();
assertEquals(1, dependency1.intReturnMethod());
Dependency dependency2 = new Dependency();
assertEquals(2, dependency2.intReturnMethod());
Dependency dependency3 = new Dependency();
assertEquals(3, dependency3.intReturnMethod());
}
拦截重写被mock对象的多个方法
@RunWith(JMockit.class)
public class MockTest {
//@Mocked 修饰,所有实例都会被mock
@Mocked
private PersonService personService;
@Test
public void testInstance(){
new Expectations(){
{
personService.showAge(anyInt);
result = -1;
personService.getDefaultPerson();
result = new Person("me", 4, null);
}
};
//record的方法,按照给定的结果返回
}
}
拦截重写被mock对象的方法中含有参数
在record和verify阶段进行方法匹配时,
- 对于原始类型对象,数值相同即可;
- 对于Object的子类,需要equals()返回true;
- 对于数组,需要长度相等且每个对象equals()返回true;
除此之外,如果不关心replay时的具体参数,可以使用anyXyz或者withXyz(...)方法。
在意参数是啥,主要为了验证参数有无正确传达
参照Verify章节
不在意参数是啥,只是为了mock方法的返回值
使用"any"
@Test
public void func(@Mocked UserDao userDao)
{
new Expectations() {{
userDao.say(anyString, (List<?>) any);
}};
UserService service = new UserService();
service.say("hello",new ArrayList()); // service层的say方法会调用dao层的say方法,然后dao层的say方法被expectation拦截重写
}
- 任何的基本类型都有对应的anyXyz,anyString对应任意字符串。
- any对应任意的对象,在使用时需要进行显式类型转换: (CastClass) any
- mockit.Invocations类中有可以使用所有anyXyz
- 使用时参数位置需要一致
使用"with"
any的限制太宽松,with可以选择特定的子集。
@Test
public void someTestMethod(@Mocked final DependencyAbc abc) {
final DataItem item = new DataItem(...);
new Expectations() {{
abc.voidMethod("str", (List<?>) withNotNull());
abc.stringReturningMethod(withSameInstance(item), withSubstring("xyz"));
}};
new UnitUnderTest().doSomething(item);
new Verifications() {{
abc.anotherVoidMethod(withAny(1L));
}};
}
使用"null"
null可以与任何对象匹配,好处是避免类型转换,`但是需要有一个any或者with`。
@Test
public void TestMethod(@Mocked final Dependency mock) {
new StrictExpectations() {{
//测试会失败,因为没有any或者with
mock.mockMethod(2, null);
//测试通过
mock.mockMethod(anyInt, null);
}};
mock.mockMethod(new Integer(2), "hello world");
}
如何需要的是null,则应该用 withNull() 方法。
varargs
要么使用常规的参数,要么使用any/with,不能混合使用。
拦截重写被mock对象的私有变量
public class CoderServiceTest {
@Mocked
private CoderService coderService;
@Test
public void testMockCase(){
new Expectations(coderService){{
//mock私有变量
Deencapsulation.setField(coderService, "desc", "coderDesc");
result = "noWork";
}};
// replay
// validate
}
}
拦截重写被mock对象的私有方法
//模拟实例方法,注意参数不能为null,如果要传null请使用带参数类型的另一个重载方法
Deencapsulation.invoke(Object objectWithMethod, String methodName, Object... nonNullArgs)
//模拟类方法
Deencapsulation.invoke(Class<?> classWithStaticMethod, String methodName, Object... nonNullArgs)
如何Replay
略,就是调用下方法
如何Verify
一般有这样的需求,比如验证方法参数有没有正确传达,或者方法的调用顺序,调用次数。
调用次数的限制
在record和verify阶段可以使用times,minTimes,maxTimes来限制。
默认为minTimes = 1。
显式验证
对于NonStrictExpectation,可以进行verification。对于StrictExpectation则没有必要。在new Verifications(){}中的方法至少被调用一次。
验证某个方法没被调用
times = 0
验证顺序调用
普通的new Verifications(){}没有验证其中方法的调用顺序。new VerificationsInOrder(){}用来验证(相对)顺序。
验证部分顺序
使用unverifiedInvocations()方法固定不需要验证的方法的位置。
第一种场景是验证部分方法的顺序,其余方法不需要验证:
@Test
public void TestMethod(@Mocked final Dependency mock) {
mock.mockMethod1();
mock.mockMethod2();
mock.mockMethod3();
mock.mockMethod4();
new VerificationsInOrder(){
{
// 下面的代码会失败:
// Unexpected invocation of: Dependency#mockMethod2()
// 如果两个方法相连,则其在replay中也必须直接相连
// unverifiedInvocations();
// mock.mockMethod1();
// mock.mockMethod4();
// 成功
mock.mockMethod1();
unverifiedInvocations();
mock.mockMethod4();
}
};
}
第二种场景是关心部分方法顺序,另一些方法也需要验证,但是不关心顺序。这时需要两个Verification块:
@Test
public void TestMethod(@Mocked final Dependency mock) {
mock.mockMethod1();
mock.mockMethod2();
mock.mockMethod3();
mock.mockMethod4();
new VerificationsInOrder(){{
mock.mockMethod1();
unverifiedInvocations();
mock.mockMethod4();
}};
new Verifications(){{
mock.mockMethod3();
mock.mockMethod2();
}};
}
多个verification块时,其相对顺序会引起比较诡异的事:
@Test
public void TestMethod(@Mocked final Dependency mock) {
mock.mockMethod1();
mock.mockMethod2();
mock.mockMethod3();
mock.mockMethod4();
//下面的代码会失败
//MissingInvocation: Missing invocation of:Dependency#mockMethod2()
//颠倒一下两个verification的顺序则会通过
//原因似乎是Verifications会将验证过的方法删除
new Verifications(){{
mock.mockMethod3();
mock.mockMethod2();
}};
new VerificationsInOrder(){{
mock.mockMethod1();
mock.mockMethod2();
mock.mockMethod4();
}};
}
full verification
new FullVerifications() {...}可以保证replay阶段调用的所有方法在verify代码块中都有相应的匹配,顺序可以不一致。
full verification in order
使用new FullVerificationsInOrder()
限制full verification的目标类型
默认使用full verification时,所有mock类的所有调用都必须显式验证。如果需要限定验证的类或者实例,使用FullVerifications(xxx.class)或者FullVerifications(mockObject)。
验证没有调用发生
使用空的FullVerifications(xxx.class)或者FullVerifications(mockObject)可以验证在指定类/实例上没有调用方法。但是如果Expectation中有minTimes和times的方法会被正常验证。
在verification中捕获调用参数
单次调用捕获
使用withCapture()捕获最后一次调用的参数。
java
@Test
public void capturingArgumentsFromSingleInvocation(@Mocked final Collaborator mock)
{
new Collaborator().doSomething(0.5, new int[2], "test");
new Verifications() {{
double d;
String s;
mock.doSomething(d = withCapture(), null, s = withCapture());
assertTrue(d > 0.0);
assertTrue(s.length() > 1);
}};
}
多次调用捕获
使用`withCapture(List)`捕获所有参数。
@Test
public void capturingArgumentsFromMultipleInvocations(@Mocked final Collaborator mock)
{
mock.doSomething(dataObject1);
mock.doSomething(dataObject2);
new Verifications() {{
List<DataObject> dataObjects = new ArrayList<>();
mock.doSomething(withCapture(dataObjects));
assertEquals(2, dataObjects.size());
DataObject data1 = dataObjects.get(0);
DataObject data2 = dataObjects.get(1);
// Perform arbitrary assertions on data1 and data2.
}};
}
捕获新实例
使用withCapture(new XX())
@Test
public void capturingNewInstances(@Mocked Person mockedPerson) {
new Person("Paul", 10);
new Person("Mary", 15);
new Person("Joe", 20);
new Verifications() {{
List<Person> personsInstantiated = withCapture(new Person(anyString, anyInt));
}};
}
使用Delegate在Expectation中定制result
使用场景:在Expectation中需要根据replay时的参数值决定返回值。
原理:JMockit拦截调用,转交给Delegate处理。
@Test
public void delegatingInvocationsToACustomDelegate(@Mocked final DependencyAbc anyAbc){
new Expectations() {{
anyAbc.intReturningMethod(anyInt, null);
result = new Delegate() {
int aDelegateMethod(int i, String s)
{
return i == 1 ? i : s.length();
}
};
}};
// Calls to "intReturningMethod(int, String)" will execute the delegate method above.
new UnitUnderTest().doSomething();
}
- delegate方法的参数应该与原始方法一致,返回值需要兼容或者为异常。
- 可以delegate构造器,这时返回值设置为空。
- delegate参数中可以有一个 Invocation对象,从而获得调用者的引用。
级联mock
出现 obj1.getObj2(...).getYetAnotherObj().doSomething(...)时可能需要mock多个对象。对于一个mock对象:
- Expectation中进行了record,则会返回record的result;
- 如果没有record,JMockit会自动创建一个返回被注解@Injectable的子对象
public class Dependency {
public CascadeDependency getCascadeDependency() {
//JMockit会拦截这个方法,返回一个非null对象
return null;
}
public CascadeDependency getAnotherCascadeDependency() {
//JMockit会拦截这个方法,返回一个非null对象
return null;
}
public String getString() {
//仍旧返回null
return null;
}
public Object getObject() {
//仍旧返回null
return null;
}
public List<Object> getList() {
//返回empty集合
return null;
}
}
@Test
public void TestMethod(@Mocked Dependency dependency) {
CascadeDependency first = dependency.getCascadeDependency();
CascadeDependency second = dependency.getCascadeDependency();
//调用另一个方法
CascadeDependency third = dependency.getAnotherCascadeDependency();
//所有都不会为null
assertNotNull(first);
assertNotNull(second);
assertNotNull(third);
//相同方法返回JMockit创建的同一个对象
assertSame(first, second);
//不同方法返回JMockit创建的同一个对象
assertNotSame(first, third);
//String返回null
assertNull(dependency.getString());
//Object返回null
assertNull(dependency.getObject());
//返回empty集合
assertNotNull(dependency.getList());
assertEquals(0, dependency.getList().size());
}
@Test
public void TestMethod(@Mocked Dependency dependency,
@Mocked CascadeDependency cascadeDependency) {
CascadeDependency first = dependency.getCascadeDependency();
CascadeDependency second = dependency.getAnotherCascadeDependency();
//因为子对象也@Mocked,所以会返回同一个对象
assertSame(first, second);
}
JMockit返回的非空对象实际上进行了@Injectable标识,所以:
@Test
public void TestMethod(@Mocked Dependency dependency) {
//虽然CascadeDependency没有出现在参数中,
//但是JMockit对其进行了@Injectable
//而由于没有在Expectation中record mockMethod的result,所以返回空
assertNull(dependency.getCascadeDependency().mockMethod());
//不影响CascadeDependency的其他实例
assertNotNull(new CascadeDependency().mockMethod());
}
也可以在Expectation中使用result指定返回对象,从而禁止JMockit自动生成。
@Test
public void TestMethod(@Mocked final Dependency dependency) {
//在Expectation中指定了返回结果,因此JMockit不会生成CascadeDependency
new NonStrictExpectations(){{
dependency.getCascadeDependency();
result = null;
result = new CascadeDependency();
}};
//第一次返回null
assertNull(dependency.getCascadeDependency());
//第二次返回新对象
assertNotNull(dependency.getCascadeDependency().mockMethod());
}
mock级联调用特别适合static factory,getCurrentInstance()永远不会返回null。
@Test
public void TestMethod(@Mocked final Dependency dependency) {
assertSame(dependency, dependency.getCurrentInstance());
}
在Builder模式中也很方便验证,
@Test
public void createOSProcessToCopyTempFiles(@Mocked final ProcessBuilder pb) throws Exception{
Process copy = new ProcessBuilder().command(cmdLine).directory(wrkDir).inheritIO().start();
new Verifications() {{ pb.command(withSubstring("copy")).start(); }};
}
部分mock
有时候只需要mock部分方法,这时候可以用new Expectations(object),object可以是实例,也可以是class对象。在replay阶段,如果在Expectation中没有进行record,则会调用原有代码。
@Test
public void partiallyMockingASingleInstance() {
final Collaborator collaborator = new Collaborator(2);
new Expectations(collaborator) {{
collaborator.getValue(); result = 123;
// 静态方法也可以
Collaborator.doSomething(anyBoolean, "test");
}};
// Mocked:
assertEquals(123, collaborator.getValue());
Collaborator.doSomething(true, "test");
// Not mocked:
assertEquals(45, new Collaborator(45).getValue());
}
- Note:上面的代码中没有出现@Mocked注解
没有record的方法也可以verify,
@Test
public void partiallyMockingA() {
final Collaborator collaborator = new Collaborator(123);
new Expectations(collaborator) {};
int value = collaborator.getValue();
collaborator.simpleOperation(45, "testing", new Date());
// 没有record也可以verify
new Verifications() {{
c1.simpleOperation(anyInt, anyString, (Date) any);
}};
}
另一种实现部分mock的方法:同时标注@Tested和@Mocked。
mock接口
有些实现类是匿名的:
public interface Service { int doSomething(); }
public final class TestedUnit {
private final Service service = new Service() {
public int doSomething() { return 2; }
};
public int businessOperation() {
return service.doSomething();
}
}
使用@Capturing标注基类/接口,所有实现类会被mock:
@Capturing Service anyService;
@Test
public void mockingImplementationClassesFromAGivenBaseType() {
new Expectations() {{
anyService.doSomething();
returns(3);
}};
int result = new TestedUnit().businessOperation();
assertEquals(3, result);
}
@Capturing是@Mock的增强版,有一个可选参数maxInstances用于捕获前面指定数量的对象,其默认值为Integer.MAX_VALUE。
@Test
public void TestMethod(@Capturing(maxInstances = 2) final Dependency dependency1,
@Capturing(maxInstances = 2) final Dependency dependency2,
@Capturing final Dependency remain) {
new NonStrictExpectations() {{
dependency1.getValue();
result = 1;
dependency2.getValue();
result = 2;
remain.getValue();
result = 3;
}};
assertEquals(1, new Dependency().getValue());
assertEquals(1, new Dependency().getValue());
assertEquals(2, new Dependency().getValue());
assertEquals(2, new Dependency().getValue());
assertEquals(3, new Dependency().getValue());
}
上面的@Capturing是出现在参数列表中的,如果是作为field声明的,maxInstances会失效,@Capturing退化为@Mock。
自动注入被测试类
用@Tested标注被测试类,在运行测试方法时,如果该实例仍然为null,JMockit会自动组装相关mock对象,进行初始化。在组装被测试类过程中,相关mock对象必须使用@Injectable标记,非mock对象除了使用@Injectable标记,还需要有明确初始值。
public class SomeTest {
@Tested CodeUnderTest tested;
@Injectable Dependency dep1;
@Injectable AnotherDependency dep2;
@Injectable int someIntegralProperty = 123;
@Test
public void someTestMethod(@Injectable("true") boolean flag) {
tested.exerciseCodeUnderTest();
}
}
注入先根据类型匹配,再根据参数名称匹配。
----------------------------------------------------------MockUp API------------------------------------------------
MockUp
前面介绍的是基于行为的测试,利用的是Expectation API,接下来介绍基于状态的测试,也就是mock-up API。
mock-up类是继承mockit.MockUp<T>的类,mock方法是@Mock的方法,T是被mock的类。例如:
public class FakeClass extends MockUp<Dependency> {
@Mock
//mock构造器
public void $init(String name) {
assertNotNull(name);
}
@Mock
public void mockMethod() {}
}
直接用匿名内部类MockUp
进行测试时,可以在@BeforeClass,@Before,@Test方法中使用new来初始化mock-up类,@Mock方法就会代替真实方法。如果被mock的类中没有相应的方法,就会抛出异常。没有被@Mock注解的方法会执行原来的方法。
为了方便,可以直接初始化匿名内部类:
@Test
public void testMethod() throws Exception() {
new MockUp<Dependency>() {};
//CodeUnderTest;
}
伪造接口实现类
直接初始化MockUp类就可以完成打桩,如果希望伪造某个接口的实现类,可以使用getInstance()方法:
@Test
public void faking() throws Exception {
CallbackHandler callbackHandler = new MockUp<CallbackHandler>() {
@Mock
void handle() {
//fake implementation
}
}.getMockInstance();
canllbackHandler.handle();
}
假设只知道某个接口类型,而不知道其实现类的具体类型(比如实现类是匿名类,或者根本未知),这时候使用泛型通配符可以为这些类设定返回类型:
@Test
public <T extends Service> void faking() {
new MockUp<T>() {
@Mock int doSomething() { return 7; }
};
//业务代码中所有 Service类的子类都返回7
}
伪造类初始化过程
如果某个类在需要在static块中完成一些初始化工作,而在测试时希望忽略掉这些初始化,就需要使用$clinit方法来伪造类初始化过程。
public class Dependency {
//static赋值会被忽略
public static String staticfield = "staticField will not be initialized";
//final可以赋值
public static final String finalfield = "finalField will be initialized";
//static语句块不会执行
static {
staticField = "staticField will not be initialized";
}
}
@Testpublic void TestMethod() {
new MockUp<Dependency>() {
@Mock
void $clinit(){}
};
assertNull(new Dependency().staticField);
assertNull(new Dependency().finalField);
}
调用者的上下文
在Fake类的方法中,可以设置一个Invocation类对象,在该方法被调用时,JMockit会为这个对象传值。这种机制的意义是Fake类可以接触到真实类。因此,Fake类的方法可以得到调用参数,调用次数等信息。
@Test
public void invocation() throws Exception {
final Subject testSubject = new Subject();
new MockUp<LoginContext>() {
@Mock
void $init(Invocation invocation, String name, Subject subject)
{
assertNotNull(name);
assertSame(testSubject, subject);
// Gets the invoked instance.
LoginContext loginContext = invocation.getInvokedInstance();
// Verifies that this is the first invocation.
assertEquals(1, invocation.getInvocationCount());
}
@Mock
void login(Invocation invocation)
{
// Gets the invoked instance.
LoginContext loginContext = invocation.getInvokedInstance();
// getSubject() returns null until the subject is authenticated.
assertNull(loginContext.getSubject());
}
@Mock
void logout(Invocation invocation)
{
// Gets the invoked instance.
LoginContext loginContext = invocation.getInvokedInstance();
assertSame(testSubject, loginContext.getSubject());
}
};
LoginContext theFakedInstance = new LoginContext("test", testSubject);
theFakedInstance.login();
theFakedInstance.logout();
}
执行原有代码
这个功能和装饰器或者拦截器相似,JMockit拦截调用,交给fake类,fake类执行某些功能之后再回调原来的代码。
@Mock
void login(Invocation inv) {
inv.proceed();
}
多个类公用MockUp
@RunWith(Suite.class)
@Suite.SuiteClasses({MyFirstTest.class, MySecondTest.class})
public final class TestSuite{
@BeforeClass
public static void applyGlobalMockUps() {
new LoggingMocks();
new MockUp<SomeClass>() {
@Mock
someMethod() {}
};
}
}
伪造所有方法
@Mock
public Object $advice(Invocation invocation) {
invocation.proceed();
}
使用$advice()方法,使得对所有方法的调用都执行该方法。
Jmockit Coverage
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit-coverage</artifactId>
<version>1.13</version>
<scope>test</scope>
</dependency>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.21.0</version>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
<argLine>
-javaagent:${settings.localRepository}/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar=coverage
</argLine>
<systemPropertyVariables>
<disableXmlReport>true</disableXmlReport>
<coverage-output>html</coverage-output>
<coverage-metrics>all</coverage-metrics>
<coverage-classes>loaded</coverage-classes>
<coverage-srcDirs>src</coverage-srcDirs>
<coverage-check>80</coverage-check>
<coverage-outputDir>${project.build.directory}/codecoverage-output</coverage-outputDir>
</systemPropertyVariables>
</configuration>
</plugin>