Mockito: จำลองการเริ่มต้นฟิลด์ส่วนตัว


107

ฉันจะจำลองตัวแปรฟิลด์ที่กำลังเริ่มต้นในบรรทัดได้อย่างไร

class Test {
    private Person person = new Person();
    ...
    public void testMethod() {
        person.someMethod();
        ...
    }
}

ที่นี่ฉันต้องการจำลองperson.someMethod()ขณะทดสอบTest.testMethod()วิธีการที่ฉันต้องการจำลองการเริ่มต้นpersonตัวแปร เบาะแสใด ๆ ?

แก้ไข:ฉันไม่ได้รับอนุญาตให้แก้ไขคลาสบุคคล


1
ลิงค์นี้อาจเป็นประโยชน์กับคุณstackoverflow.com/questions/13645571/…
Popeye

2
คุณควรปรับรหัสของคุณใหม่เพื่อที่คุณจะได้ส่งต่อPersonไป ตัวเลือกรวมถึงการเพิ่มตัวสร้างเพื่อทำสิ่งนี้หรือเพิ่มเมธอด setter
Tim Biegeleisen

คำตอบ:


114

Mockito มาพร้อมกับคลาสผู้ช่วยที่จะช่วยคุณประหยัดรหัสแผ่นสะท้อนของหม้อไอน้ำ:

import org.mockito.internal.util.reflection.Whitebox;

//...

@Mock
private Person mockedPerson;
private Test underTest;

// ...

@Test
public void testMethod() {
    Whitebox.setInternalState(underTest, "person", mockedPerson);
    // ...
}

อัปเดต: น่าเสียดายที่ทีมงาน mockito ตัดสินใจที่จะลบคลาสใน Mockito 2 ดังนั้นคุณจึงกลับมาเขียนโค้ดสำเร็จรูปสะท้อนแสงของคุณเองใช้ไลบรารีอื่น (เช่นApache Commons Lang ) หรือเพียงแค่ขโมยคลาสWhitebox (ซึ่งได้รับใบอนุญาตจาก MIT )

อัปเดต 2: JUnit 5 มาพร้อมกับคลาสReflectionSupportและAnnotationSupportของตัวเองซึ่งอาจเป็นประโยชน์และช่วยให้คุณไม่ต้องดึงไลบรารีอื่นเข้ามา


ฉันกำลังสอดแนมวัตถุเป้าหมายด้วยเหตุผลอื่น ๆ และในกรณีนี้เมื่อวัตถุของฉันถูกสอดแนมฉันไม่สามารถตั้งค่าสถานะภายในด้วยวิธีนี้ได้
อรุณ

ทำไมจะไม่ล่ะ? ฉันใช้มันกับสายลับ สร้างอินสแตนซ์บุคคล Stub สิ่งที่ต้องการทำให้ขาดจากนั้นตั้งค่าในอินสแตนซ์การทดสอบของคุณ
Ralf

คำเตือน: Whitebox อยู่ในinternalแพ็คเกจและดูเหมือนจะไม่ทำงานอีกต่อไปบน Mockito 2.6.2
Nova

คุณสามารถใช้ FieldSetter.setField () ฉันได้ยกตัวอย่างด้านล่างสำหรับสิ่งเดียวกัน
Raj Kumar

1
@Ralf เนื่องจากการเปลี่ยนชื่ออ้างอิงใน Java ควรส่งผลให้เกิดข้อผิดพลาดในการคอมไพล์
Joe Coder

79

ค่อนข้างสายไปงานปาร์ตี้ แต่ฉันหลงที่นี่และได้รับความช่วยเหลือจากเพื่อน สิ่งนี้ไม่ได้ใช้ PowerMock ใช้งานได้กับ Mockito เวอร์ชันล่าสุด

Mockito org.mockito.internal.util.reflection.FieldSetterมาพร้อมกับนี้

สิ่งที่ทำโดยทั่วไปคือช่วยให้คุณปรับเปลี่ยนฟิลด์ส่วนตัวโดยใช้การสะท้อน

นี่คือวิธีที่คุณใช้:

@Mock
private Person mockedPerson;
private Test underTest;

// ...

@Test
public void testMethod() {
    FieldSetter.setField(underTest, underTest.getClass().getDeclaredField("person"), mockedPerson);
    // ...
    verify(mockedPerson).someMethod();
}

ด้วยวิธีนี้คุณสามารถส่งผ่านวัตถุจำลองแล้วตรวจสอบในภายหลัง

นี่คือข้อมูลอ้างอิง


1
FieldSetterไม่สามารถใช้ได้อีกต่อไปใน Mockito 2.x.
Ralf

