All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy