簡體   English   中英

用 mockito 驗證 object 屬性值

[英]Verify object attribute value with mockito

我有一個方法調用,我想用 mockito 模擬。首先,我創建並注入了一個 object 的實例,將在該實例上調用該方法。 我的目標是驗證方法調用中的 object 之一。

有沒有一種方法 mockito 允許您在調用模擬方法時斷言或驗證 object 及其屬性?

例子

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

我不想做anyObject()我想檢查參數 object 是否包含一些特定的字段

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

添加到 Mockito 的新功能使這變得更加容易,

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

查看 Mockito 文檔


如果有多個參數,並且只需要捕獲單個參數,請使用其他 ArgumentMatcher 包裝其余參數:

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

我認為驗證參數對象的最簡單方法是使用refEq方法:

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

即使對象沒有實現equals()也可以使用它,因為使用了反射。 如果您不想比較某些字段,只需將它們的名稱添加為refEq參數。

另一種可能性,如果您不想使用ArgumentCaptor (例如,因為您也在使用存根),則將 Hamcrest Matchers 與 Mockito 結合使用。

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

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

這是基於iraSenthil 的答案但帶有注釋( Captor )的答案 在我看來,它有一些優點:

  • 它更短
  • 更容易閱讀
  • 它可以在沒有警告的情況下處理泛型

例子:

@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"));
    }
}

如果您使用的是 Java 8,則可以使用 Lambda 表達式進行匹配。

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);
    }
}

示例調用

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

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

更多信息: http : //source.coveo.com/2014/10/01/java8-mockito/

一種簡化的解決方案,無需創建新的 Matcher 實現類和使用 lambda 表達式:

verify(mockObject).someMockMethod(
        argThat((SomeArgument arg) -> arg.fieldToMatch.equals(expectedFieldValue)));

上面的解決方案在我的情況下並沒有真正起作用。 我無法使用 ArgumentCaptor,因為該方法被多次調用,我需要驗證每一個。 一個簡單的帶有“argThat”的 Matcher 很容易做到這一點。

自定義匹配器

// 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);
    }
}

測試運行器

// 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)));

來自com.nhaarman.mockito_kotlin非常漂亮和干凈的解決方案

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

您可以參考以下內容:

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

這將驗證是否使用 desiredObject 作為參數調用了 mockedObject 的方法。

refEq 的 javadoc 提到相等性檢查很淺! 您可以在以下鏈接中找到更多詳細信息:

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

當您使用其他未實現 .equals() 方法的類時,無法控制“淺相等”問題,“DefaultMongoTypeMapper”類是未實現 .equals() 方法的示例。

org.springframework.beans.factory.support 提供了一種可以生成 bean 定義而不是創建對象實例的方法,它可以用來消除比較失敗。

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

**“bean 定義只是對 bean 的描述,而不是 bean 本身。bean 描述正確地實現了 equals() 和 hashCode(),因此與其創建新的 DefaultMongoTypeMapper(),我們提供了一個定義來告訴 spring 它如何應該創造一個”

在你的例子中,你可以做這樣的事情

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

另一種簡單的方法是:

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;
            }
        }));

我一直在使用方法實例化模擬服務類的設置中工作。 如果我想驗證一個方法是否被調用了 1 次,參數 object 在字段中包含特定值,對於class Service ,我會這樣做,方法updateMethod(UpdateObject updateObjectParam){} :

@Mock
Service service;

...

Mockito.verify(service, times(1)).updateMethod(argThat(updateParamObject -> updateParamObject.getField().equals("fieldValue")));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM