Làm thế nào để khớp đúng varargs trong Mockito


152

Tôi đã cố gắng để giả định một phương thức với các tham số vararg bằng Mockito:

interface A {
  B b(int x, int y, C... c);
}

A a = mock(A.class);
B b = mock(B.class);

when(a.b(anyInt(), anyInt(), any(C[].class))).thenReturn(b);
assertEquals(b, a.b(1, 2));

Điều này không hoạt động, tuy nhiên nếu tôi làm điều này thay vào đó:

when(a.b(anyInt(), anyInt())).thenReturn(b);
assertEquals(b, a.b(1, 2));

Điều này hoạt động, mặc dù tôi đã hoàn toàn bỏ qua đối số varargs khi khai thác phương thức.

Bất kì manh mối nào?


thực tế là ví dụ cuối hoạt động khá tầm thường vì nó khớp với trường hợp khi các tham số varargs zero được thông qua.
topchef

Câu trả lời:


235

Mockito 1.8.1 đã giới thiệu trình so khớp anyVararg () :

when(a.b(anyInt(), anyInt(), Matchers.<String>anyVararg())).thenReturn(b);

Đồng thời xem lịch sử cho việc này: https://code.google.com.vn/archive/p/mockito/issues/62

Chỉnh sửa cú pháp mới sau khi phản đối:

when(a.b(anyInt(), anyInt(), ArgumentMatchers.<String>any())).thenReturn(b);

26
anyVararg()có Object là kiểu trả về của nó. Để làm cho nó tương thích với bất kỳ loại var arg nào (ví dụ String ..., Integer ..., v.v.), hãy thực hiện một phép truyền rõ ràng. Ví dụ, nếu bạn có doSomething(Integer number, String ... args)bạn có thể thực hiện mã giả / sơ khai với một cái gì đó như when(mock).doSomething(eq(1), (String) anyVarargs()). Điều đó sẽ chăm sóc các lỗi biên dịch.
Cú đấm tâm lý

15
để biết thông tin anyVararg hiện không được chấp nhận: "@deprecated kể từ 2.1.0, hãy sử dụng bất kỳ ()"
alexbt

5
Matchershiện không được chấp nhận để tránh xung đột tên với org.hamcrest.Matcherslớp và có thể sẽ bị xóa trong mockito v3.0. Sử dụng ArgumentMatchersthay thế.
JonyD

31

Một tính năng hơi không có giấy tờ: Nếu bạn muốn phát triển Trình so khớp tùy chỉnh phù hợp với các đối số khác nhau, bạn cần phải thực hiện org.mockito.internal.matchers.VarargMatchernó để nó hoạt động chính xác. Đó là một giao diện đánh dấu trống, không có Mockito sẽ không so sánh chính xác các đối số khi gọi một phương thức với các varargs bằng Matcher của bạn.

Ví dụ:

class MyVarargMatcher extends ArgumentMatcher<C[]> implements VarargMatcher {
    @Override public boolean matches(Object varargArgument) {
        return /* does it match? */ true;
    }
}

when(a.b(anyInt(), anyInt(), argThat(new MyVarargMatcher()))).thenReturn(b);

7

Dựa trên câu trả lời của Eli Levine ở đây là một giải pháp chung chung hơn:

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.mockito.ArgumentMatcher;
import org.mockito.internal.matchers.VarargMatcher;

import static org.mockito.Matchers.argThat;

public class VarArgMatcher<T> extends ArgumentMatcher<T[]> implements VarargMatcher {

    public static <T> T[] varArgThat(Matcher<T[]> hamcrestMatcher) {
        argThat(new VarArgMatcher(hamcrestMatcher));
        return null;
    }

    private final Matcher<T[]> hamcrestMatcher;

    private VarArgMatcher(Matcher<T[]> hamcrestMatcher) {
        this.hamcrestMatcher = hamcrestMatcher;
    }

    @Override
    public boolean matches(Object o) {
        return hamcrestMatcher.matches(o);
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("has varargs: ").appendDescriptionOf(hamcrestMatcher);
    }

}

Sau đó, bạn có thể sử dụng nó với bộ so khớp mảng của hamcrest, do đó:

verify(a).b(VarArgMatcher.varArgThat(
            org.hamcrest.collection.IsArrayContaining.hasItemInArray("Test")));

(Rõ ràng nhập tĩnh sẽ khiến điều này dễ đọc hơn.)


Đẹp. Điều này nên được tích hợp vào Mockito IMO.
bryant

Tôi đã đệ trình một vấn đề chống lại Hamcrest để thêm một cái gì đó như thế này. Xem github.com/mockito/mockito/issues/356
Đánh dấu

Đây có phải là cho Mockito 1? Tôi nhận được các lỗi biên dịch khác nhau khi cố gắng biên dịch theo 2.10.
Frans

@Frans có vẻ như bản phát hành 2 vẫn đang trong giai đoạn thử nghiệm khi tôi viết câu trả lời này, vì vậy, có lẽ nó được viết cho Mockito v1.10.19 hoặc ở đó. ( github.com/mockito/mockito/release ) Có thể cập nhật ... :-D
Peter Westmacott

3

Tôi đã sử dụng mã trong câu trả lời của Peter Westmacott, tuy nhiên với Mockito 2.2.15 bây giờ bạn có thể làm như sau:

verify(a).method(100L, arg1, arg2, arg3)

nơi arg1, arg2, arg3là varargs.


1

Dựa trên câu trả lời của topchef,

Đối với 2.0.31-beta, tôi đã phải sử dụng Mockito.anyVararg thay vì Matchers.anyVararrg:

when(a.b(anyInt(), anyInt(), Mockito.<String>anyVararg())).thenReturn(b);

3
để biết thông tin anyVararg hiện không được chấp nhận: "@deprecated kể từ 2.1.0, hãy sử dụng bất kỳ ()"
alexbt

0

Trong trường hợp của tôi, chữ ký của phương thức mà tôi muốn nắm bắt đối số của nó là:

    public byte[] write(byte ... data) throws IOException;

Trong trường hợp này, bạn nên chuyển sang mảng byte một cách rõ ràng:

       when(spi.write((byte[])anyVararg())).thenReturn(someValue);

Tôi đang sử dụng phiên bản mockito 1.10.19


0

Bạn cũng có thể lặp qua các đối số:

Object[] args = invocation.getArguments(); 
for( int argNo = 0; argNo < args.length; ++argNo) { 
    // ... do something with args[argNo] 
}

ví dụ kiểm tra các loại của chúng và bỏ chúng một cách thích hợp, thêm vào danh sách hoặc bất cứ thứ gì.


0

Điều chỉnh câu trả lời từ @topchef,

Mockito.when(a.b(Mockito.anyInt(), Mockito.anyInt(), Mockito.any())).thenReturn(b);

Theo các tài liệu java cho Mockito 2.23.4, Mockito.any () "Phù hợp với mọi thứ, bao gồm null và varargs."


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.