Thử nghiệm phương pháp Riêng tư bằng Mockito


104
công cộng hạng A {

    phương thức public void (boolean b) {
          nếu (b == true)
               method1 ();
          khác
               method2 ();
    }

    private void method1 () {}
    private void method2 () {}
}
lớp công khai TestA {

    @Kiểm tra
    public void testMethod () {
      A a = mock (A.class);
      a.method (đúng);
      // cách kiểm tra như verify (a) .method1 ();
    }
}

Cách test private method có được gọi hay không và test private method bằng mockito như thế nào ???


Câu trả lời:


81

Bạn không thể làm điều đó với Mockito nhưng bạn có thể sử dụng Powermock để mở rộng Mockito và giả lập các phương thức riêng tư. Powermock hỗ trợ Mockito. Đây là một ví dụ.


19
Tôi bối rối với câu trả lời này. Đây là sự chế giễu, nhưng tiêu đề đang thử nghiệm các phương pháp riêng tư
diyoda_

Tôi đã sử dụng Powermock để mô phỏng phương thức private, nhưng làm cách nào để tôi có thể kiểm tra phương thức private với Powermock. Ở đâu, tôi có thể chuyển một số đầu vào và mong đợi một số đầu ra từ phương pháp và sau đó xác minh đầu ra?
Rito

Bạn không thể. Bạn giả lập đầu ra đầu vào, bạn không thể kiểm tra chức năng thực.
Talha

131

Không thể thông qua mockito. Từ wiki của họ

Tại sao Mockito không chế nhạo các phương pháp riêng tư?

Thứ nhất, chúng tôi không giáo điều về việc chế giễu các phương pháp tư nhân. Chúng tôi chỉ không quan tâm đến các phương pháp riêng tư bởi vì từ quan điểm của việc thử nghiệm các phương pháp riêng tư không tồn tại. Dưới đây là một số lý do khiến Mockito không chế nhạo các phương pháp riêng tư:

Nó yêu cầu hack các trình tải lớp không bao giờ chống đạn và nó thay đổi api (bạn phải sử dụng trình chạy thử nghiệm tùy chỉnh, chú thích lớp, v.v.).

Rất dễ dàng để làm việc xung quanh - chỉ cần thay đổi khả năng hiển thị của phương thức từ riêng tư sang gói được bảo vệ (hoặc được bảo vệ).

Nó đòi hỏi tôi phải dành thời gian thực hiện và duy trì nó. Và nó không có ý nghĩa cho điểm # 2 và thực tế là nó đã được triển khai trong một công cụ khác (powermock).

Cuối cùng ... Chế giễu các phương pháp riêng tư là một gợi ý rằng có điều gì đó không ổn trong sự hiểu biết của OO. Trong OO, bạn muốn các đối tượng (hoặc vai trò) cộng tác chứ không phải các phương thức. Quên về pascal và mã thủ tục. Suy nghĩ trong các đối tượng.


1
Có một giả định nghiêm trọng được đưa ra bởi tuyên bố đó:> Chế nhạo các phương pháp riêng tư là một gợi ý rằng có điều gì đó không ổn trong sự hiểu biết của OO. Nếu tôi đang thử nghiệm một phương thức công khai và nó gọi các phương thức riêng tư, thì tôi muốn mô phỏng các phương thức trả về riêng tư. Theo giả định trên, bạn không cần phải triển khai các phương thức private. Làm thế nào đó là một sự hiểu biết kém về OO?
eggmatters

1
@eggmatters Theo Baeldung "Kỹ thuật chế nhạo nên được áp dụng cho các phần phụ thuộc bên ngoài của lớp chứ không phải cho chính lớp đó. Nếu việc bắt chước các phương thức private là cần thiết để kiểm tra các lớp của chúng ta, nó thường chỉ ra một thiết kế xấu." Đây là một chủ đề thú vị về nó softwareengineering.stackexchange.com/questions/100959/…
Jason Glez