5
@Ralf ฉันใช้เวอร์ชัน mockito-core-2.15.0 มันมีอยู่ที่นั่น static.javadoc.io/org.mockito/mockito-core/2.0.15-beta/org/… . ยังคงเป็นเบต้าอยู่
Raj Kumar

1
@RajKumar Class#getDeclaredFieldยอมรับพระรามเดียวดังนั้น parens FieldSetter.setField(underTest, underTest.getClass().getDeclaredField("person"), mockedPerson);ต้องมีลักษณะเช่นนี้ นั่นคือสิ่งที่ฉันเข้าใจผิดและคิดว่าอาจเป็นความคิดที่ดีที่จะแก้ไขในตัวอย่างของคุณ ขอบคุณสำหรับการตอบกลับ
Dapeng Li

1
การใช้ API ภายในไม่ใช่ความคิดที่ดีที่สุด
David

@RajKumar Nice แต่ตามที่ Zimbo Rodger กล่าวไว้: ควรใช้ API ภายในหรือไม่? ทำไมถึงเกิดภายในอย่างเฉียบพลัน? มันมีประโยชน์มากสำหรับการทดสอบ
Willi Mentzel

39

ในกรณีที่คุณใช้ Spring Test ให้ลองorg.springframework.test.util.ReflectionTestUtils

 ReflectionTestUtils.setField(testObject, "person", mockedPerson);

1
เยี่ยมมาก! อาจช่วยในการเชื่อมโยงไปยังบทความนี้และอาจรวมถึงการอ้างอิงที่ต้องการจากบทความนี้: baeldung.com/spring-reflection-test-utils
Willi Mentzel

build.gradle.kts:testImplementation("org.springframework:spring-test:5.1.2.RELEASE")
Willi Mentzel

30

ฉันพบวิธีแก้ปัญหานี้แล้วซึ่งฉันลืมโพสต์ไว้ที่นี่

@RunWith(PowerMockRunner.class)
@PrepareForTest({ Test.class })
public class SampleTest {

@Mock
Person person;

@Test
public void testPrintName() throws Exception {
    PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);
    Test test= new Test();
    test.testMethod();
    }
}

ประเด็นสำคัญในการแก้ปัญหานี้คือ:

  1. เรียกใช้กรณีทดสอบของฉันด้วย PowerMockRunner: @RunWith(PowerMockRunner.class)

  2. สั่งให้ Powermock เตรียมพร้อมTest.classสำหรับการจัดการฟิลด์ส่วนตัว:@PrepareForTest({ Test.class })

  3. และในที่สุดก็เยาะเย้ยคอนสตรัคเตอร์สำหรับคลาส Person:

    PowerMockito.mockStatic(Person.class); PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);


9
คำอธิบายของคุณพูดถึงmockStaticฟังก์ชัน แต่ไม่ได้แสดงในตัวอย่างโค้ดของคุณ ตัวอย่างโค้ดควรมีการmockStaticเรียกใช้หรือไม่จำเป็นสำหรับตัวสร้าง?
Shadoninja

10

รหัสต่อไปนี้สามารถใช้เพื่อเริ่มต้นการทำแผนที่ในไคลเอนต์ REST mapperฟิลด์มีความเป็นส่วนตัวและความต้องการที่จะตั้งระหว่างการติดตั้งการทดสอบหน่วย

import org.mockito.internal.util.reflection.FieldSetter;

new FieldSetter(client, Client.class.getDeclaredField("mapper")).set(new Mapper());

5

การใช้คำแนะนำของ @ Jarda คุณสามารถกำหนดสิ่งนี้ได้หากคุณต้องการตั้งค่าตัวแปรเป็นค่าเดียวกันสำหรับการทดสอบทั้งหมด:

@Before
public void setClientMapper() throws NoSuchFieldException, SecurityException{
    FieldSetter.setField(client, client.getClass().getDeclaredField("mapper"), new Mapper());
}

แต่ระวังว่าการตั้งค่าส่วนตัวให้แตกต่างกันควรได้รับการจัดการด้วยความระมัดระวัง หากเป็นส่วนตัวด้วยเหตุผลบางประการ

ตัวอย่างเช่นฉันใช้มันเพื่อเปลี่ยนเวลารอของการนอนหลับในการทดสอบหน่วย ในตัวอย่างจริงฉันต้องการนอนเป็นเวลา 10 วินาที แต่ในการทดสอบหน่วยฉันพอใจหากทำได้ทันที ในการทดสอบการรวมคุณควรทดสอบมูลค่าที่แท้จริง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.