Tôi đang làm việc trên một dự án java. Tôi mới đến thử nghiệm đơn vị. Cách tốt nhất để đơn vị kiểm tra các phương thức riêng tư trong các lớp java là gì?
Tôi đang làm việc trên một dự án java. Tôi mới đến thử nghiệm đơn vị. Cách tốt nhất để đơn vị kiểm tra các phương thức riêng tư trong các lớp java là gì?
Câu trả lời:
Bạn thường không thử nghiệm phương pháp riêng tư trực tiếp. Vì chúng là riêng tư, hãy xem chúng là một chi tiết thực hiện. Không ai sẽ gọi một trong số họ và mong đợi nó hoạt động theo một cách riêng.
Thay vào đó bạn nên kiểm tra giao diện công cộng của bạn. Nếu các phương thức gọi các phương thức riêng tư của bạn đang hoạt động như bạn mong đợi, thì bạn giả sử bằng cách mở rộng rằng các phương thức riêng tư của bạn đang hoạt động chính xác.
Nói chung, tôi sẽ tránh nó. Nếu phương thức riêng tư của bạn phức tạp đến mức nó cần một bài kiểm tra đơn vị riêng biệt, điều đó thường có nghĩa là nó xứng đáng với lớp riêng của nó. Điều này có thể khuyến khích bạn viết nó theo cách có thể tái sử dụng. Sau đó, bạn nên kiểm tra lớp mới và gọi giao diện chung của nó trong lớp cũ.
Mặt khác, đôi khi bao gồm các chi tiết triển khai thành các lớp riêng biệt dẫn đến các lớp có giao diện phức tạp, nhiều dữ liệu chuyển giữa lớp cũ và lớp mới hoặc đến một thiết kế có thể trông tốt từ quan điểm OOP, nhưng không khớp các trực giác đến từ miền vấn đề (ví dụ: chia mô hình định giá thành hai phần chỉ để tránh thử nghiệm các phương thức riêng tư không trực quan và có thể dẫn đến các vấn đề sau này khi duy trì / mở rộng mã). Bạn không muốn có "lớp sinh đôi" luôn thay đổi cùng nhau.
Khi phải đối mặt với sự lựa chọn giữa đóng gói và kiểm tra, tôi muốn đi thứ hai. Điều quan trọng hơn là phải có mã chính xác (nghĩa là tạo ra đầu ra chính xác) hơn là một thiết kế OOP đẹp không hoạt động chính xác, vì nó không được kiểm tra đầy đủ. Trong Java, bạn chỉ cần cung cấp quyền truy cập "mặc định" cho phương thức và đặt kiểm tra đơn vị trong cùng một gói. Các thử nghiệm đơn vị chỉ là một phần của gói bạn đang phát triển và không có sự phụ thuộc giữa các thử nghiệm và mã đang được thử nghiệm. Điều đó có nghĩa là khi bạn thay đổi triển khai, bạn có thể cần thay đổi các thử nghiệm của mình, nhưng không sao - mỗi thay đổi của việc triển khai đều yêu cầu kiểm tra lại mã và nếu các thử nghiệm cần được sửa đổi để thực hiện điều đó, thì bạn chỉ cần làm nó
Nói chung, một lớp có thể cung cấp nhiều hơn một giao diện. Có một giao diện cho người dùng, và một giao diện cho những người bảo trì. Cái thứ hai có thể phơi bày nhiều hơn để đảm bảo rằng mã được kiểm tra đầy đủ. Nó không phải là một bài kiểm tra đơn vị trên một phương thức riêng tư - ví dụ, có thể là ghi nhật ký. Ghi nhật ký cũng "phá vỡ đóng gói", nhưng chúng tôi vẫn làm điều đó, vì nó rất hữu ích.
Thử nghiệm các phương pháp riêng sẽ phụ thuộc vào độ phức tạp của chúng; một số phương thức riêng tư một dòng sẽ không thực sự đảm bảo nỗ lực thử nghiệm thêm (điều này cũng có thể nói về phương pháp công cộng), nhưng một số phương pháp riêng có thể phức tạp như phương thức công khai và khó kiểm tra qua giao diện công cộng.
Kỹ thuật ưa thích của tôi là làm cho gói phương thức riêng tư thành riêng tư, cho phép truy cập vào một bài kiểm tra đơn vị trong cùng một gói nhưng nó vẫn sẽ được gói gọn từ tất cả các mã khác. Điều này sẽ mang lại lợi thế cho việc kiểm tra logic phương thức riêng trực tiếp thay vì phải dựa vào kiểm tra phương thức công khai để bao quát tất cả các phần của logic phức tạp (có thể).
Nếu điều này được kết hợp với chú thích @VisibleForTesting trong thư viện Google Guava, thì bạn đang đánh dấu rõ ràng phương thức riêng tư của gói này là hiển thị để chỉ kiểm tra và do đó, không nên gọi bất kỳ lớp nào khác.
Những người phản đối kỹ thuật này cho rằng điều này sẽ phá vỡ sự đóng gói và mở các phương thức riêng tư để mã hóa trong cùng một gói. Mặc dù tôi đồng ý rằng điều này phá vỡ đóng gói và mở mã riêng cho các lớp khác, tôi cho rằng việc kiểm tra logic phức tạp quan trọng hơn việc đóng gói chặt chẽ và không sử dụng các phương thức riêng tư được đánh dấu rõ ràng để kiểm tra chỉ phải là trách nhiệm của các nhà phát triển sử dụng và thay đổi cơ sở mã.
Phương pháp riêng trước khi thử nghiệm:
private int add(int a, int b){
return a + b;
}
Gói phương thức riêng tư đã sẵn sàng để thử nghiệm:
@VisibleForTesting
int add(int a, int b){
return a + b;
}
Lưu ý: Đặt các bài kiểm tra trong cùng một gói không tương đương với việc đặt chúng vào cùng một thư mục vật lý. Việc tách mã chính và mã kiểm tra của bạn thành các cấu trúc thư mục vật lý riêng biệt là cách thực hành tốt nói chung nhưng kỹ thuật này sẽ hoạt động miễn là các lớp được định nghĩa như trong cùng một gói.
Nếu bạn không thể sử dụng API bên ngoài hoặc không muốn, bạn vẫn có thể sử dụng API JDK tiêu chuẩn thuần túy để truy cập các phương thức riêng tư bằng cách sử dụng sự phản chiếu. Đây là một ví dụ
MyObject obj = new MyObject();
Method privateMethod = MyObject.class.getDeclaredMethod("getFoo", null);
privateMethod.setAccessible(true);
String returnValue = (String) privateMethod.invoke(obj, null);
System.out.println("returnValue = " + returnValue);
Kiểm tra Hướng dẫn Java http://docs.oracle.com/javase/tutorial/reflect/ hoặc API Java http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-summary. html để biết thêm thông tin.
Như @kij đã trích dẫn câu trả lời của mình, có những lúc một giải pháp đơn giản sử dụng sự phản chiếu thực sự tốt để thử nghiệm một phương pháp riêng tư.
Trường hợp kiểm thử đơn vị có nghĩa là kiểm tra đơn vị mã. Điều đó không có nghĩa là kiểm tra giao diện vì nếu bạn đang kiểm tra giao diện, điều đó không có nghĩa là bạn đang kiểm tra đơn vị mã. Nó trở thành một loại thử nghiệm hộp đen. Ngoài ra, tốt hơn là tìm các sự cố ở cấp đơn vị nhỏ nhất hơn là xác định các sự cố ở cấp giao diện và sau đó cố gắng gỡ lỗi phần nào không hoạt động. Do đó, trường hợp thử nghiệm đơn vị nên được kiểm tra bất kể phạm vi của chúng. Sau đây là một cách để kiểm tra các phương pháp riêng tư.
Nếu bạn đang sử dụng java, bạn có thể sử dụng jmockit cung cấp Deencapsulation.invoke để gọi bất kỳ phương thức riêng tư nào của lớp đang thử nghiệm. Nó sử dụng sự phản chiếu để gọi nó cuối cùng nhưng cung cấp một trình bao bọc đẹp xung quanh nó. ( https://code.google.com.vn/p/jmockit/ )
Trước hết, như các tác giả khác đề xuất: hãy suy nghĩ kỹ nếu bạn thực sự cần thử nghiệm phương pháp riêng tư. Và nếu thế, ...
Trong .NET, bạn có thể chuyển đổi nó thành phương thức "Internal" và tạo gói " InternalVisible " cho dự án thử nghiệm đơn vị của bạn.
Trong Java, bạn có thể tự viết các bài kiểm tra trong lớp để kiểm tra và các phương thức kiểm tra của bạn cũng có thể gọi các phương thức riêng tư. Tôi thực sự không có kinh nghiệm Java lớn, vì vậy đó có lẽ không phải là cách thực hành tốt nhất.
Cảm ơn.
Tôi thường làm cho các phương pháp như vậy được bảo vệ. Giả sử lớp học của bạn ở:
src/main/java/you/Class.java
Bạn có thể tạo một lớp kiểm tra như:
src/test/java/you/TestClass.java
Bây giờ bạn có quyền truy cập vào các phương thức được bảo vệ và có thể đơn vị kiểm tra chúng (JUnit hoặc TestNG không thực sự quan trọng), nhưng bạn vẫn giữ các phương thức này từ những người gọi mà bạn không muốn.
Lưu ý rằng điều này mong đợi một cây nguồn kiểu maven.
Nếu bạn thực sự cần thử nghiệm phương thức riêng tư, với ý tôi là Java, bạn có thể sử dụng fest assert
và / hoặc fest reflect
. Nó sử dụng sự phản chiếu.
Nhập thư viện bằng maven (phiên bản đã cho không phải là phiên bản mới nhất tôi nghĩ) hoặc nhập trực tiếp vào thư viện lớp của bạn:
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-reflect</artifactId>
<version>1.2</version>
</dependency>
Ví dụ: nếu bạn có một lớp có tên 'MyClass' với phương thức riêng có tên 'myPrivateMethod', lấy Chuỗi làm tham số để cập nhật giá trị của nó thành 'đây là thử nghiệm tuyệt vời!', Bạn có thể thực hiện bài kiểm tra Junit sau:
import static org.fest.reflect.core.Reflection.method;
...
MyClass objectToTest;
@Before
public void setUp(){
objectToTest = new MyClass();
}
@Test
public void testPrivateMethod(){
// GIVEN
String myOriginalString = "toto";
// WHEN
method("myPrivateMethod").withParameterTypes(String.class).in(objectToTest).invoke(myOriginalString);
// THEN
Assert.assertEquals("this is cool testing !", myOriginalString);
}
Thư viện này cũng cho phép bạn thay thế bất kỳ thuộc tính bean nào (bất kể chúng là riêng tư và không có setters nào được viết) bằng một giả, và sử dụng nó với Mockito hoặc bất kỳ khung giả nào khác thực sự tuyệt vời. Điều duy nhất bạn phải biết vào lúc này (không biết liệu điều này sẽ tốt hơn trong các phiên bản tiếp theo hay không) là tên của trường mục tiêu / phương thức bạn muốn thao tác và chữ ký của nó.
Những gì tôi thường làm trong C # là làm cho các phương thức của tôi được bảo vệ và không riêng tư. Đó là một công cụ sửa đổi truy cập riêng tư ít hơn một chút, nhưng nó ẩn phương thức khỏi tất cả các lớp không kế thừa từ lớp đang thử nghiệm.
public class classUnderTest
{
//this would normally be private
protected void methodToTest()
{
//some code here
}
}
Bất kỳ lớp nào không kế thừa trực tiếp từ classUnderTest đều không biết rằng phương thức ToTest thậm chí còn tồn tại. Trong mã thử nghiệm của mình, tôi có thể tạo một lớp thử nghiệm đặc biệt mở rộng và cung cấp quyền truy cập vào phương thức này ...
class TestingClass : classUnderTest
{
public void methodToTest()
{
//this returns the method i would like to test
return base.methodToTest();
}
}
Lớp này chỉ tồn tại trong dự án thử nghiệm của tôi. Mục đích duy nhất của nó là cung cấp quyền truy cập vào phương pháp duy nhất này. Nó cho phép tôi truy cập vào những nơi mà hầu hết các lớp khác không có.
protected
là một phần của API công khai và phải chịu những hạn chế tương tự (không bao giờ có thể thay đổi, phải được ghi lại, ...). Trong C # bạn có thể sử dụng internal
cùng với InternalsVisibleTo
.
Bạn có thể kiểm tra các phương thức riêng tư một cách dễ dàng nếu bạn đặt các bài kiểm tra đơn vị của mình trong một lớp bên trong vào lớp bạn đang kiểm tra. Sử dụng TestNG, các bài kiểm tra đơn vị của bạn phải là các lớp bên trong tĩnh công khai được chú thích bằng @Test, như thế này:
@Test
public static class UserEditorTest {
public void test_private_method() {
assertEquals(new UserEditor().somePrivateMethod(), "success");
}
}
Vì nó là một lớp bên trong, nên phương thức riêng có thể được gọi.
Các thử nghiệm của tôi được chạy từ maven và nó tự động tìm thấy các trường hợp thử nghiệm này. Nếu bạn chỉ muốn kiểm tra một lớp, bạn có thể làm
$ mvn test -Dtest=*UserEditorTest
Nguồn: http://www.ninthavenue.com.au/how-to-unit-test-private-methods