34

Đây là một ví dụ nhỏ về cách thực hiện với powermock

public class Hello {
    private Hello obj;
    private Integer method1(Long id) {
        return id + 10;
    }
} 

Để kiểm tra method1, hãy sử dụng mã:

Hello testObj = new Hello();
Integer result = Whitebox.invokeMethod(testObj, "method1", new Long(10L));

Để thiết lập đối tượng tin obj sử dụng này:

Hello testObj = new Hello();
Hello newObject = new Hello();
Whitebox.setInternalState(testObj, "obj", newObject);

Liên kết của bạn chỉ trỏ đến repo giả lập sức mạnh @Mindaugas
Xavier

@Xavier đúng. Bạn có thể sử dụng nó nếu bạn thích trong dự án của mình.
Mindaugas Jaraminas

1
Tuyệt vời !!! rất tốt giải thích với các ví dụ đơn giản hầu hết mọi thứ :) Bởi vì mục đích là chỉ để kiểm tra mã và không phải những gì tất cả các khuôn khổ cung cấp :)
siddhusingh

Vui lòng cập nhật điều này. Hộp trắng không còn là một phần của API công khai.
user447607

17

Hãy nghĩ về điều này dưới góc độ hành vi, không phải về phương pháp có. Phương thức được gọi methodcó một hành vi cụ thể nếu blà đúng. Nó có hành vi khác nếu blà sai. Điều này có nghĩa là bạn nên viết hai bài kiểm tra khác nhau cho method; một cho mỗi trường hợp. Vì vậy, thay vì có ba bài kiểm tra định hướng phương pháp (một cho method, một cho method1, một cho method2, bạn có hai bài kiểm tra định hướng hành vi.

Liên quan đến điều này (tôi đã đề xuất điều này trong một chủ đề SO khác gần đây, và kết quả là được gọi là một từ gồm bốn chữ cái, vì vậy hãy thoải mái coi điều này với một chút muối); Tôi thấy hữu ích khi chọn tên thử nghiệm phản ánh hành vi mà tôi đang thử nghiệm, thay vì tên của phương pháp. Vì thế đừng gọi xét nghiệm của bạn testMethod(), testMethod1(), testMethod2()và vân vân. Tôi thích những cái tên như calculatedPriceIsBasePricePlusTax()hoặc taxIsExcludedWhenExcludeIsTrue()cho biết tôi đang thử nghiệm hành vi nào; thì trong mỗi phương pháp kiểm tra, chỉ kiểm tra hành vi được chỉ định. Hầu hết các hành vi như vậy sẽ chỉ liên quan đến một lệnh gọi đến một phương thức chung, nhưng có thể liên quan đến nhiều lệnh gọi đến các phương thức riêng tư.

Hi vọng điêu nay co ich.


13

Mặc dù Mockito không cung cấp khả năng đó, bạn có thể đạt được kết quả tương tự bằng cách sử dụng Mockito + lớp JUnit ReflectionUtils hoặc lớp Spring ReflectionTestUtils . Vui lòng xem một ví dụ dưới đây được lấy từ đây giải thích cách gọi một phương thức riêng:

ReflectionTestUtils.invokeMethod(student, "saveOrUpdate", "From Unit test");

Bạn có thể tìm thấy các ví dụ hoàn chỉnh với ReflectionTestUtils và Mockito trong cuốn sách Mockito for Spring


ReflectionTestUtils.invokeMethod (sinh viên, "saveOrUpdate", "đối số1", "đối số2", "đối số3"); Đối số cuối cùng của invokeMethod, sử dụng Vargs có thể nhận nhiều đối số cần chuyển cho phương thức private. nó hoạt động.
Tim

Câu trả lời này sẽ có nhiều ủng hộ hơn, cho đến nay là cách dễ nhất để kiểm tra các phương pháp riêng tư.
Tối đa

9

Bạn không được phép thử nghiệm các phương pháp riêng tư. Chỉ các phương thức không riêng tư mới cần được kiểm tra vì chúng vẫn gọi các phương thức riêng tư. Nếu bạn "muốn" thử nghiệm các phương pháp riêng tư, điều đó có thể cho thấy rằng bạn cần phải suy nghĩ lại thiết kế của mình:

Tôi có đang sử dụng phương pháp tiêm phụ thuộc thích hợp không? Tôi có thể cần chuyển các phương thức private vào một lớp riêng biệt và thay vào đó là kiểm tra nó không? Các phương pháp này phải là riêng tư? ... chúng không thể được mặc định hoặc được bảo vệ đúng hơn?

Trong ví dụ trên, hai phương thức được gọi là "ngẫu nhiên" có thể thực sự cần được đặt trong một lớp của riêng chúng, được kiểm tra và sau đó được đưa vào lớp trên.


26
Một điểm hợp lệ. Tuy nhiên, không phải lý do sử dụng công cụ sửa đổi riêng cho các phương thức là vì bạn chỉ muốn cắt bớt các mã quá dài và / hoặc lặp lại? Tách nó thành một lớp khác giống như bạn đang quảng bá những dòng mã đó trở thành công dân hạng nhất mà sẽ không được sử dụng lại ở bất kỳ nơi nào khác vì nó có ý nghĩa đặc biệt để phân vùng các mã dài và để ngăn các dòng mã lặp lại. Nếu bạn định tách nó sang một lớp khác thì cảm thấy không ổn; bạn sẽ dễ dàng bị bùng nổ lớp học.
supertonsky

2
Ghi nhận là supertonsky, tôi đang đề cập đến trường hợp chung. Tôi đồng ý rằng trong trường hợp trên, nó không nên nằm trong một lớp riêng biệt. (+1 trên nhận xét của bạn - đó là một điểm rất hợp lệ mà bạn đang thực hiện khi quảng bá các thành viên riêng tư)
Jaco Van Niekerk

4
@supertonsky, tôi không thể tìm thấy bất kỳ phản hồi thỏa đáng nào cho vấn đề này. Có một số lý do tại sao tôi có thể sử dụng các thành viên riêng tư và rất thường xuyên họ không chỉ ra mùi mã và tôi sẽ được lợi rất nhiều từ việc kiểm tra chúng. Mọi người dường như phủ nhận điều này bằng cách nói "chỉ cần đừng làm điều đó".
LuddyPants

3
Xin lỗi, tôi đã chọn phản đối dựa trên "Nếu bạn 'muốn' thử nghiệm các phương pháp riêng tư, nó có thể cho thấy bạn cần phản đối thiết kế của mình". Được rồi, đủ công bằng, nhưng một trong những lý do để thử nghiệm là bởi vì, trong thời hạn chót khi bạn không có thời gian để suy nghĩ lại về thiết kế, bạn đang cố gắng thực hiện một cách an toàn thay đổi cần được thực hiện đối với phương pháp riêng. Trong một thế giới lý tưởng, phương pháp riêng tư đó có cần phải thay đổi bởi vì thiết kế sẽ hoàn hảo? Chắc chắn rồi, nhưng trong một thế giới hoàn hảo, nhưng đó là một cuộc tranh luận vì trong một thế giới hoàn hảo, người cần thử nghiệm, tất cả chỉ hoạt động. :)
John Lockwood

