在一个会话中记录方法调用,以便在将来的测试会话中重放?

Tho*_*sen 40 java

我有一个后端系统,我们使用第三方Java API从我们自己的应用程序访问.我可以像普通用户一样以其他用户的身份访问系统,但我没有虔诚的权力.

因此,为了简化测试,我想运行一个真实的会话并记录API调用,并保留它们(最好是可编辑的代码),这样我们以后可以通过API调用进行干测试运行,只需从记录会话中返回相应的响应 - 这是重要的部分 - 无需与上述后端系统对话.

因此,如果我的应用程序包含表单上的行:

 Object b = callBackend(a);
Run Code Online (Sandbox Code Playgroud)

我希望框架首先捕获callBackend()返回的b给定参数a,然后当我在任何以后执行干运行时说"嘿,给定此调用应该返回b".a和b的值将相同(如果不是,我们将重新运行录制步骤).

我可以覆盖提供API的类,因此所有捕获的方法调用都将通过我的代码(即不需要字节代码检测来改变我控制之外的类的行为).

我应该研究什么框架才能做到这一点?


编辑:请注意,赏金猎人应提供实际代码,以证明我寻找的行为.

Saz*_*man 7

实际上,您可以使用代理模式构建此类框架或模板.在这里我解释一下,如何使用动态代理模式来完成它.这个想法是,

  1. 编写代理管理器以获取API的记录器和重放代理!
  2. 写一个包装类来存储您收集到的信息,同时也可hashCodeequals从有效的查找该包装类的方法,Map如数据结构.
  3. 最后使用录像机代理记录和重放代理以进行重放.

录音机如何工作:

  1. 调用真正的API
  2. 收集调用信息
  3. 将数据保留在预期的持久性上下文中

如何重播工作:

  1. 收集方法信息(方法名称,参数)
  2. 如果收集的信息与先前记录的信息匹配,则返回先前收集的返回值.
  3. 如果返回值不匹配,则保留收集的信息(如您所愿).

现在,让我们看一下实现.如果您的API MyApi如下:

public interface MyApi {
    public String getMySpouse(String myName);
    public int getMyAge(String myName);
    ...
}
Run Code Online (Sandbox Code Playgroud)

现在我们将记录并重放调用public String getMySpouse(String myName).为此,我们可以使用类来存储调用信息,如下:

    public class RecordedInformation {
       private String methodName;
       private Object[] args;
       private Object returnValue;

        public String getMethodName() {
            return methodName;
        }

        public void setMethodName(String methodName) {
            this.methodName = methodName;
        }

        public Object[] getArgs() {
            return args;
        }

        public void setArgs(Object[] args) {
            this.args = args;
        }

        public Object getReturnValue() {
            return returnType;
        }

        public void setReturnValue(Object returnValue) {
            this.returnValue = returnValue;
        }

        @Override
        public int hashCode() {
            return super.hashCode();  //change your implementation as you like!
        }

        @Override
        public boolean equals(Object obj) {
            return super.equals(obj);    //change your implementation as you like!
        }
    }
Run Code Online (Sandbox Code Playgroud)

现在主要部分是The RecordReplyManager.这RecordReplyManager为您提供了API的代理对象,具体取决于您是否需要录制或重播.

    public class RecordReplyManager implements java.lang.reflect.InvocationHandler {

        private Object objOfApi;
        private boolean isForRecording;

        public static Object newInstance(Object obj, boolean isForRecording) {

            return java.lang.reflect.Proxy.newProxyInstance(
                    obj.getClass().getClassLoader(),
                    obj.getClass().getInterfaces(),
                    new RecordReplyManager(obj, isForRecording));
        }

        private RecordReplyManager(Object obj, boolean isForRecording) {
            this.objOfApi = obj;
            this.isForRecording = isForRecording;
        }


        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result;
            if (isForRecording) {
                try {
                    System.out.println("recording...");
                    System.out.println("method name: " + method.getName());
                    System.out.print("method arguments:");
                    for (Object arg : args) {
                        System.out.print(" " + arg);
                    }
                    System.out.println();
                    result = method.invoke(objOfApi, args);
                    System.out.println("result: " + result);
                    RecordedInformation recordedInformation = new RecordedInformation();
                    recordedInformation.setMethodName(method.getName());
                    recordedInformation.setArgs(args);
                    recordedInformation.setReturnValue(result);
                    //persist your information

                } catch (InvocationTargetException e) {
                    throw e.getTargetException();
                } catch (Exception e) {
                    throw new RuntimeException("unexpected invocation exception: " +
                            e.getMessage());
                } finally {
                    // do nothing
                }
                return result;
            } else {
                try {
                    System.out.println("replying...");
                    System.out.println("method name: " + method.getName());
                    System.out.print("method arguments:");
                    for (Object arg : args) {
                        System.out.print(" " + arg);
                    }

                    RecordedInformation recordedInformation = new RecordedInformation();
                    recordedInformation.setMethodName(method.getName());
                    recordedInformation.setArgs(args);

                    //if your invocation information (this RecordedInformation) is found in the previously collected map, then return the returnValue from that RecordedInformation.
                    //if corresponding RecordedInformation does not exists then invoke the real method (like in recording step) and wrap the collected information into RecordedInformation and persist it as you like!

                } catch (InvocationTargetException e) {
                    throw e.getTargetException();
                } catch (Exception e) {
                    throw new RuntimeException("unexpected invocation exception: " +
                            e.getMessage());
                } finally {
                    // do nothing
                }
                return result;
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

如果你想记录方法调用,你只需要获得一个像下面这样的API代理:

    MyApi realApi = new RealApi(); // using new or whatever way get your service implementation (API implementation)
    MyApi myApiWithRecorder = (MyApi) RecordReplyManager.newInstance(realApi, true); // true for recording
    myApiWithRecorder.getMySpouse("richard"); // to record getMySpouse
    myApiWithRecorder.getMyAge("parker"); // to record getMyAge
    ...
Run Code Online (Sandbox Code Playgroud)

并重播你需要的一切:

    MyApi realApi = new RealApi(); // using new or whatever way get your service implementation (API implementation)
    MyApi myApiWithReplayer = (MyApi) RecordReplyManager.newInstance(realApi, false); // false for replaying
    myApiWithReplayer.getMySpouse("richard"); // to replay getMySpouse
    myApiWithRecorder.getMyAge("parker"); // to replay getMyAge
    ...
Run Code Online (Sandbox Code Playgroud)

你完成了!

编辑: 录音机和重播器的基本步骤可以用上面提到的方式完成.现在它取决于您,您希望如何使用或执行这些步骤.您可以在录音机和重放代码块中随心所欲地执行任何操作,只需选择您的实现!


Aqu*_*wer -3

如果我正确理解你的问题,你应该尝试 db4o。

您将使用 db4o 存储对象并稍后恢复到模拟和 JUnit 测试。