ฉีด Mockito mocks ลงใน Spring bean


284

ฉันต้องการฉีดวัตถุจำลอง Mockito ลงในถั่ว Spring (3+) เพื่อวัตถุประสงค์ในการทดสอบหน่วยกับ JUnit ขณะนี้การพึ่งพาถั่วของฉันถูกฉีดโดยใช้@Autowiredคำอธิบายประกอบในฟิลด์สมาชิกส่วนตัว

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

ฉันได้ทำตามคำแนะนำของชุมชน Spring แล้ว แต่การเยาะเย้ยไม่ได้เกิดขึ้นและการเดินสายอัตโนมัติล้มเหลว:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>

ข้อผิดพลาดที่ฉันพบในขณะนี้มีดังนี้:

...
Caused by: org...NoSuchBeanDefinitionException:
    No matching bean of type [com.package.Dao] found for dependency:
    expected at least 1 bean which qualifies as autowire candidate for this dependency.
    Dependency annotations: {
        @org...Autowired(required=true),
        @org...Qualifier(value=dao)
    }
at org...DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(D...y.java:901)
at org...DefaultListableBeanFactory.doResolveDependency(D...y.java:770)

หากฉันตั้งconstructor-argค่าเป็นสิ่งที่ไม่ถูกต้องจะไม่มีข้อผิดพลาดเกิดขึ้นเมื่อเริ่มต้นบริบทของแอปพลิเคชัน


4
โปรดดูสิ่งมีชีวิตตัวน้อยนี้: bitbucket.org/kubek2k/springockito/wiki/Home
kubek2k

นี่เป็นวิธีที่สะอาดมาก - ฉันชอบ!
teabot

2
คุณมีฉันที่บันทึกย่อของ Springockito
yihtserns


2
สำหรับผู้ที่ใช้สปริง 4 * ตั้งแต่เดือนมกราคม 2015 สิ่งนี้ดูเหมือนจะไม่ทำงานกับเวอร์ชั่นล่าสุดของ mockito ของฤดูใบไม้ผลิและโครงการดูเหมือนจะไม่ทำงาน
Murali

คำตอบ:


130

วิธีที่ดีที่สุดคือ:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock"> 
    <constructor-arg value="com.package.Dao" /> 
</bean> 

อัปเดต
ในไฟล์บริบทการเยาะเย้ยนี้จะต้องแสดงรายการก่อนที่จะมีฟิลด์ที่เป็นอัตโนมัติซึ่งขึ้นอยู่กับว่ามันถูกประกาศ


ฉันได้รับข้อผิดพลาด: "เกิดข้อผิดพลาดในการสร้าง bean ด้วยชื่อ 'mockito': นิยามของถั่วเป็นนามธรรม"
tttppp

4
@amra: ฤดูใบไม้ผลิไม่ได้อนุมานประเภทของวัตถุที่ส่งคืนในกรณีนี้ ... stackoverflow.com/q/6976421/306488
คิดเห็น

7
ไม่ทราบว่าเพราะเหตุใดคำตอบนี้จึงถูกอัปเดตมากเกินไปถั่วที่เกิดขึ้นนั้นไม่สามารถตอบรับอัตโนมัติได้เนื่องจากมีประเภทที่ไม่ถูกต้อง
azerole

4
มันสามารถ autowired ถ้ามันเป็นคนแรกที่ระบุในไฟล์บริบท (ก่อนเขต autowired ใด ๆ ที่จะขึ้นอยู่กับมันมีการประกาศ.)
ไรอันกำแพง

3
ตั้งแต่ฤดูใบไม้ผลิ 3.2 คำสั่งของถั่วไม่สำคัญอีกต่อไป ดูหัวข้อเรื่อง "วิธีการทั่วไปของโรงงาน" ในบล็อกนี้: spring.io/blog/2012/11/07/…
Ryan Walls

110
@InjectMocks
private MyTestObject testObject;

@Mock
private MyDependentObject mockedObject;

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

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


1
ฉันจะเริ่มต้นวิธีการเฉพาะได้mockedObjectอย่างไร
จิมโฮลเดน

@Teinacher เมื่อ (mockedObject.execute) แล้วกลับ (objToReturn); คุณสามารถใส่ได้ทั้งในวิธีก่อนหรือภายในวิธีการทดสอบของคุณ
chaostheory

40
FYI: วิธีการนี้ใช้ไม่ได้หากฉันต้องการ Autowiring บางส่วนและเยาะเย้ยบางส่วนใน MyTestObject
raksja

9
ฉันไม่รู้ว่าทำไมสิ่งนี้ถึงไม่ได้รับการโหวตสูงกว่า หากฉันเห็นคำตอบเพิ่มเติมใด ๆ ที่มี XML ฉันจะพุ่ง
MarkOfHall

3
ทำไมคุณไม่ใช้Mockito.spy(...)สิ่งนี้mockedObjectแทน แล้วใช้หรือwhen(mockedObject.execute).thenReturn(objToReturn) doReturn(objToReturn).when(mockedObject).execute()วิธีที่สองไม่เรียกใช้วิธีการจริง คุณสามารถตรวจสอบMockito.doCallRealMethod()เอกสารประกอบ
Tomasz Przybylski

63

ฉันมีวิธีง่ายๆในการใช้ Spring Java Config และ Mockito:

@Configuration
public class TestConfig {

    @Mock BeanA beanA;
    @Mock BeanB beanB;

    public TestConfig() {
        MockitoAnnotations.initMocks(this); //This is a key
    }

    //You basically generate getters and add @Bean annotation everywhere
    @Bean
    public BeanA getBeanA() {
        return beanA;
    }

    @Bean
    public BeanB getBeanB() {
        return beanB;
    }
}

4
ด้วยเหตุผลบางอย่างด้วยวิธีนี้สปริงจึงพยายามสร้างถั่วจริง (แทนการเยาะเย้ย) และทำให้หายใจไม่ออก ... ฉันกำลังทำอะไรผิด
Daniel Gruszczyk

1
ฉันมีปัญหาเดียวกัน
Korobko Alex

3
ไม่ใช่สปริง แต่เป็น mockito พยายามอินสแตนซ์ถั่วจริงถ้าคุณเยาะเย้ยคลาส หากคุณมีถั่วใด ๆ ที่จะต้องถูกเยาะเย้ยในการทดสอบพวกเขาควรจะใช้งานของอินเตอร์เฟซและฉีดผ่านอินเตอร์เฟซที่ หากคุณจำลองอินเทอร์เฟซ (ไม่ใช่คลาส) mockito จะไม่พยายามยกระดับคลาสนั้น
Daniel Gruszczyk

7
ประเด็นคืออะไร? เพิ่มสาขาข้อเขียนและตัวสร้างทำไมกับinitMocks? ทำไมไม่เพียงreturn Mockito.mock(BeanA.class)ในgetBeanA? วิธีนี้ง่ายกว่าและมีรหัสน้อยกว่า ฉันพลาดอะไรไป
Oleg

1
@Oleg ดูเหมือนว่าคุณมีทางออกของคุณเองซึ่งคุณควรโพสต์เป็นคำตอบเพื่อให้ชุมชนสามารถลงคะแนนได้
Dawood ibn Kareem

48

ได้รับ:

@Service
public class MyService {
    @Autowired
    private MyDAO myDAO;

    // etc
}

คุณสามารถมีคลาสที่ถูกทดสอบโหลดผ่านการ autowiring จำลองการพึ่งพา Mockito แล้วใช้ ReflectionTestUtils ของ Spring เพื่อฉีดจำลองลงในคลาสที่กำลังทดสอบ

@ContextConfiguration(classes = { MvcConfiguration.class })
@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
    @Autowired
    private MyService myService;

    private MyDAO myDAOMock;

    @Before
    public void before() {
        myDAOMock = Mockito.mock(MyDAO.class);
        ReflectionTestUtils.setField(myService, "myDAO", myDAOMock);
    }

    // etc
}

โปรดทราบว่าก่อนฤดูใบไม้ผลิ 4.3.1 วิธีนี้จะไม่ทำงานกับบริการที่อยู่เบื้องหลังพร็อกซี (หมายเหตุประกอบกับ@TransactionalหรือCacheableตัวอย่าง) สิ่งนี้ได้รับการแก้ไขโดยSPR-14050 SPR-14050

สำหรับรุ่นก่อนหน้าวิธีแก้ปัญหาคือการแกะพร็อกซีดังที่อธิบายไว้ที่นี่: คำอธิบายประกอบธุรกรรมเพื่อหลีกเลี่ยงบริการที่ถูกเยาะเย้ย (ซึ่งเป็นสิ่งที่ReflectionTestUtils.setFieldทำตามค่าเริ่มต้นแล้ว)


Double @RunWith (SpringJUnit4ClassRunner.class) และฉันใช้คำอธิบายประกอบที่แตกต่างกันสำหรับคลาสทดสอบ (นักวิ่งเดียวกัน) แต่วิธีการนี้ใช้ได้ผลสำหรับฉันขอบคุณ
user1317422

1
ฉันได้รับแรงบันดาลใจมากมายจาก "โปรดทราบว่าก่อนฤดูใบไม้ผลิ 4.3.1 วิธีนี้จะไม่ทำงานกับบริการหลังพร็อกซี (เช่นมีคำอธิบายประกอบด้วย @Transactional หรือ Cacheable เป็นต้น) ซึ่งได้รับการแก้ไขโดย SPR-14050" ฉันเพิ่งพบปัญหานี้อย่างแน่นอนและไม่ได้เบาะแสใด ๆ จนกว่าจะพบคำนี้ ขอบคุณมาก!
snowfox

1
โซลูชันนี้จัดการเมื่อคุณเชื่อมต่อบริบทของแอปพลิเคชันทั้งหมดและเพื่อวัตถุประสงค์ในการทดสอบต้องการฉีดจำลองใน bean แบบสุ่มในบริบทของคุณ ฉันใช้คำตอบนี้เพื่อเยาะเย้ยลูกค้าถั่วปลอมเพื่อหลีกเลี่ยงการเรียกส่วนที่เหลือไปยังโมดูลอื่น ๆ ในการทดสอบโมดูล ฉันได้รับหมายเหตุประกอบ InjectMock เท่านั้นที่จะทำงานเมื่อคุณฉีด mocks ใน bean ที่คุณกำลังจะทดสอบไม่ใช่ใน bean ที่สร้างโดย Spring Application Configuration
Andreas Lundgren

1
เกือบทั้งวันมีความพยายามที่จะทำให้ @MockBean ทำงานโดยไม่รีเซ็ตบริบทจากนั้นฉันก็เจออัญมณีนี้ สิ่งที่ฉันต้องการคือเสียงเชียร์
Matt R

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

36

หากคุณใช้ Spring Boot 1.4 มันเป็นวิธีที่ยอดเยี่ยมในการทำสิ่งนี้ เพียงแค่ใช้แบรนด์ใหม่@SpringBootTestในชั้นเรียนของคุณและ@MockBeanในเขตข้อมูลและ Spring Boot จะสร้างจำลองประเภทนี้และมันจะฉีดเข้าไปในบริบท (แทนการฉีดของเดิม):

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {

    @MockBean
    private RemoteService remoteService;

    @Autowired
    private Reverser reverser;

    @Test
    public void exampleTest() {
        // RemoteService has been injected into the reverser bean
        given(this.remoteService.someCall()).willReturn("mock");
        String reverse = reverser.reverseSomeCall();
        assertThat(reverse).isEqualTo("kcom");
    }

}

ในทางกลับกันถ้าคุณไม่ได้ใช้ Spring Boot หรือคุณกำลังใช้รุ่นก่อนหน้าคุณจะต้องทำงานอีกเล็กน้อย:

สร้าง@Configurationbean ที่แทรก mocks ของคุณในบริบท Spring:

@Configuration
@Profile("useMocks")
public class MockConfigurer {

    @Bean
    @Primary
    public MyBean myBeanSpy() {
        return mock(MyBean.class);
    }
}

การใช้ @Primaryคำอธิบายประกอบคุณกำลังบอกสปริงว่า bean นี้มีลำดับความสำคัญหากไม่มีการระบุตัวระบุ

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

ในการทดสอบของคุณให้เปิดใช้งานuserMocksโปรไฟล์:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
@ActiveProfiles(profiles={"useMocks"})
public class YourIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the mock!


    @Test
    public void test() {
        ....
    }
}

หากคุณไม่ต้องการใช้จำลอง แต่เป็นถั่วจริงเพียงแค่เปิดใช้งานuseMocksโปรไฟล์:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebIntegrationTest
public class AnotherIntegrationTestIT {

    @Inject
    private MyBean myBean; //It will be the real implementation!


    @Test
    public void test() {
        ....
    }
}

5
คำตอบนี้ควรไปที่ด้านบน - การสนับสนุน @MockBean ใน spring boot สามารถใช้งานได้โดยไม่ต้อง spring-boot คุณสามารถใช้ในการทดสอบหน่วยเท่านั้นจึงใช้งานได้กับแอปพลิเคชันสปริงทั้งหมด!
bedrin

2
คำอธิบายประกอบ @Profile คุณสามารถตั้งค่าบนวิธีการกำหนด bean เพื่อหลีกเลี่ยงการสร้างคลาสการกำหนดค่าแยกต่างหาก
marcin

คำตอบที่ดี! ฉันทำการเปลี่ยนแปลงเล็กน้อยเพื่อให้ทำงานกับโรงเรียนเก่าweb.xmlและการตั้งค่า AnnotationConfigWebApplicationContext ต้องใช้@WebAppConfigurationแทน@WebIntegrationTestและ@ContextHierarchyมีแทน@ContextConfiguration @SpringApplicationConfiguration
UTF_or_Death

ฉันต้องเพิ่ม@Primaryคำอธิบายประกอบสำหรับกรณีของฉันเนื่องจากมีการโทรที่ล้มเหลวภายใน@PostConstructที่ฉันต้องการที่จะเยาะเย้ย แต่@PostConstructถั่วของถูกสร้างขึ้นก่อนที่จะเยาะเย้ยฉันจึงไม่ได้ใช้การเยาะเย้ย (จนกว่าฉันจะเพิ่ม@Primary)
helleye

19

ตั้งแต่1.8.3 Mockito มี@InjectMocks- มันมีประโยชน์อย่างเหลือเชื่อ การทดสอบ JUnit ของฉันและฉันสร้างวัตถุที่ตอบสนองทุกการอ้างอิงสำหรับการเรียนที่ถูกทดสอบซึ่งจะฉีดทั้งหมดเมื่อสมาชิกส่วนตัวมีการกำกับด้วย@RunWithMockitoJUnitRunner@Mock@InjectMocks

ฉัน@RunWithSpringJUnit4Runnerสำหรับการทดสอบการรวมเฉพาะตอนนี้

ฉันจะทราบว่ามันดูเหมือนจะไม่สามารถฉีดList<T>ในลักษณะเดียวกับฤดูใบไม้ผลิ มันจะค้นหาเฉพาะวัตถุจำลองที่เป็นไปตามListและจะไม่ฉีดรายการวัตถุจำลอง วิธีแก้ปัญหาสำหรับฉันคือการใช้@Spyกับรายการ instantiated ด้วยตนเองและด้วยตนเอง. เพิ่มวัตถุจำลองไปยังรายการสำหรับการทดสอบหน่วย อาจเป็นเพราะเจตนาเพราะแน่นอนว่าบังคับให้ฉันต้องใส่ใจกับสิ่งที่ถูกล้อเลียนด้วยกัน


ใช่นี่เป็นวิธีที่ดีที่สุด Springockito ไม่ได้ฉีด mocks ด้วยเหตุผลอะไรก็ตามในกรณีของฉัน
chaostheory

13

อัปเดต:ขณะนี้มีวิธีที่ดีกว่าและสะอาดกว่าสำหรับการแก้ไขปัญหานี้ โปรดพิจารณาคำตอบอื่น ๆ ก่อน

ในที่สุดฉันก็พบคำตอบนี้โดยโรนันในบล็อกของเขา ปัญหาผมมีเป็นเพราะวิธีการประกาศประเภทการกลับมาของMockito.mock(Class c) Objectดังนั้น Spring ไม่สามารถอนุมานชนิด bean จากชนิดการคืนเมธอดของ factory

วิธีแก้ปัญหาของ Ronenคือการสร้างการFactoryBeanใช้งานที่ส่งกลับ mocks FactoryBeanอินเตอร์เฟซที่ช่วยให้ฤดูใบไม้ผลิในแบบสอบถามชนิดของวัตถุที่สร้างขึ้นโดยถั่วโรงงาน

คำจำกัดความล้อเลียนของฉันตอนนี้ดูเหมือนว่า:

<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>

1
อัปเดตลิงก์ไปยังโซลูชันของ Ronen: narkisr.com/blog/2008/2647754885089732945
Jeff Martin

ฉันไม่เข้าใจว่าวิธีการที่โรงงานมีประเภทการคืนค่าวัตถุ ... แต่วิธีการแก้ปัญหาของ amra มีประเภทผลตอบแทนทั่วไปเพื่อให้ฤดูใบไม้ผลิควรรับรู้ ... แต่วิธีการแก้ปัญหาของ amra ไม่ได้ผลสำหรับฉัน
lisak

ฤดูใบไม้ผลิไม่ได้สรุปประเภทของถั่วที่ส่งคืนจากโรงงานถั่วดังนั้นจึงไม่มีถั่วที่ตรงกันประเภท [com.package.Dao] ...
คิดเห็น

1
เครื่องถอยกลับ: web.archive.org/web/20120806223839/http://…
Daniel Kaplan

ลิงก์นี้ยังใช้งานได้จริง: javadevelopmentforthemasses.blogspot.com/2008/07/…เพียงปิดการใช้งานการเปลี่ยนเส้นทางลิงก์ในเบราว์เซอร์ของคุณและคุณจะเห็นมันแทนที่จะถูกบังคับให้ดู 404 ในบล็อกใหม่ของเขา
ประมาณ

12

ตั้งแต่ Spring 3.2 นี่ไม่มีปัญหาอีกต่อไป Spring สนับสนุนการ Autowiring ผลลัพธ์ของวิธีการทั่วไปของโรงงาน ดูหัวข้อ "ทั่วไปโรงงานวิธีการ" ในการโพสต์บล็อกนี้: http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/

จุดสำคัญคือ:

ในฤดูใบไม้ผลิ 3.2 ประเภทการคืนสินค้าทั่วไปสำหรับวิธีการของโรงงานได้รับการอนุมานอย่างเหมาะสมและการลงทะเบียนอัตโนมัติตามประเภทสำหรับ mocks ควรทำงานตามที่คาดไว้ ด้วยเหตุนี้การแก้ไขปัญหาที่กำหนดเองเช่น MockitoFactoryBean, EasyMockFactoryBean หรือ Springockito จึงไม่มีความจำเป็นอีกต่อไป

ซึ่งหมายความว่าควรทำงานนอกกรอบ:

<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.package.Dao" />
</bean>

9

โค้ดด้านล่างใช้งานได้กับการเรียกใช้อัตโนมัติ - ไม่ใช่เวอร์ชันที่สั้นที่สุด แต่มีประโยชน์เมื่อมันควรใช้กับไหสปริง / ม็อกโตโตะมาตรฐานเท่านั้น

<bean id="dao" class="org.springframework.aop.framework.ProxyFactoryBean">
   <property name="target"> <bean class="org.mockito.Mockito" factory-method="mock"> <constructor-arg value="com.package.Dao" /> </bean> </property>
   <property name="proxyInterfaces"> <value>com.package.Dao</value> </property>
</bean> 

ทำงานให้ฉัน ฉันต้องแกะพร็อกซี่ในการทดสอบของฉันเพื่อตรวจสอบตามที่อธิบายไว้ที่นี่: forum.spring.io/forum/spring-projects/aop/ …
Holgzn

9

หากคุณกำลังใช้สปริง> = 3.0ให้ลองใช้@Configurationคำอธิบายประกอบของสปริงเพื่อกำหนดส่วนของบริบทแอปพลิเคชัน

@Configuration
@ImportResource("com/blah/blurk/rest-of-config.xml")
public class DaoTestConfiguration {

    @Bean
    public ApplicationService applicationService() {
        return mock(ApplicationService.class);
    }

}

หากคุณไม่ต้องการใช้ @ImportResource ก็สามารถทำได้เช่นกัน:

<beans>
    <!-- rest of your config -->

    <!-- the container recognize this as a Configuration and adds it's beans 
         to the container -->
    <bean class="com.package.DaoTestConfiguration"/>
</beans>

สำหรับข้อมูลเพิ่มเติมให้ดูที่การอ้างอิงสปริงเฟรมเวิร์ก: การกำหนดค่าคอนเทนเนอร์ที่ใช้ Java


ทำได้ดีนี่. ฉันใช้สิ่งนี้เมื่อการทดสอบที่ฉันทดสอบคือ @ อัตโนมัติในกรณีทดสอบจริง
enkor

8

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


3
ฉันเข้าใจแนวทางของคุณ อย่างไรก็ตามฉันพบว่าตัวเองอยู่ในสถานการณ์เช่นนี้บนฐานรหัสขนาดใหญ่ที่ไม่อนุญาตให้ทำเช่นนี้ได้อย่างง่ายดาย
teabot

1
ฉันพบว่าคำสั่งผสม Mockito / Spring มีประโยชน์อย่างมากเมื่อฉันต้องการทดสอบโค้ดที่ต้องอาศัย Spring เป็นอย่างมาก / AOP (เช่นเมื่อทดสอบกฎความปลอดภัยของสปริง) แม้ว่าจะมีข้อพิสูจน์ที่สมบูรณ์ในการอ้างว่าการทดสอบดังกล่าวควรเป็นการทดสอบการรวมเข้าด้วยกัน
Lars Tackmann

@ ลาร์ส - ตกลง - สามารถพูดแบบเดียวกันกับการทดสอบที่ฉันจัดการได้
teabot

7

ฉันสามารถทำสิ่งต่อไปนี้โดยใช้ Mockito:

<bean id="stateMachine" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.abcd.StateMachine"/>
</bean>

1
ขอบคุณสำหรับคำตอบ @Alexander ฉันขอ: มันถูกสายได้อย่างถูกต้อง? คุณใช้ Spring / Mockito รุ่นใดอยู่
teabot

6

การโพสต์ตัวอย่างเล็ก ๆ น้อย ๆ ตามวิธีการข้างต้น

กับฤดูใบไม้ผลิ:

@ContextConfiguration(locations = { "classpath:context.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService;
    @Mock
    private TestService2 testService2;
}

ไม่มีฤดูใบไม้ผลิ:

@RunWith(MockitoJUnitRunner.class)
public class TestServiceTest {
    @InjectMocks
    private TestService testService = new TestServiceImpl();
    @Mock
    private TestService2 testService2;
}

2

อัปเดต - คำตอบใหม่ที่นี่: https://stackoverflow.com/a/19454282/411229 https://stackoverflow.com/a/19454282/411229คำตอบนี้ใช้ได้เฉพาะกับคำว่า Spring เท่านั้นก่อน 3.2

ฉันได้มองหาคำตอบที่ชัดเจนกว่านี้ โพสต์บล็อกนี้ดูเหมือนจะครอบคลุมทุกความต้องการของฉันและไม่พึ่งพาการสั่งซื้อประกาศถั่ว เครดิตทั้งหมดให้กับ Mattias Severson http://www.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects/

โดยทั่วไปใช้ FactoryBean

package com.jayway.springmock;

import org.mockito.Mockito;
import org.springframework.beans.factory.FactoryBean;

/**
 * A {@link FactoryBean} for creating mocked beans based on Mockito so that they 
 * can be {@link @Autowired} into Spring test configurations.
 *
 * @author Mattias Severson, Jayway
 *
 * @see FactoryBean
 * @see org.mockito.Mockito
 */
public class MockitoFactoryBean<T> implements FactoryBean<T> {

    private Class<T> classToBeMocked;

    /**
     * Creates a Mockito mock instance of the provided class.
     * @param classToBeMocked The class to be mocked.
     */
    public MockitoFactoryBean(Class<T> classToBeMocked) {
        this.classToBeMocked = classToBeMocked;
    }

    @Override
    public T getObject() throws Exception {
        return Mockito.mock(classToBeMocked);
    }

    @Override
    public Class<?> getObjectType() {
        return classToBeMocked;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

ถัดไปอัปเดตการกำหนดค่าสปริงด้วยสิ่งต่อไปนี้:

<beans...>
    <context:component-scan base-package="com.jayway.example"/>

    <bean id="someDependencyMock" class="com.jayway.springmock.MockitoFactoryBean">
        <constructor-arg name="classToBeMocked" value="com.jayway.example.SomeDependency" />
    </bean>
</beans>

2

ดูการพัฒนาของ Springockitoและจำนวนปัญหาเปิดฉันจะกังวลเล็กน้อยที่จะแนะนำในชุดทดสอบของฉันในปัจจุบัน ข้อเท็จจริงที่ว่าการเปิดตัวครั้งสุดท้ายเสร็จสิ้นก่อนที่ Spring 4 จะปล่อยคำถามเช่น "เป็นไปได้หรือไม่ที่จะรวมเข้ากับ Spring 4 ได้อย่างง่ายดาย" ฉันไม่รู้เพราะฉันไม่ได้ลอง ฉันชอบวิธีการสปริงบริสุทธิ์ถ้าฉันต้องการจำลองการทดสอบการรวมตัว

มีตัวเลือกในการปลอมถั่ว Spring ฤดูใบไม้ผลิที่มีคุณสมบัติเพียงธรรมดาคือ คุณจำเป็นต้องใช้@Primary, @Profileและ@ActiveProfilesคำอธิบายประกอบสำหรับมัน ฉันเขียนโพสต์บล็อกในหัวข้อ


1

ฉันพบคำตอบที่คล้ายกันเป็น teabot เพื่อสร้าง MockFactory ที่ให้ mocks ฉันใช้ตัวอย่างต่อไปนี้เพื่อสร้างโรงงานจำลอง (เนื่องจากลิงก์ไปยัง narkisr ตายแล้ว): http://hg.randompage.org/java/src/407e78aa08a0/projects/bookmarking/backend/spring/src/test/java/ org / randompage / บุ๊คมาร์ค / แบ็กเอนด์ / testUtils / MocksFactory.java

<bean id="someFacade" class="nl.package.test.MockFactory">
    <property name="type" value="nl.package.someFacade"/>
</bean>

สิ่งนี้ยังช่วยป้องกันไม่ให้สปริงต้องการแก้ไขการฉีดจากถั่วที่เยาะเย้ย


1
<bean id="mockDaoFactory" name="dao" class="com.package.test.MocksFactory">
    <property name="type" value="com.package.Dao" />
</bean>

^ นี้ทำงานได้อย่างสมบูรณ์แบบหากประกาศเป็นครั้งแรก / ต้นในไฟล์ XML Mockito 1.9.0 / Spring 3.0.5


1

ฉันใช้การผสมผสานของวิธีการที่ใช้ในการตอบโดย Markus T และการใช้งานตัวช่วยอย่างง่ายImportBeanDefinitionRegistrarซึ่งจะมีคำอธิบายประกอบแบบกำหนดเอง ( @MockedBeans) ซึ่งสามารถระบุได้ว่าจะให้คลาสใดถูกเยาะเย้ย ฉันเชื่อว่าวิธีการนี้ส่งผลให้มีการทดสอบหน่วยโดยย่อด้วยรหัสสำเร็จรูปบางส่วนที่เกี่ยวข้องกับการเยาะเย้ยลบออก

นี่คือลักษณะตัวอย่างของการทดสอบหน่วยด้วยวิธีการดังกล่าว:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class ExampleServiceIntegrationTest {

    //our service under test, with mocked dependencies injected
    @Autowired
    ExampleService exampleService;

    //we can autowire mocked beans if we need to used them in tests
    @Autowired
    DependencyBeanA dependencyBeanA;

    @Test
    public void testSomeMethod() {
        ...
        exampleService.someMethod();
        ...
        verify(dependencyBeanA, times(1)).someDependencyMethod();
    }

    /**
     * Inner class configuration object for this test. Spring will read it thanks to
     * @ContextConfiguration(loader=AnnotationConfigContextLoader.class) annotation on the test class.
     */
    @Configuration
    @Import(TestAppConfig.class) //TestAppConfig may contain some common integration testing configuration
    @MockedBeans({DependencyBeanA.class, DependencyBeanB.class, AnotherDependency.class}) //Beans to be mocked
    static class ContextConfiguration {

        @Bean
        public ExampleService exampleService() {
            return new ExampleService(); //our service under test
        }
    }
}

ในการทำให้สิ่งนี้เกิดขึ้นคุณต้องกำหนดคลาสตัวช่วยอย่างง่ายสองคลาส - หมายเหตุประกอบแบบกำหนดเอง ( @MockedBeans) และการImportBeanDefinitionRegistrarใช้งานแบบกำหนดเอง @MockedBeansคำอธิบายประกอบคำอธิบายประกอบจะต้องมีคำอธิบายประกอบด้วย@Import(CustomImportBeanDefinitionRegistrar.class)และImportBeanDefinitionRgistrarความต้องการที่จะเพิ่มคำจำกัดความถั่วจำลองไปยังการกำหนดค่าในregisterBeanDefinitionsวิธีมัน

ถ้าคุณชอบวิธีการที่คุณสามารถหาตัวอย่างการใช้งานบนของบล็อกโพสต์


1

ฉันพัฒนาวิธีแก้ปัญหาตามข้อเสนอของ Kresimir Nesek ฉันได้เพิ่มคำอธิบายประกอบใหม่@EnableMockedBeanเพื่อให้โค้ดเป็นบิตที่สะอาดและเป็นโมดูล

@EnableMockedBean
@SpringBootApplication
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes=MockedBeanTest.class)
public class MockedBeanTest {

    @MockedBean
    private HelloWorldService helloWorldService;

    @Autowired
    private MiddleComponent middleComponent;

    @Test
    public void helloWorldIsCalledOnlyOnce() {

        middleComponent.getHelloMessage();

        // THEN HelloWorldService is called only once
        verify(helloWorldService, times(1)).getHelloMessage();
    }

}

ฉันเขียนโพสต์เพื่ออธิบาย


1

ฉันขอแนะนำให้ย้ายโครงการของคุณไปที่ Spring Boot 1.4 หลังจากนั้นคุณสามารถใช้คำอธิบายประกอบใหม่@MockBeanเพื่อปลอมของคุณcom.package.Dao


0

วันนี้ฉันพบว่าบริบทของฤดูใบไม้ผลิที่ฉันประกาศก่อนถั่ว Mockito ไม่สามารถโหลดได้ หลังจากย้าย AFTER ไปที่ mocks บริบทของแอปจะถูกโหลดสำเร็จ ดูแล :)


1
มีบางอย่างขาดหายไป 8-) คุณเคลื่อนไหวอะไรหลังจาก mocks?
Hans-Peter Störr

