Xác minh giá trị thuộc tính đối tượng với mockito


264

Tôi có một cuộc gọi phương thức mà tôi muốn chế giễu với mockito. Để bắt đầu, tôi đã tạo và chèn một thể hiện của một đối tượng mà phương thức sẽ được gọi. Mục đích của tôi là xác minh một trong các đối tượng trong cuộc gọi phương thức.

Có cách nào mockito cho phép bạn xác nhận hoặc xác minh đối tượng và thuộc tính của nó khi phương thức giả được gọi không?

thí dụ

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>anyObject())

Thay vì làm, anyObject()tôi muốn kiểm tra đối tượng đối số có chứa một số trường cụ thể

Mockito.verify(mockedObject)
       .someMethodOnMockedObject(
              Mockito.<SomeObjectAsArgument>**compareWithThisObject()**)

Thay thế cho việc sử dụng mockito trong những trường hợp này, bạn có thể xem xét việc tạo một cuống tùy chỉnh mở rộng lớp của mockedObject và ghi đè một sốMethodOnMockedObject để lưu đối tượng để so sánh sau.
Gonen I

Câu trả lời:


540

Tính năng mới được thêm vào Mockito giúp việc này trở nên dễ dàng hơn,

ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("John", argument.getValue().getName());

Hãy xem tài liệu của Mockito


Trong trường hợp khi có nhiều hơn một tham số và chỉ cần chụp một tham số duy nhất, hãy sử dụng các đối số ArgumentMatchers khác để bọc phần còn lại của các đối số:

verify(mock).doSomething(eq(someValue), eq(someOtherValue), argument.capture());
assertEquals("John", argument.getValue().getName());

1
nếu phương thức của bạn có nhiều hơn một đối số, bạn cũng phải sử dụng Bộ so khớp cho tất cả các đối số khác. akcasoy.wordpress.com/tag/argumentcaptor
robsonrosa

1
Nếu có nhiều đối số thì sao? Làm thế nào bạn xác định chính xác một trong những bạn quan tâm?
IgorGanapolsky

2
@IgorGanapolsky Giả sử tham số Chuỗi thứ hai cho doS Something bạn cần làm: verify (mock) .doS Something (argument.capture (), anyString ());
GreenTurtle

nhu cầu sử dụng các công cụ đối sánh cho tất cả các đối số chỉ theo mỗi thông số sử dụng đối sánh tất cả hoặc không có tiêu chuẩn.
Charney Kaye

54

Tôi nghĩ cách dễ nhất để xác minh một đối tượng đối số là sử dụng refEqphương thức:

Mockito.verify(mockedObject).someMethodOnMockedObject(Matchers.refEq(objectToCompareWith));

Nó có thể được sử dụng ngay cả khi đối tượng không thực hiện equals(), vì phản xạ được sử dụng. Nếu bạn không muốn so sánh một số trường, chỉ cần thêm tên của chúng làm đối số cho refEq.


1
đó là một cách rất thanh lịch nhưng thật không may org.mockito.Matchers hiện không được chấp nhận
ihebiheb

5
@ihebiheb Nó được chuyển đến ArgumentMatchers
Michael

48

Một khả năng nữa, nếu bạn không muốn sử dụng ArgumentCaptor(ví dụ, vì bạn cũng đang sử dụng tính năng khai thác), là sử dụng Bộ so khớp Hamcrest kết hợp với Mockito.

import org.mockito.Mockito
import org.hamcrest.Matchers
...

Mockito.verify(mockedObject).someMethodOnMockedObject(MockitoHamcrest.argThat(
    Matchers.<SomeObjectAsArgument>hasProperty("propertyName", desiredValue)));

2
Sidenote: đảm bảo Matchersgói chính xác, vì viết cùng một dòng mã với org.mockito.Matcherslớp sẽ ném một ngoại lệ sai lệch cho biết tham số của hàm giả đơn giản là không khớp.
buer

1
Xin lưu ý rằng trong các phiên bản Mockito hiện đại, có MockitoHamcrest.argThat()và khôngMockito.argThat()
Roman Puchkovskiy

17

Đây là câu trả lời dựa trên câu trả lời từ iraSenthil nhưng có chú thích ( Captor ). Theo tôi nó có một số lợi thế:

  • nó ngắn hơn
  • nó dễ đọc hơn
  • nó có thể xử lý thuốc generic mà không cần cảnh báo

Thí dụ:

@RunWith(MockitoJUnitRunner.class)
public class SomeTest{

    @Captor
    private ArgumentCaptor<List<SomeType>> captor;

    //...

    @Test 
    public void shouldTestArgsVals() {
        //...
        verify(mockedObject).someMethodOnMockedObject(captor.capture());

        assertThat(captor.getValue().getXXX(), is("expected"));
    }
}

Điều này sẽ chỉ làm việc cho một đối số duy nhất trong params.
IgorGanapolsky

Bạn có thể sử dụng một captor cho nhiều hơn một đối số. Nếu bạn nắm bắt được nhiều hơn một đối số, bạn có thể liệt kê tất cả các kết quả với captor.getAllValues(). Phương pháp captor.getValue()được sử dụng trong câu trả lời mang lại kết quả cuối cùng.
Walery Strauch

11

Nếu bạn đang sử dụng Java 8, bạn có thể sử dụng các biểu thức Lambda để khớp.

import java.util.Optional;
import java.util.function.Predicate;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;

public class LambdaMatcher<T> extends BaseMatcher<T>
{
    private final Predicate<T> matcher;
    private final Optional<String> description;

