ความแตกต่างระหว่าง @Mock และ @InjectMocks


คำตอบ:


542

@Mockสร้างจำลอง @InjectMocksสร้างตัวอย่างของคลาสและฉีด mocks ที่สร้างขึ้นด้วยคำอธิบายประกอบ@Mock(หรือ@Spy) ลงในอินสแตนซ์นี้

โปรดทราบว่าคุณต้องใช้@RunWith(MockitoJUnitRunner.class)หรือMockito.initMocks(this)เพื่อเริ่มต้น mocks เหล่านี้และฉีดพวกเขา

@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {

    @InjectMocks
    private SomeManager someManager;

    @Mock
    private SomeDependency someDependency; // this will be injected into someManager

     //tests...

}

2
คำตอบสั้นและกระชับ มีประโยชน์ด้วย;)
Chaklader Asfak Arefe

มันใช้งานได้สำหรับการอ้างอิงสกรรมกริยาหรือเฉพาะสมาชิกโดยตรง?
Pierre Thibault

mocks @PierreThibault ฉีดทำงานเฉพาะสำหรับสมาชิกโดยตรง แต่คุณสามารถตั้งค่าการเยาะเย้ยเพื่อให้สมบูรณ์ลึกstatic.javadoc.io/org.mockito/mockito-core/3.0.0/org/mockito/...
ทอม Verelst

1
ฉันรู้สึกว่ามันชัดเจนกว่าบทความส่วนใหญ่ทางออนไลน์ .... ความคิดเห็นเล็ก ๆ น้อย ๆ ที่ช่วยชีวิตฉัน ...
IHC_Applroid

ฉันมีบางรายการที่ไม่สามารถให้บริการโดย @Mock คำอธิบายประกอบเช่นบริบท ฉันจะให้สิ่งนั้นกับชั้นเรียนหลักได้อย่างไร
มาห์ดี

220

นี่คือตัวอย่างโค้ดเกี่ยวกับวิธีการ@Mockและการ@InjectMocksทำงาน

บอกว่าเรามีGameและPlayerชั้นเรียน

class Game {

    private Player player;

    public Game(Player player) {
        this.player = player;
    }

    public String attack() {
        return "Player attack with: " + player.getWeapon();
    }

}

class Player {

    private String weapon;

    public Player(String weapon) {
        this.weapon = weapon;
    }

    String getWeapon() {
        return weapon;
    }
}

ที่คุณเห็นGameระดับความต้องการที่จะดำเนินการPlayerattack

@RunWith(MockitoJUnitRunner.class)
class GameTest {

    @Mock
    Player player;

    @InjectMocks
    Game game;

    @Test
    public void attackWithSwordTest() throws Exception {
        Mockito.when(player.getWeapon()).thenReturn("Sword");

        assertEquals("Player attack with: Sword", game.attack());
    }

}

Mockito จะเยาะเย้ยคลาสของผู้เล่นโดยใช้พฤติกรรมwhenและthenReturnวิธีการ สุดท้ายการใช้@InjectMocksMockito จะนำสิ่งนั้นPlayerไปGameใช้

โปรดสังเกตว่าคุณไม่จำเป็นต้องสร้างnew Gameวัตถุ Mockito จะทำการฉีดให้คุณ

// you don't have to do this
Game game = new Game(player);

นอกจากนี้เรายังจะได้รับพฤติกรรมเดียวกันโดยใช้@Spyคำอธิบายประกอบ แม้ว่าชื่อแอตทริบิวต์จะแตกต่างกัน

@RunWith(MockitoJUnitRunner.class)
public class GameTest {

  @Mock Player player;

  @Spy List<String> enemies = new ArrayList<>();

  @InjectMocks Game game;

  @Test public void attackWithSwordTest() throws Exception {
    Mockito.when(player.getWeapon()).thenReturn("Sword");

    enemies.add("Dragon");
    enemies.add("Orc");

    assertEquals(2, game.numberOfEnemies());

    assertEquals("Player attack with: Sword", game.attack());
  }
}