2
@John. Điểm được thực hiện, phiếu giảm giá của bạn được đảm bảo (+1). Cảm ơn bạn cũng đã nhận xét - Tôi đồng ý với bạn về quan điểm bạn đưa ra. Trong những trường hợp như vậy, tôi có thể thấy một trong hai tùy chọn: Phương thức được đặt là gói riêng tư hoặc được bảo vệ và các bài kiểm tra đơn vị được viết như bình thường; hoặc (và đây là thói quen xấu) một phương pháp chính được viết nhanh để đảm bảo rằng nó vẫn hoạt động. Tuy nhiên, phản hồi của tôi dựa trên một kịch bản mã MỚI được viết và không cấu trúc lại khi bạn có thể không sửa đổi được thiết kế ban đầu.
Jaco Van Niekerk

6

Tôi đã có thể kiểm tra một phương pháp riêng tư bên trong bằng cách sử dụng mockito bằng cách sử dụng phản chiếu. Đây là ví dụ, đã cố gắng đặt tên sao cho nó có ý nghĩa

//Service containing the mock method is injected with mockObjects

@InjectMocks
private ServiceContainingPrivateMethod serviceContainingPrivateMethod;

//Using reflection to change accessibility of the private method

Class<?>[] params = new Class<?>[]{PrivateMethodParameterOne.class, PrivateMethodParameterTwo.class};
    Method m = serviceContainingPrivateMethod .getClass().getDeclaredMethod("privateMethod", params);
    //making private method accessible
    m.setAccessible(true); 
    assertNotNull(m.invoke(serviceContainingPrivateMethod, privateMethodParameterOne, privateMethodParameterTwo).equals(null));

