อะไรคือความแตกต่างระหว่าง@Mock
และ@InjectMocks
ในกรอบ Mockito?
อะไรคือความแตกต่างระหว่าง@Mock
และ@InjectMocks
ในกรอบ Mockito?
คำตอบ:
@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...
}
นี่คือตัวอย่างโค้ดเกี่ยวกับวิธีการ@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
ระดับความต้องการที่จะดำเนินการPlayer
attack
@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
วิธีการ สุดท้ายการใช้@InjectMocks
Mockito จะนำสิ่งนั้น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
ของชั้นเกมซึ่งเป็นและPlayer
List<String>
@InjectMocks
ในชั้นเรียนการทดสอบของคุณชั้นควรจะทดสอบข้อเขียนด้วย สิ่งนี้บอก Mockito ว่าคลาสไหนที่จะฉีด mocks ลงไป:
@InjectMocks
private SomeManager someManager;
จากนั้นเราสามารถระบุวิธีการเฉพาะหรือวัตถุในชั้นเรียนในกรณีนี้SomeManager
จะถูกแทนที่ด้วย mocks:
@Mock
private SomeDependency someDependency;
ในตัวอย่างนี้SomeDependency
ภายในSomeManager
ชั้นเรียนจะถูกเยาะเย้ย
@Mock
คำอธิบายประกอบ mocks วัตถุที่เกี่ยวข้อง
@InjectMocks
คำอธิบายประกอบจะช่วยให้การฉีดเข้าไปในวัตถุต้นแบบที่แตกต่างกัน (และมีความเกี่ยวข้อง) mocks @Mock
สร้างขึ้นโดย
ทั้งคู่เสริม
@InjectMocks
จะใช้สร้างคลาสนี้และสอดแนมในชั้นเรียนด้วย
ตัวอย่างเช่น
@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 ตั้งอยู่บนเป็นกรอบที่ช่วยให้คุณสามารถสร้างวัตถุจำลอง (ในแง่เก่าวัตถุเหล่านี้อาจเรียกว่า shunts ในขณะที่พวกเขาทำงานเป็น shunts สำหรับการพึ่งพาการทำงาน) ในคำอื่น ๆ ล้อเลียน วัตถุที่ใช้ในการเลียนแบบวัตถุจริงรหัสของคุณขึ้นอยู่กับคุณสร้างวัตถุพร็อกซี่ที่มีกรอบการเยาะเย้ย โดยการใช้วัตถุจำลองในการทดสอบของคุณคุณจะเริ่มจากการทดสอบหน่วยปกติไปจนถึงการทดสอบเชิงบูรณาการ
Mockito เป็นกรอบการทดสอบโอเพ่นซอร์สสำหรับ Java ที่เผยแพร่ภายใต้ MIT License มันเป็น "กรอบการเยาะเย้ย" ที่ช่วยให้คุณเขียนการทดสอบที่สวยงามด้วย API ที่สะอาดและเรียบง่าย มีกรอบการเยาะเย้ยที่แตกต่างกันมากมายในพื้นที่ของจาวาอย่างไรก็ตามโดยพื้นฐานแล้วจะมีสองประเภทหลักของกรอบวัตถุจำลองซึ่งมีการใช้งานผ่านพร็อกซีและคนที่นำมาใช้ผ่านการแมปคลาส
เฟรมเวิร์กการฉีดพึ่งพาเช่นสปริงอนุญาตให้คุณฉีดวัตถุพร็อกซีของคุณโดยไม่ต้องแก้ไขโค้ดใด ๆ วัตถุจำลองคาดว่าจะเรียกวิธีการบางอย่างและจะส่งคืนผลลัพธ์ที่คาดหวัง
@InjectMocks
บันทึกย่อพยายามที่จะยกตัวอย่างเช่นการทดสอบวัตถุและแทรกฟิลด์ข้อเขียนด้วย@Mock
หรือ@Spy
ลงในเขตข้อมูลส่วนตัวของวัตถุทดสอบ
MockitoAnnotations.initMocks(this)
โทรรีเซ็ตการทดสอบวัตถุและเริ่มต้น mocks อีกครั้งดังนั้นอย่าลืมที่จะมีสิ่งนี้ใน@Before
/ @BeforeMethod
หมายเหตุของคุณ
ข้อดีอย่างหนึ่งที่คุณได้รับจากวิธีการที่ @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...
}
ไม่ว่าจะเป็นแนวปฏิบัติที่ดีหรือไม่ขึ้นอยู่กับการออกแบบแอปพลิเคชันของคุณ
หลายคนได้ให้คำอธิบายที่ดีที่นี่เกี่ยวกับVS@Mock
@InjectMocks
ฉันชอบ @InjectMocks
แต่ผมคิดว่าการทดสอบและโปรแกรมของเราควรจะเขียนในลักษณะดังกล่าวว่าเราไม่จำเป็นต้องใช้
ข้อมูลอ้างอิงสำหรับการอ่านเพิ่มเติมด้วยตัวอย่าง: https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/
@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(){
}
}
@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"));
}
ขอให้สังเกตว่า@InjectMocks
กำลังจะเลิก
เลิกใช้ @InjectMocks และกำหนดเวลาเพื่อนำออกใน Mockito 3/4
และคุณสามารถติดตาม @avp คำตอบและลิงก์ใน:
ทำไมคุณไม่ควรใช้ InjectMocks Annotation กับ Autowire Fields
แม้ว่าคำตอบข้างต้นได้ครอบคลุมฉันได้พยายามเพิ่มรายละเอียดนาทีที่ฉันเห็นหายไป เหตุผลเบื้องหลังพวกเขา (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>
}
}