    public LambdaMatcher(Predicate<T> matcher)
    {
        this(matcher, null);
    }

    public LambdaMatcher(Predicate<T> matcher, String description)
    {
        this.matcher = matcher;
        this.description = Optional.ofNullable(description);
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean matches(Object argument)
    {
        return matcher.test((T) argument);
    }

    @Override
    public void describeTo(Description description)
    {
        this.description.ifPresent(description::appendText);
    }
}

Cuộc gọi ví dụ

@Test
public void canFindEmployee()
{
    Employee employee = new Employee("John");
    company.addEmployee(employee);

    verify(mockedDal).registerEmployee(argThat(new LambdaMatcher<>(e -> e.getName()
                                                                         .equals(employee.getName()))));
}

Thông tin thêm: http://source.coveo.com/2014/10/01/java8-mockito/


5

Các giải pháp trên không thực sự hiệu quả trong trường hợp của tôi. Tôi không thể sử dụng ArgumentCaptor vì phương thức được gọi nhiều lần và tôi cần xác thực từng cái. Một Matcher đơn giản với "argThat" đã thực hiện thủ thuật một cách dễ dàng.

Trình so khớp tùy chỉnh

// custom matcher
private class PolygonMatcher extends ArgumentMatcher<PolygonOptions> {
    private int fillColor;
    public PolygonMatcher(int fillColor) {
        this.fillColor = fillColor;
    }

    @Override
    public boolean matches(Object argument) {
        if (!(argument instanceof PolygonOptions)) return false;
        PolygonOptions arg = (PolygonOptions)argument;
        return Color.red(arg.getFillColor()) == Color.red(fillColor)
                && Color.green(arg.getFillColor()) == Color.green(fillColor)
                && Color.blue(arg.getFillColor()) == Color.blue(fillColor);
    }
}

Người chạy thử

// do setup work setup
// 3 light green polygons
int green = getContext().getResources().getColor(R.color.dmb_rx_bucket1);
verify(map, times(3)).addPolygon(argThat(new PolygonMatcher(green)));

// 1 medium yellow polygons
int yellow = getContext().getResources().getColor(R.color.dmb_rx_bucket4);
    verify(map, times(1)).addPolygon(argThat(new PolygonMatcher(yellow)));

// 3 red polygons
int orange = getContext().getResources().getColor(R.color.dmb_rx_bucket5);
verify(map, times(3)).addPolygon(argThat(new PolygonMatcher(orange)));

// 2 red polygons
int red = getContext().getResources().getColor(R.color.dmb_rx_bucket7);
verify(map, times(2)).addPolygon(argThat(new PolygonMatcher(red)));

3

Và giải pháp rất hay và sạch trong koltin từ com.nhaarman.mockito_kotlin

verify(mock).execute(argThat {
    this.param = expected
})

1

Bạn có thể tham khảo như sau:

Mockito.verify(mockedObject).someMethodOnMockedObject(eq(desiredObject))

Điều này sẽ xác minh xem phương thức của mockedObject có được gọi với tham số mong muốn không.


1

Một cách dễ dàng khác để làm như vậy:

import org.mockito.BDDMockito;    
import static org.mockito.Matchers.argThat;
import org.mockito.ArgumentMatcher;

BDDMockito.verify(mockedObject)
        .someMethodOnMockedObject(argThat(new ArgumentMatcher<TypeOfMethodArg>() {

            @Override
            public boolean matches(Object argument) {
                final TypeOfMethodArg castedArg = (TypeOfMethodArg) argument;

                // Make your verifications and return a boolean to say if it matches or not
                boolean isArgMarching = true;

                return isArgMarching;
            }
        }));

0

Các javadoc cho refEq đã đề cập rằng kiểm tra bình đẳng là nông cạn! Bạn có thể tìm thêm chi tiết tại liên kết dưới đây:

[ https://static.javadoc.io/org.mockito/mockito-core/2.2.29/org/mockito/ArgumentMatchers.html#refEq(T,%20java.lang.String...)[[1]

Vấn đề "bình đẳng nông" không thể được kiểm soát khi bạn sử dụng các lớp khác không triển khai phương thức .equals (), lớp "DefaultMongoTypeMapper" là một ví dụ trong đó phương thức .equals () không được triển khai.

org.springframework.beans.factory.support cung cấp một phương thức có thể tạo ra một định nghĩa bean thay vì tạo một thể hiện của đối tượng và nó có thể được sử dụng để loại bỏ Lỗi so sánh.

 genericBeanDefinition(DefaultMongoTypeMapper.class)
                        .setScope(SCOPE_SINGLETON)
                        .setAutowireMode(AUTOWIRE_CONSTRUCTOR)
                        .setLazyInit(false)
                        .addConstructorArgValue(null)
                        .getBeanDefinition()

** "Định nghĩa bean chỉ là một mô tả về bean chứ không phải là bean. Các mô tả bean thực hiện đúng bằng () và hashCode (), vì vậy thay vì tạo một DefaultMongoTypeMapper () mới, chúng tôi cung cấp một định nghĩa cho mùa xuân biết nó như thế nào nên tạo một "

Trong ví dụ của bạn, bạn có thể làm điều gì đó như thế này

Mockito.verify(mockedObject)
       .doSoething(genericBeanDefinition(YourClass.class).setA("a")
       .getBeanDefinition());

0

Một giải pháp đơn giản hóa mà không cần tạo lớp triển khai Matcher mới và sử dụng biểu thức lambda:

        verify(mockObject).someMockMethod(argThat((SomeArgument arg) -> arg.fieldToMatch.equals(expectedFieldValue));
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.