Là chế giễu để thử nghiệm đơn vị thích hợp trong kịch bản này?


8

Tôi đã viết khoảng 20 phương thức trong Java và tất cả chúng đều gọi một số dịch vụ web. Không có dịch vụ web nào trong số này có sẵn. Để tiếp tục với mã hóa phía máy chủ, tôi đã mã hóa các kết quả mà dịch vụ web dự kiến ​​sẽ cung cấp.

Chúng ta có thể đơn vị kiểm tra các phương pháp này? Theo tôi biết, kiểm tra đơn vị đang chế nhạo các giá trị đầu vào và xem chương trình đáp ứng như thế nào. Là chế nhạo cả hai giá trị đầu vào và ouput có ý nghĩa?

Biên tập :

Các câu trả lời ở đây đề nghị tôi nên viết trường hợp kiểm tra đơn vị.

Bây giờ, làm thế nào tôi có thể viết nó mà không sửa đổi mã hiện có?

Xem xét mã mẫu sau (mã giả định):

    public int getAge()
    {
            Service s = locate("ageservice"); // line 1
            int age = s.execute(empId); // line 2
             return age; // line 3

 }

Bây giờ làm thế nào để chúng ta chế nhạo đầu ra?

Ngay bây giờ, tôi đang bình luận 'dòng 1' và thay thế dòng 2 bằng int age= 50. Thê nay đung không ? Bất cứ ai có thể chỉ cho tôi cách làm đúng?

Câu trả lời:


12

Đúng.

Sử dụng giả và sơ khai để mô phỏng hành vi dự kiến ​​của dịch vụ web. Xác thực rằng mã của bạn hoạt động chính xác trong tất cả các giá trị biên và các lớp tương đương dự kiến.

BIÊN TẬP:

Không, bạn không nên chỉnh sửa thủ công bài kiểm tra đơn vị int age= 50. Bạn nên sử dụng phương pháp tiêm phụ thuộc để có thể dễ dàng thay thế Servicebằng đối tượng giả.

public int getAge(Service s)
{
    int age = s.execute(empId); // line 2
    return age; // line 3
}

Bài kiểm tra đơn vị của bạn sẽ trông giống như mã giả sau đây:

public int getAgeMinimumValueTest()
{
    ClassUnderTest uut = new ClassUnderTest();
    MockService service = new MockService();
    service.age = 10;
    int age = uut.getAge(service);
    Assert(age == 10);
}

1
Nếu chúng ta đang chế nhạo cả đầu vào và đầu ra, thì điểm là gì? Làm thế nào để chúng ta chế nhạo đầu ra ở nơi đầu tiên?
Vinoth Kumar CM

1
Khi kiểm thử đơn vị một phương thức, bạn nên tạo một kiểm thử đơn vị cho từng trường hợp sử dụng. Nghĩa là, trong mỗi thử nghiệm đơn vị, bạn chỉ định các giá trị đầu vào khác nhau cho phương thức và xác minh rằng đầu ra là giá trị chính xác. Nếu phương thức có sự phụ thuộc vào một dịch vụ web, bạn giả định dịch vụ web để nó hoạt động như mong đợi trong trường hợp sử dụng đó. Ví dụ: bạn có thể viết mã giả cho dịch vụ web để cung cấp các giá trị được coi là đầu vào cho phương thức.
M. Dudley

3

Có, bạn nên chế giễu dịch vụ của bạn. Mocking là thực hành sử dụng các đối tượng giả để thay thế các đối tượng thực bạn thường sử dụng trong thời gian chạy. Hầu hết thời gian bạn sẽ muốn sử dụng một khung mô phỏng để dễ dàng và linh hoạt tạo các đối tượng giả.

Nếu bạn sử dụng các đối tượng giả, có lẽ bạn sẽ viết mã trông giống như thế này:

public int getAge(ServiceInterface service)
{
    return service.execute(empId);
}

nơi servicelà dịch vụ thực tế bạn sẽ sử dụng trong thời gian chạy, nhưng trong một thử nghiệm đơn vị đó là một đối tượng giả với hành vi giả. Lưu ý rằng tham số getAgekhông còn là một lớp cụ thể, mà thay vào đó là một giao diện. Điều này cho phép bạn sử dụng bất kỳ đối tượng nào làm tham số miễn là nó thực hiện giao diện, thay vì chỉ một lớp cụ thể. Kiểm tra đơn vị của bạn sau đó sẽ thực hiện các bước sau:

  1. Tạo đối tượng giả
  2. Nói với nó để trở về một độ tuổi nhất định khi executeđược gọi
  3. Gọi getAgevới đối tượng giả làm tham số
  4. Xác nhận getAgetrả về tuổi chính xác.