class Game {

  private Player player;

  private List<String> opponents;

  public Game(Player player, List<String> opponents) {
    this.player = player;
    this.opponents = opponents;
  }

  public int numberOfEnemies() {
    return opponents.size();
  }

  // ...

นั่นเป็นเพราะ Mockito จะตรวจสอบType Signatureของชั้นเกมซึ่งเป็นและPlayerList<String>


16
ด้วยตัวอย่างนี้ควรเป็นคำตอบที่ยอมรับได้
AnnaKlein

4
ฉันคิดว่านี่เป็นสิ่งที่ดีที่สุดเช่นกัน
Evgeniy Dorofeev

4
ฉันคิดว่านี่เป็นคำตอบที่ดีที่สุดสำหรับสามคน
ฮาร์วีย์เดนท์

1
บางครั้งฉันพบว่าการทดสอบโดยการล้อเลียนนั้นยากที่จะเข้าใจและออกแบบสำหรับชั้นเรียน อย่างไรก็ตามตัวอย่างนี้ช่วยได้มากในการให้ภาพรวม
Chaklader Asfak Arefe

1
ขอบคุณมาก :) ถึงจุดนั้นด้วยคำอธิบายที่ดีกว่า
ฤishiษี

80

@InjectMocksในชั้นเรียนการทดสอบของคุณชั้นควรจะทดสอบข้อเขียนด้วย สิ่งนี้บอก Mockito ว่าคลาสไหนที่จะฉีด mocks ลงไป:

@InjectMocks
private SomeManager someManager;

จากนั้นเราสามารถระบุวิธีการเฉพาะหรือวัตถุในชั้นเรียนในกรณีนี้SomeManagerจะถูกแทนที่ด้วย mocks:

@Mock
private SomeDependency someDependency;

ในตัวอย่างนี้SomeDependencyภายในSomeManagerชั้นเรียนจะถูกเยาะเย้ย


6
จะใช้งานได้ไหมถ้ามีผู้จัดการบางคนมีคอนสตรัคเตอร์มากกว่าหนึ่งตัว เกิดอะไรขึ้นถ้าผู้จัดการบางคนมี 5 คอนสตรัคมันจะรู้ได้อย่างไรว่าอันไหนที่คุณต้องการใช้?
j2emanue

51

@Mock คำอธิบายประกอบ mocks วัตถุที่เกี่ยวข้อง

@InjectMocksคำอธิบายประกอบจะช่วยให้การฉีดเข้าไปในวัตถุต้นแบบที่แตกต่างกัน (และมีความเกี่ยวข้อง) mocks @Mockสร้างขึ้นโดย

ทั้งคู่เสริม


1
พวกเขาสามารถใช้ควบคู่บนวัตถุเดียวกันได้หรือไม่
IgorGanapolsky

1
คุณมีตัวอย่างเล็ก ๆ ของความต้องการของคุณหรือไม่?
Mik378

ฉันมีคลาสที่ต้องสอดแนม (ผ่าน Mockito Spy) และคลาสนี้มีคอนสตรัคเตอร์ ดังนั้นฉันจึงคิดที่@InjectMocksจะใช้สร้างคลาสนี้และสอดแนมในชั้นเรียนด้วย
IgorGanapolsky

1
นั่นคือสิ่งที่คุณกำลังมองหา? stackoverflow.com/a/35969166/985949
Mik378

23
  • @Mockสร้างการใช้งานจำลองสำหรับคลาสที่คุณต้องการ
  • @InjectMock สร้างตัวอย่างของการเรียนและการอัดฉีด mocks ที่มีการทำเครื่องหมายด้วยคำอธิบายประกอบที่@Mockเป็นมัน

ตัวอย่างเช่น

@Mock
StudentDao studentDao;

@InjectMocks
StudentService service;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
}

