Mô phỏng một hàm tạo với tham số


89

Tôi có một lớp học như sau:

public class A {
    public A(String test) {
        bla bla bla
    }

    public String check() {
        bla bla bla
    }
}

Logic trong hàm tạo A(String test)check()là những thứ tôi đang cố gắng chế nhạo. Tôi muốn bất kỳ lệnh gọi nào như: new A($$$any string$$$).check()trả về một chuỗi giả "test".

Tôi đã thử:

 A a = mock(A.class); 
 when(a.check()).thenReturn("test");

 String test = a.check(); // to this point, everything works. test shows as "tests"

 whenNew(A.class).withArguments(Matchers.anyString()).thenReturn(rk);
 // also tried:
 //whenNew(A.class).withParameterTypes(String.class).withArguments(Matchers.anyString()).thenReturn(rk);

 new A("random string").check();  // this doesn't work

Nhưng nó dường như không hoạt động. new A($$$any string$$$).check()vẫn đang đi qua logic của phương thức khởi tạo thay vì tìm nạp đối tượng giả lập của A.


phương thức kiểm tra () giả mạo của bạn có hoạt động đúng không?
Ben Glasser

Kiểm tra @BenGlasser () hoạt động tốt. Chỉ là khiNew dường như không hoạt động chút nào. Tôi cũng đã cập nhật mô tả.
Shengjie

Câu trả lời:


93

Mã bạn đăng phù hợp với tôi với phiên bản mới nhất của Mockito và Powermockito. Có thể bạn chưa chuẩn bị A? Thử đi:

A.java

public class A {
     private final String test;

    public A(String test) {
        this.test = test;
    }

    public String check() {
        return "checked " + this.test;
    }
}

MockA.java

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(A.class)
public class MockA {
    @Test
    public void test_not_mocked() throws Throwable {
        assertThat(new A("random string").check(), equalTo("checked random string"));
    }
    @Test
    public void test_mocked() throws Throwable {
         A a = mock(A.class); 
         when(a.check()).thenReturn("test");
         PowerMockito.whenNew(A.class).withArguments(Mockito.anyString()).thenReturn(a);
         assertThat(new A("random string").check(), equalTo("test"));
    }
}

Cả hai bài kiểm tra sẽ vượt qua với mockito 1.9.0, powermockito 1.4.12 và junit 4.8.2


24
Cũng lưu ý rằng nếu các nhà xây dựng được gọi từ lớp khác, bao gồm nó trong danh sách trongPrepareForTest
Jeff E

Bất cứ ai có ý tưởng tại sao chúng ta nên tự chuẩn bị khi "PowerMockito.whenNew" được gọi?
udayanga

50

Theo hiểu biết của tôi, bạn không thể chế nhạo các hàm tạo bằng mockito, chỉ có các phương thức. Nhưng theo wiki trên trang mã google Mockito, có một cách để chế nhạo hành vi của hàm tạo bằng cách tạo một phương thức trong lớp của bạn để trả về một phiên bản mới của lớp đó. thì bạn có thể mô phỏng phương pháp đó. Dưới đây là phần trích dẫn trực tiếp từ Mockito wiki :

Mẫu 1 - sử dụng các phương thức một dòng để tạo đối tượng

Để sử dụng mẫu 1 (kiểm tra một lớp được gọi là MyClass), bạn sẽ thay thế một lệnh gọi như

   Foo foo = new Foo( a, b, c );

với

   Foo foo = makeFoo( a, b, c );

và viết phương thức một dòng

   Foo makeFoo( A a, B b, C c ) { 
        return new Foo( a, b, c );
   }

Điều quan trọng là bạn không bao gồm bất kỳ logic nào trong phương pháp; chỉ một dòng tạo đối tượng. Lý do cho điều này là bản thân phương pháp sẽ không bao giờ được thử nghiệm đơn vị.

Khi bạn đến để kiểm tra lớp, đối tượng mà bạn kiểm tra thực sự sẽ là gián điệp của Mockito, với phương thức này được ghi đè, để trả về một mô hình. Do đó, những gì bạn đang thử nghiệm không phải là bản thân lớp, mà là một phiên bản được sửa đổi rất nhẹ của nó.

Lớp thử nghiệm của bạn có thể chứa các thành viên như

  @Mock private Foo mockFoo;
  private MyClass toTest = spy(new MyClass());

Cuối cùng, bên trong phương pháp thử nghiệm của bạn, bạn mô phỏng lệnh gọi makeFoo với một dòng như

  doReturn( mockFoo )
      .when( toTest )
      .makeFoo( any( A.class ), any( B.class ), any( C.class ));

Bạn có thể sử dụng các so khớp cụ thể hơn bất kỳ () nào nếu bạn muốn kiểm tra các đối số được chuyển cho hàm tạo.

Nếu bạn chỉ muốn trả về một đối tượng bị chế nhạo của lớp mình, tôi nghĩ điều này sẽ phù hợp với bạn. Trong mọi trường hợp, bạn có thể đọc thêm về tạo đối tượng chế nhạo tại đây:

http://code.google.com/p/mockito/wiki/MockingObjectCreation


21
+1, Tôi không thích thực tế là tôi cần điều chỉnh mã nguồn của mình để làm cho nó thân thiện hơn với mockito. Cảm ơn vì những chia sẻ.
Shengjie

22
Không bao giờ là xấu khi có mã nguồn dễ kiểm tra hơn hoặc tránh các mẫu chống khả năng kiểm tra khi bạn viết mã của mình. Nếu bạn viết mã nguồn dễ kiểm tra hơn, nó sẽ tự động dễ bảo trì hơn. Cô lập các cuộc gọi hàm tạo của bạn trong các phương thức của riêng chúng chỉ là một cách để đạt được điều này.
Dawood ibn Kareem

1
Viết mã có thể kiểm tra là tốt. Bắt buộc phải thiết kế lại lớp A để mình viết bài kiểm tra cho lớp B, cái này phụ thuộc vào A, vì A mà phụ thuộc C cứng rắn, cảm thấy ... kém hay. Vâng, cuối cùng thì mã sẽ tốt hơn, nhưng tôi sẽ thiết kế lại bao nhiêu lớp để có thể viết xong một bài kiểm tra?
Mark Wood

@MarkWood theo kinh nghiệm của tôi, trải nghiệm thử nghiệm vụng về thường là dấu hiệu của một số lỗi thiết kế. IRL nếu bạn đang kiểm tra các trình xây dựng, mã của bạn có thể đang hét vào mặt bạn về một nhà máy hoặc một số nội dung phụ thuộc. Nếu bạn tuân theo các mẫu thiết kế điển hình cho hai trường hợp đó, mã của bạn nói chung sẽ dễ kiểm tra và làm việc hơn nhiều. Nếu bạn đang kiểm tra các hàm tạo vì bạn có nhiều logic trong đó, bạn có thể cần một số lớp đa hình hoặc bạn có thể chuyển logic đó sang một phương thức khởi tạo.
Ben Glasser

12

Không cần sử dụng Powermock .... Hãy xem ví dụ bên dưới dựa trên câu trả lời của Ben Glasser vì tôi đã mất một khoảng thời gian để tìm ra nó .. cách tiết kiệm một số lần ...

Lớp gốc:

public class AClazz {

    public void updateObject(CClazz cClazzObj) {
        log.debug("Bundler set.");
        cClazzObj.setBundler(new BClazz(cClazzObj, 10));
    } 
}

Lớp sửa đổi:

@Slf4j
public class AClazz {

    public void updateObject(CClazz cClazzObj) {
        log.debug("Bundler set.");
        cClazzObj.setBundler(getBObject(cClazzObj, 10));
    }

    protected BClazz getBObject(CClazz cClazzObj, int i) {
        return new BClazz(cClazzObj, 10);
    }
 }

Lớp kiểm tra

public class AClazzTest {

    @InjectMocks
    @Spy
    private AClazz aClazzObj;

    @Mock
    private CClazz cClazzObj;

    @Mock
    private BClazz bClassObj;

    @Before
    public void setUp() throws Exception {
        Mockito.doReturn(bClassObj)
               .when(aClazzObj)
               .getBObject(Mockito.eq(cClazzObj), Mockito.anyInt());
    }

    @Test
    public void testConfigStrategy() {
        aClazzObj.updateObject(cClazzObj);

        Mockito.verify(cClazzObj, Mockito.times(1)).setBundler(bClassObj);
    }
}

6

Với mockito, bạn có thể sử dụng withSettings (), ví dụ: nếu CounterService yêu cầu 2 phụ thuộc, bạn có thể chuyển chúng dưới dạng mô hình:

UserService userService = Mockito.mock(UserService.class); SearchService searchService = Mockito.mock(SearchService.class); CounterService counterService = Mockito.mock(CounterService.class, withSettings().useConstructor(userService, searchService));


Theo tôi, câu trả lời dễ nhất và tốt nhất. Cảm ơn bạn.
Eldon

4

Mockito có giới hạn trong việc kiểm tra các phương pháp cuối cùng, tĩnh và riêng tư.

với thư viện thử nghiệm jMockit, bạn có thể thực hiện một số thao tác rất dễ dàng và đơn giản như sau:

Phương thức tạo giả lập của lớp java.io.File:

new MockUp<File>(){
    @Mock
    public void $init(String pathname){
        System.out.println(pathname);
        // or do whatever you want
    }
};
  • tên phương thức khởi tạo công khai nên được thay thế bằng $ init
  • các đối số và ngoại lệ được ném vẫn như cũ
  • kiểu trả về nên được định nghĩa là void

Mô phỏng một phương thức tĩnh:

  • loại bỏ tĩnh khỏi chữ ký giả phương thức
  • chữ ký phương thức vẫn giữ nguyên nếu không
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.