0

สำหรับบันทึกการทดสอบทั้งหมดของฉันทำงานอย่างถูกต้องเพียงแค่ทำการฟิกซ์เจอร์สันหลังยาวที่เริ่มต้นได้เช่น:

<bean id="fixture"
      class="it.tidalwave.northernwind.rca.embeddedserver.impl.DefaultEmbeddedServer"
      lazy-init="true" /> <!-- To solve Mockito + Spring problems -->

<bean class="it.tidalwave.messagebus.aspect.spring.MessageBusAdapterFactory" />

<bean id="applicationMessageBus"
      class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="it.tidalwave.messagebus.MessageBus" />
</bean>

<bean class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="javax.servlet.ServletContext" />
</bean>

ฉันคิดว่าเหตุผลเป็นหนึ่งใน Mattias อธิบายที่นี่ (ที่ด้านล่างของโพสต์) ว่าการแก้ปัญหาจะเปลี่ยนลำดับถั่วประกาศ - เริ่มต้นขี้เกียจคือ "การเรียงลำดับ" มีการติดตั้งประกาศในตอนท้าย


-1

หากคุณใช้ Controller Injection ตรวจสอบให้แน่ใจว่าตัวแปรในเครื่องของคุณไม่ใช่ "ขั้นสุดท้าย"

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