ที่นี่เราต้องการคลาส DAO สำหรับคลาสบริการ ดังนั้นเราจึงจำลองและอัดไว้ในอินสแตนซ์ของคลาสบริการ ในทำนองเดียวกันในกรอบฤดูใบไม้ผลิทั้งหมด@Autowiredถั่วสามารถหัวเราะเยาะ@Mockใน jUnits และฉีดเข้าไปในถั่วของคุณผ่าน @InjectMocks

MockitoAnnotations.initMocks(this)วิธีการเริ่มต้น mocks เหล่านี้และฉีดพวกเขาสำหรับทุกวิธีการทดสอบจึงจำเป็นต้องเรียกว่าในsetUp()วิธีการ

ลิงค์นี้มีบทช่วยสอนที่ดีสำหรับกรอบ Mockito


13

"กรอบการเยาะเย้ย" ซึ่ง Mockito ตั้งอยู่บนเป็นกรอบที่ช่วยให้คุณสามารถสร้างวัตถุจำลอง (ในแง่เก่าวัตถุเหล่านี้อาจเรียกว่า shunts ในขณะที่พวกเขาทำงานเป็น shunts สำหรับการพึ่งพาการทำงาน) ในคำอื่น ๆ ล้อเลียน วัตถุที่ใช้ในการเลียนแบบวัตถุจริงรหัสของคุณขึ้นอยู่กับคุณสร้างวัตถุพร็อกซี่ที่มีกรอบการเยาะเย้ย โดยการใช้วัตถุจำลองในการทดสอบของคุณคุณจะเริ่มจากการทดสอบหน่วยปกติไปจนถึงการทดสอบเชิงบูรณาการ

Mockito เป็นกรอบการทดสอบโอเพ่นซอร์สสำหรับ Java ที่เผยแพร่ภายใต้ MIT License มันเป็น "กรอบการเยาะเย้ย" ที่ช่วยให้คุณเขียนการทดสอบที่สวยงามด้วย API ที่สะอาดและเรียบง่าย มีกรอบการเยาะเย้ยที่แตกต่างกันมากมายในพื้นที่ของจาวาอย่างไรก็ตามโดยพื้นฐานแล้วจะมีสองประเภทหลักของกรอบวัตถุจำลองซึ่งมีการใช้งานผ่านพร็อกซีและคนที่นำมาใช้ผ่านการแมปคลาส

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

@InjectMocksบันทึกย่อพยายามที่จะยกตัวอย่างเช่นการทดสอบวัตถุและแทรกฟิลด์ข้อเขียนด้วย@Mockหรือ@Spyลงในเขตข้อมูลส่วนตัวของวัตถุทดสอบ

MockitoAnnotations.initMocks(this)โทรรีเซ็ตการทดสอบวัตถุและเริ่มต้น mocks อีกครั้งดังนั้นอย่าลืมที่จะมีสิ่งนี้ใน@Before/ @BeforeMethodหมายเหตุของคุณ


2
ฉันจะไม่พูดว่า "โดยการใช้วัตถุจำลองในการทดสอบของคุณคุณจะเริ่มจากการทดสอบหน่วยปกติไปจนถึงการทดสอบเชิงบูรณาการ" สำหรับฉันการเยาะเย้ยคือการแยกฟิกซ์เจอร์ทดสอบเพื่อทดสอบหน่วย การทดสอบการรวมจะใช้การอ้างอิงจริงที่ไม่ได้เยาะเย้ย
WesternGun

10

ข้อดีอย่างหนึ่งที่คุณได้รับจากวิธีการที่ @Tom กล่าวคือคุณไม่จำเป็นต้องสร้าง Constructor ใด ๆ ใน SomeManager และด้วยเหตุนี้จึงเป็นการ จำกัด ลูกค้าเพื่อสร้างอินสแตนซ์

