Mockito: Khởi tạo trường riêng giả


95

Làm cách nào tôi có thể mô phỏng một biến trường đang được khởi tạo nội tuyến?

class Test {
    private Person person = new Person();
    ...
    public void testMethod() {
        person.someMethod();
        ...
    }
}

Ở đây tôi muốn mô phỏng person.someMethod()trong khi thử nghiệm Test.testMethod()phương pháp mà tôi cần mô phỏng việc khởi tạo personbiến. Có manh mối nào không?

Chỉnh sửa: Tôi không được phép sửa đổi lớp Người.


1
Liên kết này có thể hữu ích cho bạn stackoverflow.com/questions/13645571/…
Popeye

2
Bạn nên cấu trúc lại mã của mình để bạn có thể chuyển vào một mô hình cho Person. Các tùy chọn bao gồm thêm một phương thức khởi tạo để thực hiện việc này hoặc thêm một phương thức setter.
Tim Biegeleisen

Câu trả lời:


109

Mockito đi kèm với một lớp trợ giúp để tiết kiệm cho bạn một số mã tấm lò hơi phản chiếu:

import org.mockito.internal.util.reflection.Whitebox;

//...

@Mock
private Person mockedPerson;
private Test underTest;

// ...

@Test
public void testMethod() {
    Whitebox.setInternalState(underTest, "person", mockedPerson);
    // ...
}

Cập nhật: Thật không may, nhóm mockito đã quyết định xóa lớp trong Mockito 2. Vì vậy, bạn quay lại viết mã chương trình tạo phản chiếu của riêng mình, sử dụng một thư viện khác (ví dụ: Apache Commons Lang ), hoặc đơn giản là thử nghiệm lớp Whitebox (nó được MIT cấp phép ).

Cập nhật 2: JUnit 5 đi kèm với các lớp ReflectionSupportAnnotationSupport của riêng nó có thể hữu ích và giúp bạn không phải kéo vào một thư viện khác.


Tôi đang theo dõi đối tượng mục tiêu của mình vì những lý do khác và trong trường hợp này khi đối tượng của tôi là gián điệp, tôi không thể đặt trạng thái bên trong theo cách này.
Arun

Tại sao không? Tôi đang sử dụng nó với gián điệp. Tạo một cá thể Người. Stub bất cứ thứ gì cần khai thác, sau đó đặt nó trên phiên bản Thử nghiệm của bạn.
Ralf

Cảnh báo: Whitebox nằm trong internalgói và dường như không hoạt động nữa trên Mockito 2.6.2.
Nova

Bạn có thể sử dụng FieldSetter.setField (). Tôi đã đưa ra một ví dụ dưới đây cho tương tự.
Raj Kumar

1
@Ralf Bởi vì việc thay đổi tên tham chiếu trong Java luôn dẫn đến lỗi biên dịch.
Joe Coder

69

Đến bữa tiệc khá muộn, nhưng tôi đã bị bắt ở đây và được một người bạn giúp đỡ. Vấn đề là không sử dụng PowerMock. Điều này hoạt động với phiên bản mới nhất của Mockito.

Mockito đi kèm với điều này org.mockito.internal.util.reflection.FieldSetter.

Về cơ bản, nó giúp bạn sửa đổi các trường riêng tư bằng cách sử dụng phản chiếu.

Đây là cách bạn sử dụng nó:

@Mock
private Person mockedPerson;
private Test underTest;

// ...

@Test
public void testMethod() {
    FieldSetter.setField(underTest, underTest.getClass().getDeclaredField("person"), mockedPerson);
    // ...
    verify(mockedPerson).someMethod();
}

Bằng cách này, bạn có thể chuyển một đối tượng giả và sau đó xác minh nó sau.

Đây là tài liệu tham khảo.


FieldSetterkhông còn khả dụng trong Mockito 2.x.
Ralf