Để có danh sách tốt các khung mô phỏng Java, hãy xem câu hỏi stackoverflow này .

BIÊN TẬP

Đừng bình luận mã của bạn và thay thế nó bằng một hằng số chỉ cho các bài kiểm tra đơn vị của bạn. Vấn đề với điều này là bạn sẽ phải đảm bảo điều đúng được nhận xét khi chạy thử nghiệm đơn vị và điều đúng được nhận xét khi phát hành cho khách hàng. Điều này sẽ biến thành cơn ác mộng cho các mục đích bảo trì dài hạn, đặc biệt nếu bạn có ý định viết nhiều hơn 2 bài kiểm tra yêu cầu giả. Ngoài ra, các thử nghiệm của bạn nên được chạy với sản phẩm hoàn chỉnh ; nếu không, điều đó có nghĩa là bạn không kiểm tra mã thực tế đang được phát hành, điều này phủ nhận mục đích của việc kiểm tra ở nơi đầu tiên.


1

Vâng, đúng vậy. Công việc của bạn là chứng minh rằng mã của bạn làm đúng, với môi trường của nó. Các dịch vụ web bên ngoài là một phần của môi trường; Đây không phải là công việc của bạn để kiểm tra chức năng phù hợp của họ, đó là công việc của những người viết dịch vụ. Ánh xạ đầu vào / đầu ra mà các kiểm tra cần kiểm tra là đầu vào / đầu ra cho mã của bạn ; nếu mã tạo đầu vào cho một cộng tác viên khác thực hiện công việc của mình, điều đó hoàn toàn ngẫu nhiên.

Trên thực tế, ngay cả sau khi các dịch vụ web có sẵn, có lẽ bạn nên giữ các bài kiểm tra đơn vị của mình bằng môi trường giả chứ không phải thực. Nó làm cho các bài kiểm tra đơn giản hơn, ít mong manh hơn đối với bối cảnh hệ thống dự kiến ​​và có lẽ cũng nhanh hơn.


"Có lẽ là một ý tưởng tốt"? Tôi muốn nói là thiết yếu. Bạn không thể đảm bảo Kiểm tra đơn vị đáng tin cậy qua các ranh giới hệ thống.
thehowler

1

Để thêm vào câu trả lời tuyệt vời của emddudley, lợi ích lớn nhất bạn có thể nhận được khi chế nhạo dịch vụ là có thể kiểm tra những gì sẽ xảy ra khi dịch vụ không hoạt động chính xác. Mã giả thử nghiệm có thể trông giống như thế này:

public int AgeMinimumValue_LogsServiceError_Test()
{
    ClassUnderTest uut = new ClassUnderTest();
    MockService service = new MockService();
    service.Throws(new TimeoutException());

    MockLogger logger = new MockLogger();

    try {
        int age = uut.getAge(service, logger);
        Assert.Fail("Exception was not raised by the class under test");
    }
    catch (TimeoutException) {
        Assert(logger.LogError().WasCalled());
    }
}

Và bây giờ việc thực hiện của bạn đã được chỉnh sửa với yêu cầu mới này

public int getAge(Service s, Logger l)
{
    try {
        int age = s.execute(empId);
        return age;
    }
    catch(Exception ex) {
        l.LogError(ex);
        throw;
    }
}

Trong các kịch bản khác, nhiều khả năng bạn sẽ cần phải trả lời các phản hồi phức tạp hơn. Nếu dịch vụ cung cấp xử lý thẻ tín dụng, bạn sẽ cần phản hồi Thành công, Dịch vụ không khả dụng, Thẻ tín dụng đã hết hạn, Số không hợp lệ, v.v. Bằng cách chế tạo dịch vụ, bạn có thể đảm bảo bạn đáp ứng các tình huống này theo cách phù hợp với tình huống của bạn. Trong trường hợp này, bạn phải giả định đầu vào / đầu ra từ dịch vụ và phản hồi bạn nhận được từ việc biết rằng mã tiêu thụ sẽ hoạt động cho tất cả các đầu ra đã biết thực sự có ý nghĩa và có giá trị.

EDIT: Tôi chỉ nhận thấy bạn muốn có thể giả định mà không sửa đổi phương thức hiện có. Để làm điều này, locate("ageservice");phương thức sẽ cần phải được thay đổi để hỗ trợ các đối tượng giả trong các thử nghiệm và định vị dịch vụ thực sự một khi nó đã sẵn sàng. Đây là một biến thể của công cụ định vị dịch vụ , tóm tắt logic để truy xuất việc triển khai dịch vụ bạn đang sử dụng. Một phiên bản nhanh có thể trông như thế này:

public Service locate(string serviceToLocate) {
    if(testEnvironment) // Test environment should be set externally
        return MockService(serviceToLocate);
    else
        return Service(serviceToLocate);
}

Tuy nhiên, khuyến nghị của tôi là chuyển các phụ thuộc dịch vụ vào Trình xây dựng:

public int AgeMinimumValue_LogsServiceError_Test()
{
    MockService service = new MockService();
    service.Throws(new TimeoutException());

    MockLogger logger = new MockLogger();

    ClassUnderTest uut = new ClassUnderTest(service, logger);

    try {
        int age = uut.getAge();
        Assert.Fail("Exception was not raised by the class under test");
    }
    catch (TimeoutException) {
        Assert(logger.LogError().WasCalled());
    }
}

Bây giờ phương thức getAge không còn có trách nhiệm tra cứu dịch vụ vì nó đã bị trừu tượng hóa khỏi lớp hoàn toàn để lại một triển khai tương tự như sau:

public int getAge()
{
    try {
        // _service is a private field set by the constructor
        int age = _service.execute(empId); 
        return age;
    }
    catch(Exception ex) {
         // _logger is a private field set by the constructor
        _logger.LogError(ex);
        throw;
    }
}

0

Là chế nhạo cả hai giá trị đầu vào và ouput có ý nghĩa?

Không, nhưng bạn không chế nhạo giá trị đầu vào. Thử nghiệm của bạn đang được chạy và dịch vụ giả sẽ xác minh rằng phần bên phải của hợp đồng được truy cập. Rằng bạn tình cờ trả lại một giá trị cụ thể là không liên quan.

Mặt khác, nếu phương thức của bạn thực hiện bất kỳ logic nào, thì giá trị trả về có vấn đề. Ở đây bạn đang chế nhạo các đầu vào theo logic (kết quả của dịch vụ web) và kiểm tra đầu ra.


0

Bây giờ, làm thế nào tôi có thể viết nó mà không sửa đổi mã hiện có?

bạn không thể Nhưng bạn có thể tạo giao diện "IMyService" mà bạn có thể lập trình để chống lại tất cả các chữ ký phương thức dịch vụ webservice.

public int getAge()
{
        IMyService s = getService(); 
        int age = s.execute(empId);
         return age; // line 3

}

Trong chế độ Sản xuất getService();sẽ trả về một tham chiếu đến một dịch vụ web đầy đủ chức năng và trong chế độ thử nghiệm, một triển khai thay thế (hoặc giả) trả về dữ liệu giả của bạn.


0

Mocking không phải là về đầu vào hoặc đầu ra của nó về việc thay thế các phụ thuộc bên ngoài. Vì vậy, có nó là thích hợp để viết bài kiểm tra đơn vị và giả định các dịch vụ web bên ngoài.

Bây giờ đối với tin xấu: tùy thuộc vào ngôn ngữ và công cụ có sẵn cho bạn, bạn có thể không thể chế giễu các phụ thuộc bên ngoài trừ khi mã được thiết kế để cho phép bạn. Nếu đây là trường hợp thì bạn sẽ thấy các bài kiểm tra của mình thực sự biến thành các bài kiểm tra tích hợp nhỏ hơn là các bài kiểm tra thuần túy.

Về mặt tích cực, có vẻ như bạn được phép sửa đổi mã (nếu không bạn nhận xét nó như thế nào) chuyển vào một giao diện cho dịch vụ của bạn để cho phép nó bị giả mạo (hoặc sử dụng một số hình thức tiêm phụ thuộc khác )

Cuối cùng, những gì bạn muốn kiểm tra dường như là một trình bao bọc thuần túy xung quanh dịch vụ bên ngoài, hầu như không có gì để kiểm tra đơn vị ở đây ngoài việc bạn gọi dịch vụ. Vì đây về cơ bản là một giao diện, hầu hết các thử nghiệm sẽ phải được thực hiện sau đó ở cấp độ cao hơn (thử nghiệm tích hợp)

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.