6
  1. Bằng cách sử dụng phản xạ, các phương thức riêng có thể được gọi từ các lớp thử nghiệm. Trong trường hợp này,

    // phương thức kiểm tra sẽ như thế này ...

    public class TestA {
    
      @Test
        public void testMethod() {
    
        A a= new A();
        Method privateMethod = A.class.getDeclaredMethod("method1", null);
        privateMethod.setAccessible(true);
        // invoke the private method for test
        privateMethod.invoke(A, null);
    
        }
    }
  2. Nếu phương thức private gọi bất kỳ phương thức private nào khác, thì chúng ta cần theo dõi đối tượng và khai thác phương thức khác. Lớp thử nghiệm sẽ giống như ...

    // phương thức kiểm tra sẽ như thế này ...

    public class TestA {
    
      @Test
        public void testMethod() {
    
        A a= new A();
        A spyA = spy(a);
        Method privateMethod = A.class.getDeclaredMethod("method1", null);
        privateMethod.setAccessible(true);
        doReturn("Test").when(spyA, "method2"); // if private method2 is returning string data
        // invoke the private method for test
        privateMethod.invoke(spyA , null);
    
        }
    }

** Cách tiếp cận là kết hợp phản chiếu và theo dõi đối tượng. ** method1 và ** method2 là các phương thức riêng và method1 gọi phương thức2.


4

Tôi không thực sự hiểu nhu cầu của bạn để kiểm tra phương pháp riêng tư. Vấn đề cơ bản là phương thức công khai của bạn không có giá trị như kiểu trả về và do đó bạn không thể kiểm tra phương thức công khai của mình. Do đó bạn buộc phải thử nghiệm phương pháp riêng tư của mình. Suy đoán của tôi có đúng không ??

Một số giải pháp khả thi (AFAIK):

  1. Chế giễu các phương pháp riêng tư của bạn, nhưng bạn vẫn sẽ không "thực sự" thử nghiệm các phương pháp của mình.

  2. Xác minh trạng thái của đối tượng được sử dụng trong phương thức. NHẤT là các phương thức thực hiện một số xử lý các giá trị đầu vào và trả về kết quả đầu ra hoặc thay đổi trạng thái của các đối tượng. Kiểm tra các đối tượng cho trạng thái mong muốn cũng có thể được sử dụng.

    public class A{
    
    SomeClass classObj = null;
    
    public void publicMethod(){
       privateMethod();
    }
    
    private void privateMethod(){
         classObj = new SomeClass();
    }
    
    }

    [Ở đây bạn có thể kiểm tra phương thức private, bằng cách kiểm tra sự thay đổi trạng thái của classObj từ null sang không null.]

  3. Cấu trúc lại mã của bạn một chút (Hy vọng đây không phải là mã kế thừa). Cơ sở của tôi khi viết một phương thức là, một phương thức phải luôn trả về một cái gì đó (một int / a boolean). Giá trị trả về CÓ THỂ hoặc CÓ THỂ KHÔNG được sử dụng bởi quá trình triển khai, nhưng nó sẽ BẤT NGỜ được sử dụng bởi thử nghiệm

    mã.

    public class A
    { 
        public int method(boolean b)
        {
              int nReturn = 0;
              if (b == true)
                   nReturn = method1();
              else
                   nReturn = method2();
        }
    
        private int method1() {}
    
        private int method2() {}
    
    }

