Mockito: ฉีดของจริงลงในช่อง @Autowired ส่วนตัว


191

ฉันใช้ Mockito @Mockและ@InjectMocksคำอธิบายประกอบในการฉีดการพึ่งพาในเขตข้อมูลส่วนตัวที่มีบันทึกย่อของสปริง@Autowired:

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Mock
    private SomeService service;

    @InjectMocks
    private Demo demo;

    /* ... */
}

และ

public class Demo {

    @Autowired
    private SomeService service;

    /* ... */
}

ตอนนี้ฉันต้องการฉีดของจริงลงใน@Autowiredฟิลด์ส่วนตัว(โดยไม่มีตัวตั้งค่า) เป็นไปได้หรือเป็นกลไกที่ จำกัด เฉพาะการฉีด Mocks เท่านั้น?


5
โดยปกติเมื่อคุณล้อเลียนสิ่งต่าง ๆ มันก็หมายความว่าคุณไม่สนใจวัตถุที่เป็นรูปธรรมมากนัก ที่คุณสนใจเกี่ยวกับพฤติกรรมของวัตถุที่เยาะเย้ยเท่านั้น บางทีคุณต้องการทดสอบการรวมแทนหรือไม่ หรือคุณอาจให้เหตุผลว่าทำไมคุณถึงต้องการวัตถุที่เยาะเย้ยและเป็นรูปธรรมอยู่ด้วยกัน?
โกโตะ

2
ฉันกำลังจัดการกับรหัสดั้งเดิมและจะต้องใช้เวลานานมากในการ (... ) จากนั้นส่งคืนคำสั่ง (... ) เพื่อตั้งค่าจำลองเพียงเพื่อป้องกัน NPE บางอย่างและไม่ชอบ ในทางกลับกันวัตถุจริงสามารถใช้ได้อย่างปลอดภัยสำหรับเรื่องนี้ มันจะมีประโยชน์มากถ้ามีตัวเลือกในการฉีดของจริงพร้อมกับ mocks แม้ว่านี่อาจจะเป็นกลิ่นรหัสฉันคิดว่ามันสมเหตุสมผลในกรณีนี้
user2286693

อย่าลืมMockitoAnnotations.initMocks(this);ใน@Beforeวิธีการ ฉันรู้ว่ามันไม่ได้เกี่ยวข้องโดยตรงกับคำถามดั้งเดิม แต่สำหรับใครก็ตามที่เข้ามาในภายหลังจะต้องมีการเพิ่มเพื่อทำให้ runnable นี้
Cuga

7
@Cuga: ถ้าคุณใช้นักวิ่ง Mockito สำหรับ JUnit, ( @RunWith(MockitoJUnitRunner.class)) คุณไม่จำเป็นต้องต่อแถวMockitoAnnotations.initMocks(this);
Clint Eastwood

1
ขอบคุณ - ฉันไม่เคยรู้มาก่อนและได้ระบุทั้งสองเสมอ
Cuga

คำตอบ:


306

ใช้@Spyคำอธิบายประกอบ

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
    @Spy
    private SomeService service = new RealServiceImpl();

    @InjectMocks
    private Demo demo;

    /* ... */
}

Mockito จะพิจารณาเขตข้อมูลทั้งหมดที่มี@Mockหรือมี@Spyคำอธิบายประกอบเป็นผู้สมัครที่มีโอกาสถูกแทรกลงในอินสแตนซ์ที่มี@InjectMocksคำอธิบายประกอบพร้อมคำอธิบายประกอบ ในกรณี'RealServiceImpl'ตัวอย่างข้างต้นจะได้รับการฉีดเข้าไปใน 'สาธิต'

สำหรับรายละเอียดเพิ่มเติมดู

Mockito บ้าน

@สอดแนม

@Mock


9
+1: ทำงานให้ฉัน ... ยกเว้นวัตถุ String Mockito บ่น:Mockito cannot mock/spy following: - final classes - anonymous classes - primitive types
Adrian Pronk

ขอบคุณมันใช้งานได้สำหรับฉันเช่นกัน :) เพื่อให้การใช้พร็อกซีจำลองเป็นอย่างอื่นสำหรับการใช้งานจริงนั้นใช้สายลับ
Swarit Agarwal

ขอบคุณ! นั่นคือสิ่งที่ฉันต้องการ!
nterry

2
ในกรณีของฉัน Mockito ไม่ได้ฉีด Spy มันจะทำการเยาะเย้ย ฟิลด์นี้เป็นแบบส่วนตัวและไม่มีตัวตั้งค่า
Vituel

8
BTW มีความจำเป็นที่จะไม่new RealServiceImpl(), @Spy private SomeService service;สร้างวัตถุจริงโดยใช้สร้างเริ่มต้นก่อนที่จะสืบในมันอยู่แล้ว
parxier

20

นอกเหนือจาก @Dev Blanked answer หากคุณต้องการใช้ bean ที่มีอยู่ซึ่งสร้างโดย Spring โค้ดสามารถปรับเปลี่ยนเป็น:

@RunWith(MockitoJUnitRunner.class)
public class DemoTest {

    @Inject
    private ApplicationContext ctx;

    @Spy
    private SomeService service;

    @InjectMocks
    private Demo demo;

    @Before
    public void setUp(){
        service = ctx.getBean(SomeService.class);
    }

    /* ... */
}

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


2
@Aada คุณสามารถทำอย่างละเอียด?
Yoaz Menda

1
ห้องสมุดนี้มาจากไหนฉันสามารถเห็น InjectMocks ใน org.mockito
Sameer

1
@sameer นำเข้า org.mockito.InjectMocks;
Yoaz Menda

การเพิ่มความคิดเห็นเพื่อยกเลิก Aada สิ่งนี้ใช้ได้สำหรับฉัน ฉันไม่สามารถ "ไม่ใช้การฉีดภาคสนาม" ตามคำแนะนำอื่น ๆ ดังนั้นฉันจึงต้องตรวจสอบให้แน่ใจว่าAutowiredเขตข้อมูลจากการอ้างอิงถูกสร้างขึ้นอย่างถูกต้อง
Scrambo

1
ฉันได้ลองสิ่งนี้แล้ว แต่ฉันได้รับข้อยกเว้นตัวชี้โมฆะจากวิธีการตั้งค่า
Akhil Surapuram

3

Mockito ไม่ใช่กรอบ DI และแม้แต่ DI Frameworks ก็สนับสนุนการสร้างคอนสตรัคชั่นเหนือการฉีดภาคสนาม
ดังนั้นคุณเพียงแค่ประกาศให้นวกรรมิกตั้งค่าการพึ่งพาของชั้นเรียนภายใต้การทดสอบ:

@Mock
private SomeService serviceMock;

private Demo demo;

/* ... */
@BeforeEach
public void beforeEach(){
   demo = new Demo(serviceMock);
}

การใช้ Mockito spyสำหรับกรณีทั่วไปเป็นคำแนะนำที่แย่มาก มันทำให้คลาสทดสอบเปราะไม่ตรงและเกิดข้อผิดพลาดได้ง่าย: มีการล้อเลียนอะไรจริง ๆ ผ่านการทดสอบอะไรจริงๆ?
@InjectMocksและ@Spyยังสร้างความเสียหายต่อการออกแบบโดยรวมเนื่องจากมันช่วยให้ชั้นเรียนป่องและความรับผิดชอบที่หลากหลายในชั้นเรียน
โปรดอ่านspy()javadocก่อนใช้งานแบบสุ่มสี่สุ่มห้า (เน้นไม่ใช่ของฉัน):

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

ตามปกติคุณจะต้องอ่านpartial mock warning: การเขียนโปรแกรมเชิงวัตถุจะจัดการกับความซับซ้อนโดยแบ่งความซับซ้อนออกเป็นวัตถุ SRPy แยกต่างหาก แบบจำลองบางส่วนเข้ากันได้กับกระบวนทัศน์นี้อย่างไร ก็แค่ไม่ได้ ... การเยาะเย้ยบางส่วนมักจะหมายถึงความซับซ้อนได้ถูกย้ายไปยังวิธีการที่แตกต่างกันในวัตถุเดียวกัน ในกรณีส่วนใหญ่นี่ไม่ใช่วิธีที่คุณต้องการออกแบบแอปพลิเคชันของคุณ

อย่างไรก็ตามมีบางกรณีที่ mocks บางส่วนเกิดประโยชน์: การจัดการกับโค้ดที่คุณไม่สามารถเปลี่ยนแปลงได้อย่างง่ายดาย (อินเทอร์เฟซของบุคคลที่สามการเปลี่ยนรหัสดั้งเดิมชั่วคราวเป็นต้น) อย่างไรก็ตามฉันจะไม่ใช้ mocks บางส่วนสำหรับการทดสอบใหม่ รหัสที่ออกแบบ


0

ในฤดูใบไม้ผลิมียูทิลิตี้เฉพาะที่เรียกว่าReflectionTestUtilsสำหรับจุดประสงค์นี้ ใช้อินสแตนซ์ที่เฉพาะเจาะจงและฉีดลงในฟิลด์


@Spy
..
@Mock
..

@InjectMock
Foo foo;

@BeforeEach
void _before(){
   ReflectionTestUtils.setField(foo,"bar", new BarImpl());// `bar` is private field
}

-1

ฉันรู้ว่านี่เป็นคำถามเก่า แต่เราต้องเผชิญกับปัญหาเดียวกันเมื่อพยายามฉีด Strings ดังนั้นเราจึงคิดค้นส่วนขยาย JUnit5 / Mockito ที่ทำสิ่งที่คุณต้องการ: https://github.com/exabrial/mockito-object-inject

แก้ไข:

@InjectionMap
 private Map<String, Object> injectionMap = new HashMap<>();

 @BeforeEach
 public void beforeEach() throws Exception {
  injectionMap.put("securityEnabled", Boolean.TRUE);
 }

 @AfterEach
 public void afterEach() throws Exception {
  injectionMap.clear();
 }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.