【问题标题】:Conditionally simulating exceptions/return values when testing Axis 1.4 SOAP calls在测试 Axis 1.4 SOAP 调用时有条件地模拟异常/返回值
【发布时间】:2011-09-07 10:41:29
【问题描述】:

为这个冗长的问题道歉......

为了测试在我的单元测试中调用 Axis 1.4 Web 服务的业务逻辑,我目前正在使用 Spring 代理,它允许我设置异常并模拟返回值,如下所示。

但是,我想知道是否有更清洁的方法:

在我的 JUnit 应用程序上下文中,我提出以下建议:

<bean id="testProxy" class="appl.service.TestProxyImpl" />
<bean id="testAdvice" class="org.springframework.aop.support.DefaultIntroductionAdvisor">
    <constructor-arg index="0" ref="testProxy"/>
    <constructor-arg index="1" value="appl.service.ITestProxy"/>
</bean>

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames" value="myAxisProxy"/>
    <property name="interceptorNames" value="testAdvice"/>
</bean>

TestProxyImpl类如下(需要看接口的告诉我):

public class TestProxyImpl 
    extends DelegatingIntroductionInterceptor 
    implements ITestProxy {

    private Map<String, Integer> calls = new HashMap<String, Integer>();
    private Map<String, Throwable[]> exceptions = new HashMap<String, Throwable[]>();
    private Map<String, Object> returnValues = new HashMap<String, Object>();
    private Map<String, Object[]> lastParams = new HashMap<String, Object[]>();

    public int getCalls(String methodName) {
        Integer noCalls = calls.get(methodName);
        if (noCalls == null) {
            return 0;
        }
        return noCalls;
    }

    public void resetCalls() {
        calls.clear();
        returnValues.clear();
        exceptions.clear();
        lastParams.clear();
    }

    public void addExceptions(String method, Throwable... exceptions) {
        this.exceptions.put(method, exceptions);
    }

    public void addReturnValue(String method, Object result) {
        returnValues.put(method, result);
    } 

    @SuppressWarnings("unchecked")
    public <T> T getLastParameter(String method, Class<? extends T> paramClass) {
        Object[] args = lastParams.get(method);
        if (args != null) {
            for (Object arg : args) {
                if (arg != null) {
                    if (paramClass.isAssignableFrom(arg.getClass())) {
                        return (T)arg;
                    }
                }
            }
        }
        return null;
    }

    @Override
    protected Object doProceed(MethodInvocation mi) throws Throwable {
        String methodName = mi.getMethod().getName();
        int noCalls;
        synchronized (calls) {
            noCalls = getCalls(methodName);
            calls.put(methodName, noCalls + 1);
        }
        Object[] args = mi.getArguments();
        synchronized (lastParams) {
            lastParams.put(methodName, args);
        }
        if (exceptions.containsKey(methodName)) {
            Throwable[] exceptionArray = exceptions.get(methodName);
            if (exceptionArray != null) {
                Throwable e = exceptionArray[noCalls % exceptionArray.length];
                if (e != null) {
                    throw e;
                }
            }
        }
        if (returnValues.containsKey(methodName)) {
            return returnValues.get(methodName);
        }
        return super.doProceed(mi);
    }
}

这允许我使用以下单元测试:

protected void onSetUp() throws Exception {
    super.onSetUp();

    // testProxy is autowired into the unit test class
    testProxy.resetCalls();
}

public void testSuccess() throws Exception {

    // myBusinessObject is autowired into the unit test class
    // myAxisProxy is injected into myBusinessObject in the spring context
    // myBusinessObject calls myAxisProxy.myMethod(MyRequestObject) internally
    myBusinessObject.doSomething();

    // check that myAxisProxy has been called the correct times
    // with the correct parameters
    assertEquals(1, testProxy.getCalls("myMethod"));
    assertEquals(
        "foo", 
        testProxy.getLastParameter(
            "myMethod", 
             MyRequestObject.class).getMyField());
 }

 public void testWithoutCallingProxy() throws Exception {

     testProxy.addReturnValue("myMethod", createTestReturnValues());

     myBusinessObject.doSomething();

     // check that the response is as expected
 }

 public void testWithException() throws Exception {
     testProxy.addException(
         "myMethod", 
         AxisFault.makeFault(new ConnectException("test")),
         null);

     // this would check that the first call results in a ConnectException
     // simulating a network error, but myBusinessObject retries
     // and goes through to the service on the second call.
     myBusinessObject.doSomething();
 }

