如何使用在mockito中调用之间更改状态的相同参数来验证相同模拟方法的调用?

时间:2016-04-12 12:32:28

标签: java unit-testing mockito

我有以下代码进行单元测试:

persistence.save

我想验证第一次调用entity.getDate() null时返回Mockito.verify(/*...*/)

因此我无法使用foo因为当时方法entity.setDate(Date)已经完成且 </script> <script> var app = angular.module('myApp',[]); app.directive('tagInputType', function() { return { restrict: 'E', scope: { tags: '=' }, template: '<div class="tags">' + '<a ng-repeat="(idx, tag) in tags" class="tag" ng-click="remove(idx)">{{tag}}</a>' + '</div>' + '<input type="text" placeholder="Add a tag..." ng-model="new_value"></input> ' + '<input type="button" value="Add" ng-click="add()"></input>', link: function ( $scope, $element ) { // var input = angular.element( $element.children()[1] ); $scope.add = function() { $scope.tags.push( $scope.new_value ); $scope.new_value = ""; }; $scope.remove = function ( idx ) { $scope.tags.splice( idx, 1 ); }; input.bind( 'keypress', function ( event ) { if ( event.keyCode == 13 ) { $scope.$apply( $scope.add ); } }); } }; }); app.controller('MainCtrl', function ( $scope ) { $scope.tags = { "value1":"angular","value2":"html"}; }); </script> 已被调用。

所以我认为我需要在调用发生时对调用进行验证。我如何使用Mockito做到这一点?

2 个答案:

答案 0 :(得分:6)

我创建了以下Answer实现:

public class CapturingAnswer<T, R> implements Answer<T> {

    private final Function<InvocationOnMock, R> capturingFunction;

    private final List<R> capturedValues = new ArrayList<R>();

    public CapturingAnswer(final Function<InvocationOnMock, R> capturingFunction) {
        super();
        this.capturingFunction = capturingFunction;
    }

    @Override
    public T answer(final InvocationOnMock invocation) throws Throwable {
        capturedValues.add(capturingFunction.apply(invocation));
        return null;
    }

    public List<R> getCapturedValues() {
        return Collections.unmodifiableList(capturedValues);
    }

}

此答案捕获正在进行的调用的属性。然后capturedValues可用于简单断言。该实现使用Java 8 API。如果不可用,则需要使用能够将InvocationOnMock转换为捕获值的接口。测试用例中的用法如下:

@Test
public void testSomething() {
    CapturingAnswer<Void,Date> captureDates = new CapturingAnswer<>(this::getEntityDate)
    Mockito.doAnswer(captureDates).when(persistence).save(Mockito.any(Entity.class));

    service.foo();

    Assert.assertNull(captureDates.getCapturedValues().get(0));
}

private Date getEntityDate(InvocationOnMock invocation) {
    Entity entity = (Entity)invocation.getArguments()[0];
    return entity.getDate();
}

使用Mockitos Answer无法实现所呈现的ArgumentCaptor实现所执行的捕获,因为这仅在调用被测方法之后使用。

答案 1 :(得分:1)

在我原来的评论中,这是我想到的答案。

要嘲笑的课程:

class MockedClass{
    void save(SomeBean sb){
        //doStuff
    }
}

我们需要验证Date对象的类是null。

class SomeBean{
    Date date;

    Date getDate(){return date;}

    void setDate(Date date){this.date=date;}
}

受测试的课程:

class TestClass{
    MockedClass mc;

    TestClass(MockedClass mc){this.mc = mc;}

    void doWork(){
        SomeBean sb = new SomeBean();
        mc.save(sb);
        sb.setDate(new Date());
        mc.save(sb);
    }
}

测试用例:

@Test
public void testAnswer(){
    MockedClass mc = Mockito.mock(MockedClass.class);
    Mockito.doAnswer(new Answer<Void>(){
        boolean checkDate = true;
        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            SomeBean sb = (SomeBean) invocation.getArguments()[0];
            if(checkDate && sb.getDate() != null){
                throw new NullPointerException(); //Or a more meaningful exception
            }
            checkDate = false;
            return null;
        }}).when(mc).save(Mockito.any(SomeBean.class));;

    TestClass tc =  new TestClass(mc);
    tc.doWork();
}

第一次通过这个Answer(我应该在我的原始注释中使用的术语),如果date不为null,这将抛出异常并使测试用例失败。第二次,checkDate将为false,因此不会执行检查。