@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {

    @InjectMocks
    private SomeManager someManager;

    @Mock
    private SomeDependency someDependency; // this will be injected into someManager

    //You don't need to instantiate the SomeManager with default contructor at all
   //SomeManager someManager = new SomeManager();    
   //Or SomeManager someManager = new SomeManager(someDependency);

     //tests...

}

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


เกิดอะไรขึ้นถ้าผู้จัดการบางคนมีคอนสตรัคเตอร์ต่างกัน 3 ตัวจะรู้ได้อย่างไรว่าจะใช้อันไหน
j2emanue

จากนั้นคุณจะตรวจสอบเนื้อหาในsomeManager ได้อย่างไรหากไม่ถูกเยาะเย้ย
IgorGanapolsky

5

หลายคนได้ให้คำอธิบายที่ดีที่นี่เกี่ยวกับVS@Mock @InjectMocksฉันชอบ @InjectMocksแต่ผมคิดว่าการทดสอบและโปรแกรมของเราควรจะเขียนในลักษณะดังกล่าวว่าเราไม่จำเป็นต้องใช้

ข้อมูลอ้างอิงสำหรับการอ่านเพิ่มเติมด้วยตัวอย่าง: https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/


1
อันนี้ดูเหมือนจะเป็นทางออกระยะยาว
Vinayak Dornala

4

@Mockใช้เพื่อประกาศ / จำลองการอ้างอิงของถั่วที่ต้องพึ่งพาขณะที่@InjectMocksใช้เพื่อจำลองถั่วที่จะทำการทดสอบ

ตัวอย่างเช่น:

public class A{

   public class B b;

   public void doSomething(){

   }

}

ทดสอบสำหรับชั้นเรียนA:

public class TestClassA{

   @Mocks
   public class B b;

   @InjectMocks
   public class A a;

   @Test
   public testDoSomething(){

   }

}

4

@InjectMocks คำอธิบายประกอบสามารถใช้ในการฉีดฟิลด์จำลองลงในวัตถุทดสอบโดยอัตโนมัติ

ในตัวอย่างด้านล่าง @InjectMocks ได้ใช้ในการฉีด mock dataMap ลงใน dataLibrary

@Mock
Map<String, String> dataMap ;

@InjectMocks
DataLibrary dataLibrary = new DataLibrary();


    @Test
    public void whenUseInjectMocksAnnotation_() {
        Mockito.when(dataMap .get("aData")).thenReturn("aMeaning");

        assertEquals("aMeaning", dataLibrary .getMeaning("aData"));
    }


3

แม้ว่าคำตอบข้างต้นได้ครอบคลุมฉันได้พยายามเพิ่มรายละเอียดนาทีที่ฉันเห็นหายไป เหตุผลเบื้องหลังพวกเขา (The Why)

ป้อนคำอธิบายรูปภาพที่นี่


ภาพประกอบ:

Sample.java
---------------
    public class Sample{
        DependencyOne dependencyOne;
        DependencyTwo dependencyTwo;


        public SampleResponse methodOfSample(){
            dependencyOne.methodOne();
            dependencyTwo.methodTwo();

            ...

            return sampleResponse;
        }
    }

SampleTest.java
-----------------------
@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassA.class})
public class SampleTest{

    @InjectMocks
    Sample sample;

    @Mock
    DependencyOne dependencyOne;

    @Mock
    DependencyTwo dependencyTwo;

    @Before
    public void init() {
        MockitoAnnotations.initMocks(this);
    }

    public void sampleMethod1_Test(){
        //Arrange the dependencies
        DependencyResponse dependencyOneResponse = Mock(sampleResponse.class);
        Mockito.doReturn(dependencyOneResponse).when(dependencyOne).methodOne();

        DependencyResponse dependencyTwoResponse = Mock(sampleResponse.class);
        Mockito.doReturn(dependencyOneResponse).when(dependencyTwo).methodTwo();

        //call the method to be tested
        SampleResponse sampleResponse = sample.methodOfSample() 

        //Assert
        <assert the SampleResponse here>
    }
}

การอ้างอิง

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