我已经简要研究了 EasyMock,但不确定如何创建一个模拟对象来拦截调用并在某些时候返回异常或指定的返回值,但在其他时候使用正确的调用次,所以想知道是否有人对如何简化这一点有想法。

注意,理想情况下,我希望避免重新连接myBusinessObject 的属性。

谢谢。

编辑:

我正在使用 Spring 2.5、JUnit 3.8 和 Axis 1.4

EDIT2:

我正在使用这种方法来自动化集成测试,而不是简单的单元测试,所以我非常欢迎关于如何用基于库的东西替换手工解决方案的任何建议。

【问题讨论】:

    标签: java spring exception junit axis


    【解决方案1】:

    典型的分层开发应该像下面这样组织:

    数据访问

    业务逻辑

    服务接口

    这再次提醒您,在开发可重复的独立单元测试时,您的测试应仅针对其预期层和组件中的逻辑和功能。因此,在测试业务逻辑时,我根本不想进行实际的服务调用,我什至也不想进行数据访问调用,这就是我们使用像 EasyMock 这样的 Mocking 框架的原因。

    如果您不喜欢 EasyMock,请尝试查看 Mockito。它还有一些功能可以处理不太适合模拟的代码。

    而且我觉得很奇怪,你会为了避免重新布线而费这么多麻烦。重构工作非常值得。

    【讨论】:

    • 我同意分层开发的方法(我也有一些独立的测试)。不重新布线方法的主要原因是自动化端到端测试,因为我已经被多次不做这样的测试所困扰(由于不同的团队在不同的组件上工作)。并且由于“myBusinessObject”包含许多不同的“myAxisProxy”对象,这种方法可以更轻松地测试许多不同的场景(例如,myAxisProxy1 失败但 myAxisProxy 可以,等等),而无需重做“myBusinessObject”的接线。这有意义吗?
    • @beny23,完美的感觉,你需要端到端(集成)测试,但你也需要单元测试。如果您碰巧在其他团队的组件一直损坏的情况下工作,那么当集成测试告诉您它损坏时,单元测试会告诉您您的组件很好!它们是您打印组件的测试运行并证明您的代码没有问题的重要手段。这都是关于 CYA 的。您在集成测试中增加了相当大的复杂性,似乎是因为您被其他人的错误“咬伤”了?
    • 我喜欢这种方法并且它是有道理的,但是就像我之前所说的那样,当集成测试应该测试所有内容都正确部署到端到端的特定环境。你明白其中的区别吗?就好像其他组件开发人员没有做足够的测试他们的组件的工作,所以你基本上是在为他们测试他们的功能,只是因为你受到了指责。这是一种反模式,更高级别的组件消费者会为问题承担责任。
    • 呵呵,告诉我吧,我们团队的一个笑话是,如果互联网坏了,人们会指责我们的组件。但说真的,我们有时会遇到测试资源稀缺并且不同团队“忘记”测试整个应用程序的情况,当业务逻辑因有人在回归测试中走捷径而中断时,我们会收到支持请求。所以,我将看看我是否可以使用 EasyMock 和 Mockito 来代替我手工制作的解决方案。谢谢。
    • we've sometimes got situations where testing resources are scarce and different teams "forget" to test the whole applications 代码的单元测试是一项开发任务,而不是 QA 任务。这不是“测试资源”的问题,而是“开发人员的懒惰”和/或“管理层给出不可能的最后期限”的问题。 because somebody took shortcuts with the regression testing. 如果他们编写了正确的单元测试,那么回归测试就像点击鼠标一样简单。但我离题了。你不是一个人相信我。继续打好仗。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-12
    • 1970-01-01
    • 1970-01-01
    • 2015-12-23
    • 1970-01-01
    相关资源
    最近更新 更多