Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.test4j.mock.faking.fluent.FluentMockUp Maven / Gradle / Ivy
package org.test4j.mock.faking.fluent;
import org.test4j.Context;
import org.test4j.mock.Invocation;
import org.test4j.mock.MockUp;
import org.test4j.mock.faking.meta.FakeMethod;
import org.test4j.mock.faking.meta.FakeMethods;
import org.test4j.mock.faking.meta.FakeStates;
import org.test4j.mock.faking.meta.MethodId;
import org.test4j.mock.startup.Startup;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import static org.test4j.mock.faking.fluent.MockMethod.INDEX_REST_BEHAVIOR;
import static org.test4j.mock.faking.util.ObjectIdentify.identities;
import static org.test4j.mock.faking.util.ReflectUtility.doThrow;
/**
* 用来构造fluent mock up的基类
*
* @param
* @author darui.wu
*/
@SuppressWarnings({"rawtypes"})
public class FluentMockUp extends MockUp {
private final Map methods = new HashMap<>();
/**
* key: methodName+parasDescription
* value: (key:index of call, value:behavior)
*/
protected final MethodBehaviors behaviors;
/**
* 是否已经在 {@link FakeStates} 中注册 {@link #fakeMethods}
*/
private boolean registered = false;
/**
* 注册到 {@link FakeStates} 中的mock方法列表, 对应 {@link #behaviors} 的行为
*/
private final FakeMethods fakeMethods = new FakeMethods(fakedSeqNo);
public FluentMockUp() {
this.behaviors = new MethodBehaviors(super.declaredToFake);
}
public FluentMockUp(Class declaredToFake) {
super(declaredToFake);
this.behaviors = new MethodBehaviors(super.declaredToFake);
}
public FluentMockUp(String fullClassName, Object[] objects) {
super(fullClassName, (Object[]) identities(objects));
this.behaviors = new MethodBehaviors(super.declaredToFake);
}
public FluentMockUp(Class declaredClass, MethodBehaviors behaviors, Set fakedHashCodes) {
super(declaredClass, fakedHashCodes);
assert behaviors != null : "behaviors can't be null.";
this.behaviors = behaviors;
}
public void put(String methodName, MockMethod mockMethod) {
this.methods.put(methodName, mockMethod);
}
/**
* 去掉基类的默认实现
*/
protected final void initFakeMethods() {
}
protected void init() {
}
/**
* fluent mock行为定义只能发生在测试类执行期间
* 不能发生在static代码块和BeforeAll方法类中
*/
public void apply() {
if (Context.currTestMethod() == null) {
throw new IllegalStateException("Behaviors of FluentMockUp can only be applied in test methods.");
} else if (Startup.initializing) {
throw new IllegalStateException("Behaviors of FluentMockUp can't be applied in global mock, please use new MockUp() style.");
}
if (!registered) {
this.init();
FakeStates.register(fakeMethods);
registered = true;
}
}
/**
* 清除mock行为
*/
public void clear() {
for (String method : this.behaviors.keySet()) {
MockMethod mockMethod = this.methods.get(method);
if (mockMethod != null) {
mockMethod.resetIndex();
}
}
this.behaviors.clear();
this.registered = false;
}
/**
* mock方法第index次调用
*/
public void mockMethod(String realClass, String name, String desc, int index, FakeFunction fake) {
this.addMockBehavior(realClass, name, desc, index, fake);
}
public void mockReturn(String realClass, String name, String desc, int index, Object result) {
this.addMockBehavior(realClass, name, desc, index, FakeResult.value(result));
}
public void mockThrows(String realClass, String name, String desc, int index, Throwable e) {
this.addMockBehavior(realClass, name, desc, index, FakeResult.eject(e));
}
private void addMockBehavior(String realClass, String name, String desc, int index, MockBehavior behavior) {
MethodId meta = this.behaviors.addMockBehavior(realClass, name, desc, index, behavior);
fakeMethods.add(new FakeMethod(this, meta));
}
/**
* 根据预定义的行为, 执行mock方法
*
* @param invocation mock调用代理
* @param methodDesc 调用方法描述
* @param args 方法实际入参
* @return mock执行结果
*/
public Object invoke(Invocation invocation, String methodDesc, Object[] args) {
int index = invocation.getInvokedTimes();
this.assertBefore(methodDesc, invocation);
MockBehavior behavior = this.getBehavior(index, methodDesc);
if (behavior == null) {
Object result = invocation.proceed(args);
return this.assertAfter(methodDesc, invocation, result);
} else if (behavior instanceof FakeFunction) {
return ((FakeFunction) behavior).doFake(invocation);
}
FakeResult result = (FakeResult) behavior;
if (result.isThrowable()) {
return doThrow((Throwable) result.getResult());
} else {
return this.assertAfter(methodDesc, invocation, result.getResult());
}
}
private final Map> parasAsserts = new HashMap<>();
private final Map> resultAsserts = new HashMap<>();
void addAssertMethodParas(String methodDesc, Consumer asserts) {
this.parasAsserts.put(methodDesc, asserts);
}
void addAssertMethodResult(String methodDesc, BiConsumer asserts) {
this.resultAsserts.put(methodDesc, asserts);
}
/**
* 断言方法入参
*
* @param methodDesc method签名
* @param invocation Invocation
*/
private void assertBefore(String methodDesc, Invocation invocation) {
Consumer asserts = parasAsserts.get(methodDesc);
if (asserts != null) {
asserts.accept(invocation);
}
}
/**
* 断言方法结果值
*
* @param methodDesc method签名
* @param invocation Invocation
* @param result ignore
*/
private Object assertAfter(String methodDesc, Invocation invocation, Object result) {
BiConsumer asserts = this.resultAsserts.get(methodDesc);
if (asserts != null) {
asserts.accept(invocation, result);
}
return result;
}
/**
* 根据方法执行序号返回预定义行为
*
* @param index 方法实际执行序号(从1开始)
* @param methodDesc 方法描述
* @return ignore
*/
private MockBehavior getBehavior(int index, String methodDesc) {
Map methods = this.behaviors.get(methodDesc);
if (methods == null || methods.isEmpty()) {
return null;
}
MockBehavior behavior = methods.get(index);
if (behavior == null) {
behavior = methods.get(INDEX_REST_BEHAVIOR);
}
return behavior;
}
/**
* 如果realClass不为空, 返回realClass
* 否则, 返回接口class
*
* @param realClass real class
* @return ignore
*/
public String getFakeClass(String realClass) {
if (realClass == null) {
return this.declaredToFake.getName().replace('.', '/');
} else {
return realClass;
}
}
}