ตรวจสอบค่าแอตทริบิวต์ของวัตถุด้วย mockito


264

ฉันมีวิธีการโทรซึ่งฉันต้องการที่จะเยาะเย้ยกับ mockito เริ่มต้นด้วยฉันได้สร้างและฉีดอินสแตนซ์ของวัตถุที่เรียกว่าวิธีการ จุดประสงค์ของฉันคือการตรวจสอบหนึ่งในวัตถุในวิธีการโทร

มีวิธีที่ mockito ช่วยให้คุณสามารถยืนยันหรือตรวจสอบวัตถุและมันเป็นคุณลักษณะเมื่อมีการเรียกวิธีการจำลอง?

ตัวอย่าง

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

แทนที่จะทำanyObject()ฉันต้องการตรวจสอบว่าวัตถุอาร์กิวเมนต์มีบางฟิลด์

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

เป็นทางเลือกแทนการใช้ mockito ในกรณีเหล่านี้คุณสามารถพิจารณาสร้าง stub แบบกำหนดเองที่ขยายคลาสของ mockedObject และแทนที่ someMethodOnMockedObject เพื่อบันทึกวัตถุสำหรับการเปรียบเทียบในภายหลัง
Gonen ฉัน

คำตอบ:


540

คุณสมบัติใหม่ที่เพิ่มใน Mockito ทำให้สิ่งนี้ง่ายยิ่งขึ้น

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

ดูเอกสาร Mockito


ในกรณีที่มีมากกว่าหนึ่งพารามิเตอร์และต้องการการจับพารามิเตอร์เพียงครั้งเดียวให้ใช้ ArgumentMatchers อื่น ๆ เพื่อตัดอาร์กิวเมนต์ที่เหลือ:

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

1
หากวิธีการของคุณมีมากกว่าหนึ่งอาร์กิวเมนต์คุณต้องใช้ Matchers สำหรับอาร์กิวเมนต์อื่นทั้งหมด akcasoy.wordpress.com/tag/argumentcaptor
robsonrosa

1
เกิดอะไรขึ้นถ้ามีข้อโต้แย้งหลายอย่าง? คุณระบุคนที่คุณสนใจได้อย่างไร?
IgorGanapolsky

2
@IgorGanapolsky สมมติว่ามีพารามิเตอร์ String ตัวที่สองสำหรับ doSomething ที่คุณต้องทำ: ตรวจสอบ (จำลอง) .doSomething (argument.capture (), anyString ());
GreenTurtle

จำเป็นที่จะต้องใช้ matchers สำหรับการขัดแย้งทั้งหมดเป็นเพียงต่อมาตรฐานข้อมูลการใช้งาน matcher ทั้งหมดหรือไม่มี
Charney Kaye

54

ฉันคิดว่าวิธีที่ง่ายที่สุดในการตรวจสอบวัตถุอาร์กิวเมนต์คือการใช้refEqวิธีการ:

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

สามารถใช้งานได้แม้ว่าวัตถุนั้นจะไม่ได้ใช้งานequals()ก็ตามเพราะมีการใช้การสะท้อนภาพ refEqหากคุณไม่ต้องการที่จะเปรียบเทียบบางสาขาเพียงแค่เพิ่มชื่อของพวกเขาเป็นข้อโต้แย้ง


1
นั่นเป็นวิธีที่สง่างามมาก แต่น่าเสียดายที่ org.mockito.Matchers เลิกใช้แล้ว
ihebiheb

5
@ihebiheb มันถูกย้ายไปยัง ArgumentMatchers
ไมเคิล

48

ความเป็นไปได้อีกอย่างหนึ่งหากคุณไม่ต้องการใช้ArgumentCaptor(ตัวอย่างเช่นเพราะคุณกำลังใช้การขัดด้วย) ก็คือการใช้ Hamcrest Matchers ร่วมกับ Mockito

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

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

2
Sidenote: ตรวจสอบให้แน่ใจว่าMatchersแพคเกจนั้นถูกต้องเนื่องจากการเขียนบรรทัดของรหัสเดียวกันกับorg.mockito.Matchersคลาสจะมีข้อผิดพลาดที่ทำให้เข้าใจผิดโดยระบุว่าพารามิเตอร์ของฟังก์ชันจำลองนั้นไม่ตรงกัน
Buer

1
โปรดทราบว่าในรุ่น Mockito ที่ทันสมัยมันเป็นMockitoHamcrest.argThat()และไม่ได้Mockito.argThat()
โรมัน Puchkovskiy

17

นี่คือคำตอบตามคำตอบจาก 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"));
    }
}

สิ่งนี้จะใช้งานได้กับอาร์กิวเมนต์เดียวใน params เท่านั้น
IgorGanapolsky

คุณสามารถใช้หนึ่ง captor เพื่อโต้แย้งมากกว่าหนึ่งข้อ captor.getAllValues()ถ้าคุณจับมากกว่าหนึ่งอาร์กิวเมนต์คุณสามารถแสดงผลทั้งหมดที่มี วิธีการ captor.getValue()ที่ใช้ในคำตอบให้ผลลัพธ์สุดท้าย
Walery Strauch

11

หากคุณใช้ Java 8 คุณสามารถใช้นิพจน์แลมบ์ดาเพื่อจับคู่

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/


5

การแก้ปัญหาข้างต้นไม่ได้ผลในกรณีของฉัน ฉันไม่สามารถใช้ ArgumentCaptor ได้เนื่องจากวิธีการนั้นถูกเรียกหลายครั้งและฉันต้องการตรวจสอบแต่ละวิธี Matcher ง่าย ๆ ที่มี "argThat" ทำได้ง่าย ๆ

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

3

และทางออกที่ดีมากและสะอาดใน koltin จาก com.nhaarman.mockito_kotlin

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

1

คุณสามารถอ้างอิงต่อไปนี้:

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

สิ่งนี้จะตรวจสอบว่าเมธอดของ mockedObject นั้นถูกเรียกใช้พร้อมกับปรารถนาวัตถุเป็นพารามิเตอร์


1

อีกวิธีง่าย ๆ ในการทำเช่นนี้:

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

javadoc สำหรับ refEq กล่าวถึงว่าการตรวจสอบความเท่าเทียมกันนั้นตื้นมาก! คุณสามารถหารายละเอียดเพิ่มเติมได้ที่ลิงค์ด้านล่าง:

[ 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 เสนอวิธีการที่สามารถสร้างคำจำกัดความของถั่วแทนการสร้างตัวอย่างของวัตถุและสามารถใช้ในการคอมไพล์กำจัดความล้มเหลวเปรียบเทียบ

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

** "คำจำกัดความถั่วเป็นเพียงคำอธิบายของถั่วไม่ใช่คำอธิบายของถั่วคำอธิบายของถั่วใช้อย่างถูกต้องเท่ากับ () และ hashCode () ดังนั้นแทนที่จะสร้างใหม่ DefaultMongoTypeMapper () เราให้คำจำกัดความที่บอกว่าสปริงเป็นอย่างไร ควรสร้างหนึ่ง "

ในตัวอย่างของคุณคุณสามารถทำอะไรแบบนี้ได้

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

0

โซลูชันที่ง่ายขึ้นโดยไม่ต้องสร้างคลาสการใช้งาน Matcher ใหม่และการใช้แลมบ์ดานิพจน์:

        verify(mockObject).someMockMethod(argThat((SomeArgument arg) -> arg.fieldToMatch.equals(expectedFieldValue));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.