【转帖】jMock Argument Interceptor - 一种独特的设计思路

jMock Argument Interceptor - 一种独特的设计思路
Motivation
An Object you need to test is constructing another complex object internally which you cannot access and this object is passed to a collaborator you can replace with a mock.
Solution
Write an Interceptor (a custom jMock Stub ) to intercept the argument passed to the mocked method. The Interceptor exposes the argument, allowing for standard jUnit assertions.
Alternatives

    * Use a custom jMock Constraint when the argument's asserted state is simple.
    * Use a Fake Object when a class cannot be dynamically mocked.

Example

public void testInterceptArgument() {

    List arguments = new ArrayList();

    Mock mockSubmissionTracker = mock(SubmissionTracker.class);
    mockSubmissionTracker.expects(once()).method("record").will(captureArgumentsIn(arguments));

    SecretLottery lotto = new SecretLottery( (SubmissionTracker)mockSubmissionTracker.proxy());

    lotto.createTicket();

    LotteryTicket ticket = (LotteryTicket) arguments.get(0);
    assertEquals("Secret Number", 12345, ticket.number);
}

private Stub captureArgumentsIn(List argumentList) {

    return new ArgumentInterceptor(argumentList);

}

class ArgumentInterceptor implements Stub {

    List arguments;

    public ArgumentInterceptor(List argumentList) {
        arguments = argumentList;
    }

    public Object invoke(Invocation invocation) throws Throwable {
        arguments.addAll(invocation.parameterValues);
        return null;
    }

    public StringBuffer describeTo(StringBuffer buffer) {
        return buffer;
    }
}

The Traditional jMock approach
The traditional way to handle this with jMock is to create a custom Constraint to verify the argument passed into the mocked method. Unfortunatly, this technique can produce a less explict test and a lot of supporting code. The above example handled with a traditional jMock Constraint would look like the following...

public void testInterceptArgument() {

    Mock mockSubmissionTracker = mock(SubmissionTracker.class);
    mockSubmissionTracker.expects(once()).method("record").with(ticketNumber(12345));

    SecretLottery lotto = new SecretLottery( (SubmissionTracker) mockSubmissionTracker.proxy());

    lotto.createTicket();
}

private Constraint ticketNumber(final int ticketNumber) {

    return new Constraint() {

        public boolean eval(Object arg) {

            LotteryTicket ticket = (LotteryTicket) arg;  
            return ticketNumber == ticket.number;
        }

        public StringBuffer describeTo(StringBuffer buffer) {
            return buffer.append(ticketNumber);
        }
    }
}

While this example works fairly well, each new test method would likely require its own Constraint. When the verification in the eval method becomes complex, it can become difficult to determine why a test is failing. An Argument Interceptor allows for traditional jUnit assertions, providing clearer failures for complex verfications.

posted on 2011-01-31 22:24 koradji 阅读(244) 评论(0)  编辑  收藏 所属分类: unit testing


只有注册用户登录后才能发表评论。


网站导航:
 
<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

导航

统计

常用链接

留言簿(2)

随笔分类

随笔档案

文章分类

文章档案

收藏夹

db2

dos

Groovy

Hibernate

java

WAS

web application

搜索

最新评论

阅读排行榜

评论排行榜