4
@Ralf Tôi đang sử dụng phiên bản mockito-core-2.15.0. Nó có sẵn ở đó. static.javadoc.io/org.mockito/mockito-core/2.0.15-beta/org/… . Vẫn là phiên bản beta.
Raj Kumar

1
@RajKumar là tham số Class#getDeclaredFieldđơn chấp nhận, vì vậy các parens cần phải trông như thế này FieldSetter.setField(underTest, underTest.getClass().getDeclaredField("person"), mockedPerson);. Đó là cách tôi đã sai và tôi nghĩ có thể là một ý tưởng hay để sửa nó trong ví dụ của bạn. Cảm ơn vì đã trả lời.
Dapeng Li

1
sử dụng API nội bộ không phải là ý tưởng tốt nhất
David

@RajKumar Tốt, nhưng như Zimbo Rodger đã đề cập: Sử dụng API nội bộ có phải là một ý tưởng hay không? Tại sao nó là âm bên trong? Nó rất hữu ích cho việc thử nghiệm.
Willi Mentzel

32

Trong trường hợp bạn sử dụng Spring Test, hãy thử org.springframework.test.util.ReflectionTestUtils

 ReflectionTestUtils.setField(testObject, "person", mockedPerson);

1
Tuyệt quá! Có thể trợ giúp để liên kết đến bài viết này và có thể bao gồm các phụ thuộc mà người ta cần từ nó: baeldung.com/spring-reflection-test-utils
Willi Mentzel

build.gradle.kts:testImplementation("org.springframework:spring-test:5.1.2.RELEASE")
Willi Mentzel

28

Tôi đã tìm thấy giải pháp cho vấn đề này mà tôi quên đăng ở đây.

@RunWith(PowerMockRunner.class)
@PrepareForTest({ Test.class })
public class SampleTest {

@Mock
Person person;

@Test
public void testPrintName() throws Exception {
    PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);
    Test test= new Test();
    test.testMethod();
    }
}

Các điểm chính của giải pháp này là:

  1. Chạy các trường hợp thử nghiệm của tôi với PowerMockRunner: @RunWith(PowerMockRunner.class)

  2. Hướng dẫn Powermock chuẩn bị Test.classcho việc thao tác các trường riêng tư:@PrepareForTest({ Test.class })

  3. Và cuối cùng giả lập hàm tạo cho lớp Person:

    PowerMockito.mockStatic(Person.class); PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);


8
Giải thích của bạn nói về mockStaticchức năng, nhưng điều đó không được trình bày trong ví dụ mã của bạn. Ví dụ mã nên có lệnh mockStaticgọi hay không bắt buộc đối với các hàm tạo?
Shadoninja

10

Mã sau có thể được sử dụng để khởi tạo ánh xạ trong mô hình máy khách REST. Các mapperlĩnh vực là tư nhân và cần phải được đặt trong thiết lập đơn vị kiểm tra.

import org.mockito.internal.util.reflection.FieldSetter;

new FieldSetter(client, Client.class.getDeclaredField("mapper")).set(new Mapper());

5

Sử dụng hướng dẫn của @ Jarda, bạn có thể xác định điều này nếu bạn cần đặt biến cùng một giá trị cho tất cả các thử nghiệm:

@Before
public void setClientMapper() throws NoSuchFieldException, SecurityException{
    FieldSetter.setField(client, client.getClass().getDeclaredField("mapper"), new Mapper());
}

Nhưng hãy lưu ý rằng việc đặt các giá trị riêng tư thành các giá trị khác nhau nên được xử lý cẩn thận. Nếu chúng là riêng tư là vì một số lý do.

Ví dụ, tôi sử dụng nó, chẳng hạn, để thay đổi thời gian chờ của một giấc ngủ trong các bài kiểm tra đơn vị. Trong các ví dụ thực tế, tôi muốn ngủ trong 10 giây nhưng trong bài kiểm tra đơn vị, tôi hài lòng nếu nó ngay lập tức. Trong các bài kiểm tra tích hợp, bạn nên kiểm tra giá trị thực.

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.