Mockito: Tiêm các đối tượng thực vào các trường riêng tư @Autowired


190

Tôi đang sử dụng các chú thích @Mock@InjectMockschú thích của Mockito để đưa các phụ thuộc vào các trường riêng được chú thích với Spring @Autowired:

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

    @InjectMocks
    private Demo demo;

    /* ... */
}

public class Demo {

    @Autowired
    private SomeService service;

    /* ... */
}

Bây giờ tôi cũng muốn đưa các đối tượng thực vào các @Autowiredtrường riêng (không có setters). Điều này là có thể hay chỉ là cơ chế giới hạn trong việc tiêm Mocks?


5
Thông thường khi bạn chế giễu mọi thứ, điều đó có nghĩa là bạn không quan tâm nhiều đến vật thể cụ thể; rằng bạn chỉ thực sự quan tâm đến hành vi của đối tượng bị chế giễu. Có lẽ bạn muốn làm một bài kiểm tra tích hợp thay thế? Hoặc, bạn có thể cung cấp một lý do về lý do tại sao bạn muốn các đối tượng cụ thể và bị chế giễu sống cùng nhau?
Makoto

2
Chà, tôi đang xử lý mã kế thừa và sẽ mất rất nhiều thời gian (...). Sau đóReturn (...) để thiết lập giả chỉ để ngăn một số NPE và tương tự. Mặt khác, một đối tượng thực sự có thể được sử dụng một cách an toàn cho việc này. Vì vậy, sẽ rất thuận tiện khi có một tùy chọn để tiêm các vật thật cùng với giả. Ngay cả khi đây có thể là một mùi mã, tôi cho rằng nó hợp lý trong trường hợp cụ thể này.
dùng2286693

Đừng quên MockitoAnnotations.initMocks(this);trong @Beforephương pháp. Tôi biết nó không liên quan trực tiếp đến câu hỏi ban đầu, nhưng với bất kỳ ai đến sau, điều đó sẽ cần phải được thêm vào để làm cho câu hỏi này có thể chạy được.
Cuga

7
@Cuga: nếu bạn sử dụng trình chạy Mockito cho JUnit, ( @RunWith(MockitoJUnitRunner.class)), bạn không cần dòngMockitoAnnotations.initMocks(this);
Clint Eastwood

1
Cảm ơn-- Tôi chưa bao giờ biết điều đó và luôn chỉ định cả hai
Cuga

Câu trả lời:


304

Sử dụng @Spychú thích

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

    @InjectMocks
    private Demo demo;

    /* ... */
}

Mockito sẽ coi tất cả các lĩnh vực có @Mockhoặc @Spychú thích là các ứng cử viên tiềm năng sẽ được đưa vào ví dụ chú thích với @InjectMockschú thích. Trong trường hợp trên, 'RealServiceImpl'ví dụ sẽ được đưa vào 'bản demo'

Để biết thêm chi tiết tham khảo

Mockito-nhà

@Spy

@Chế nhạo


9
+1: Làm việc cho tôi ... ngoại trừ các đối tượng String. Mockito phàn nàn:Mockito cannot mock/spy following: - final classes - anonymous classes - primitive types
Adrian Pronk

Cảm ơn Nó cũng làm việc cho tôi :) Để tạo proxy sử dụng giả lập cho việc triển khai thực sự sử dụng gián điệp
Swarit Agarwal

Cảm ơn! Đó chính xác là những gì tôi cần!
khai quật

2
Trong trường hợp của tôi, Mockito không tiêm Spy. Nó không tiêm Mock mặc dù. Trường này là riêng tư và không có setter.
Vituel

8
BTW, không cần thiết new RealServiceImpl(), @Spy private SomeService service;tạo một đối tượng thực sự bằng cách sử dụng hàm tạo mặc định trước khi theo dõi nó.
parxier

20

Ngoài câu trả lời @Dev Blanks, nếu bạn muốn sử dụng một bean hiện có được tạo bởi Spring, mã có thể được sửa đổi thành:

@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);
    }

    /* ... */
}

Bằng cách này, bạn không cần thay đổi mã của mình (thêm một hàm tạo khác) chỉ để làm cho các thử nghiệm hoạt động.


2
@Aada Bạn có thể giải thích?
Yoaz Menda

1
Thư viện này có nguồn gốc từ đâu, tôi chỉ có thể thấy MethMocks trong org.mockito
Sameer

1
@sameer nhập org.mockito.InjectMocks;
Yoaz Menda

Thêm một bình luận để hủy bỏ Aada. Điều này làm việc cho tôi. Tôi không thể "đơn giản là không sử dụng phép tiêm trường" như được đề xuất bởi các câu trả lời khác, vì vậy tôi phải đảm bảo các Autowiredtrường từ các phụ thuộc được tạo chính xác.
Scrambo

1
Tôi đã thử điều này. nhưng tôi nhận được ngoại lệ con trỏ null từ phương thức thiết lập
Akhil Surapuram

3

Mockito không phải là một khung DI và thậm chí các khung DI khuyến khích các nhà xây dựng tiêm trên các trường tiêm.
Vì vậy, bạn chỉ cần khai báo một hàm tạo để đặt các phụ thuộc của lớp đang kiểm tra:

@Mock
private SomeService serviceMock;

private Demo demo;

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

Sử dụng Mockito spycho trường hợp chung là một lời khuyên khủng khiếp. Nó làm cho lớp kiểm tra giòn, không thẳng và dễ bị lỗi: Điều gì thực sự bị chế giễu? Những gì thực sự được thử nghiệm?
@InjectMocks@Spycũng làm tổn thương thiết kế tổng thể vì nó khuyến khích các lớp cồng kềnh và trách nhiệm hỗn hợp trong các lớp.
Vui lòng đọc spy()javadoc trước khi sử dụng một cách mù quáng (nhấn mạnh không phải của tôi):

Tạo ra một gián điệp của các đối tượng thực sự. Các điệp viên gọi các phương thức thực sự trừ khi chúng được khai thác. Các gián điệp thực sự nên được sử dụng cẩn thận và đôi khi , ví dụ như khi xử lý mã kế thừa.

Như thường lệ, bạn sẽ đọc partial mock warning: Lập trình hướng đối tượng giải quyết sự phức tạp bằng cách chia độ phức tạp thành các đối tượng SRPy riêng biệt, cụ thể. Làm thế nào để một phần giả phù hợp với mô hình này? Chà, nó chỉ không ... Giả một phần thường có nghĩa là sự phức tạp đã được chuyển sang một phương thức khác trên cùng một đối tượng. Trong hầu hết các trường hợp, đây không phải là cách bạn muốn thiết kế ứng dụng của mình.

Tuy nhiên, có một số trường hợp hiếm khi các giả lập một phần trở nên tiện dụng: xử lý mã bạn không thể thay đổi dễ dàng (giao diện của bên thứ 3, tái cấu trúc tạm thời mã kế thừa, v.v.) Tuy nhiên, tôi sẽ không sử dụng giả một phần cho mới, được điều khiển thử nghiệm & tốt- mã thiết kế.


0

Trong Spring có một tiện ích chuyên dụng được gọi ReflectionTestUtilscho mục đích này. Lấy ví dụ cụ thể và tiêm vào lĩnh vực này.


@Spy
..
@Mock
..

@InjectMock
Foo foo;

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

-1

Tôi biết đây là một câu hỏi cũ, nhưng chúng tôi đã phải đối mặt với cùng một vấn đề khi cố gắng tiêm String. Vì vậy, chúng tôi đã phát minh ra tiện ích mở rộng JUnit5 / Mockito, thực hiện chính xác những gì bạn muốn: https://github.com/exabrial/mockito-object-injection

BIÊN TẬP:

@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();
 }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.