3

Thực ra có một cách để kiểm tra các phương pháp từ một thành viên riêng với Mockito. Giả sử bạn có một lớp học như thế này:

public class A {
    private SomeOtherClass someOtherClass;
    A() {
        someOtherClass = new SomeOtherClass();
    }
    public void method(boolean b){
        if (b == true)
            someOtherClass.method1();
        else
            someOtherClass.method2();
    }

}

public class SomeOtherClass {
    public void method1() {}
    public void method2() {}
}

Nếu bạn muốn kiểm tra a.methodsẽ gọi một phương thức từ SomeOtherClass, bạn có thể viết một cái gì đó như dưới đây.

@Test
public void testPrivateMemberMethodCalled() {
    A a = new A();
    SomeOtherClass someOtherClass = Mockito.spy(new SomeOtherClass());
    ReflectionTestUtils.setField( a, "someOtherClass", someOtherClass);
    a.method( true );

    Mockito.verify( someOtherClass, Mockito.times( 1 ) ).method1();
}

ReflectionTestUtils.setField(); sẽ khai báo thành viên riêng tư với một cái gì đó bạn có thể theo dõi.


2

Đặt thử nghiệm của bạn trong cùng một gói, nhưng một thư mục nguồn khác (src / main / java so với src / test / java) và đặt các phương pháp đó ở chế độ riêng tư. Khả năng kiểm tra Imo quan trọng hơn quyền riêng tư.


6
Lý do chính đáng duy nhất là để kiểm tra một phần của hệ thống kế thừa. Nếu bạn bắt đầu thử nghiệm phương thức private / package-private, bạn sẽ để lộ nội bộ đối tượng của mình. Làm như vậy thường dẫn đến mã cấu trúc lại kém. Thành phần PRefer để bạn có thể đạt được khả năng kiểm tra, với tất cả sự tốt đẹp của một hệ thống hướng đối tượng.
Brice

1
Đồng ý - đó sẽ là cách ưu tiên. Tuy nhiên, nếu bạn thực sự muốn thử nghiệm các phương pháp riêng tư với mockito, thì đây là lựa chọn (an toàn loại) duy nhất mà bạn có. Tuy nhiên, câu trả lời của tôi hơi vội vàng, lẽ ra tôi phải chỉ ra những rủi ro, giống như bạn và những người khác đã làm.
Roland Schneider

Đây là cách ưa thích của tôi. Không có gì sai khi hiển thị đối tượng bên trong ở cấp độ gói-riêng; và kiểm thử đơn vị là kiểm thử hộp trắng, bạn cần biết nội bộ để kiểm tra.
Andrew Feng,

0

Trong trường hợp phương thức private không vô hiệu và giá trị trả về được sử dụng làm tham số cho phương thức của phần phụ thuộc bên ngoài, bạn có thể giả lập phần phụ thuộc và sử dụng một ArgumentCaptorđể nắm bắt giá trị trả về. Ví dụ:

ArgumentCaptor<ByteArrayOutputStream> csvOutputCaptor = ArgumentCaptor.forClass(ByteArrayOutputStream.class);
//Do your thing..
verify(this.awsService).uploadFile(csvOutputCaptor.capture());
....
assertEquals(csvOutputCaptor.getValue().toString(), "blabla");
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.