Tôi có thực sự cần một khung kiểm tra đơn vị?


19

Hiện tại trong công việc của tôi, chúng tôi có một bộ thử nghiệm đơn vị lớn cho ứng dụng C ++ của chúng tôi. Tuy nhiên, chúng tôi không sử dụng khung kiểm tra đơn vị. Họ chỉ đơn giản sử dụng một macro C về cơ bản bao bọc một xác nhận và một cout. Cái gì đó như:

VERIFY(cond) if (!(cond)) {std::cout << "unit test failed at " << __FILE__ << "," << __LINE__; asserst(false)}

Sau đó, chúng tôi chỉ cần tạo các hàm cho từng thử nghiệm của mình như

void CheckBehaviorYWhenXHappens()
{
    // a bunch of code to run the test
    //
    VERIFY(blah != blah2);
    // more VERIFY's as needed
}

Máy chủ CI của chúng tôi chọn "thử nghiệm đơn vị không thành công" và không xây dựng được, gửi email thông báo cho các nhà phát triển.

Và nếu chúng ta có mã thiết lập trùng lặp, chúng ta chỉ cần cấu trúc lại nó giống như bất kỳ mã trùng lặp nào khác mà chúng ta có trong sản xuất. Chúng tôi gói nó phía sau các hàm trợ giúp, làm cho một số lớp kiểm tra bao bọc thiết lập các kịch bản được sử dụng thường xuyên.

Tôi biết có các khung ngoài đó như CppUnit và tăng cường kiểm tra đơn vị. Tôi đang tự hỏi những giá trị nào làm thêm? Tôi có thiếu những gì những thứ này mang đến bàn không? Có điều gì hữu ích tôi có thể đạt được từ họ? Tôi do dự để thêm một phụ thuộc trừ khi nó thêm giá trị thực sự đặc biệt vì có vẻ như những gì chúng ta có là đơn giản và hoạt động tốt.

Câu trả lời:


8

Như những người khác đã nói, bạn đã có khuôn khổ làm tại nhà, đơn giản của riêng bạn.

Nó có vẻ là tầm thường để làm cho một. Tuy nhiên, có một số tính năng khác của khung kiểm tra đơn vị không dễ thực hiện vì chúng có một số kiến ​​thức nâng cao về ngôn ngữ. Các tính năng tôi thường yêu cầu từ khung kiểm tra và không dễ để homebrew là:

  • Tự động thu thập các trường hợp thử nghiệm. Tức là xác định phương thức thử nghiệm mới là đủ để thực hiện nó. JUnit tự động thu thập tất cả các phương thức có tên bắt đầu test, NUnit có [Test]chú thích, Boost.Test sử dụng các macro BOOST_AUTO_TEST_CASEBOOST_FIXTURE_TEST_CASEmacro.

    Đó chủ yếu là sự tiện lợi, nhưng mỗi chút tiện lợi bạn có thể nhận được đều cải thiện cơ hội các nhà phát triển sẽ thực sự viết các bài kiểm tra họ cần và họ sẽ kết nối chúng một cách chính xác. Nếu bạn có hướng dẫn dài, ai đó sẽ bỏ lỡ một phần trong số họ bây giờ và hơn thế nữa và có lẽ một số bài kiểm tra sẽ không được chạy và sẽ không có ai chú ý.

  • Khả năng chạy các trường hợp thử nghiệm được chọn, mà không cần chỉnh sửa mã và biên dịch lại. Bất kỳ khung kiểm tra đơn vị phù hợp nào cũng cho phép bạn chỉ định kiểm tra nào bạn muốn chạy trên dòng lệnh. Nếu bạn muốn gỡ lỗi trong các bài kiểm tra đơn vị (đó là điểm quan trọng nhất đối với nhiều nhà phát triển), bạn cần có thể chọn chỉ một số để chạy, mà không cần chỉnh sửa mã ở mọi nơi.

    Giả sử bạn vừa nhận được báo cáo lỗi # 4211 và nó có thể được sao chép bằng thử nghiệm đơn vị. Vì vậy, bạn viết một, nhưng hơn bạn cần nói với người chạy chỉ chạy thử nghiệm đó, để bạn có thể gỡ lỗi những gì thực sự sai ở đó.

  • Khả năng đánh dấu các bài kiểm tra thất bại dự kiến, trên mỗi trường hợp kiểm tra, mà không sửa đổi bản kiểm tra. Chúng tôi thực sự đã chuyển đổi khuôn khổ tại nơi làm việc để có được cái này.

    Bất kỳ bộ thử nghiệm cỡ nào cũng sẽ có các thử nghiệm thất bại vì các tính năng mà chúng thử nghiệm chưa được triển khai, chưa hoàn thành, chưa có ai có thời gian để sửa chúng hoặc một cái gì đó. Không có khả năng đánh dấu các bài kiểm tra là thất bại dự kiến, bạn sẽ không nhận thấy một thất bại khác khi có một số thường xuyên, vì vậy các bài kiểm tra ngừng phục vụ mục đích chính của chúng.


cảm ơn tôi nghĩ rằng đây là câu trả lời tốt nhất Ngay bây giờ macro của tôi thực hiện công việc của mình, nhưng tôi không thể thực hiện bất kỳ tính năng nào bạn đề cập.
Doug T.

1
@Jan Hudec "Chủ yếu là sự tiện lợi, nhưng mọi tiện ích bạn có thể nhận được đều cải thiện cơ hội các nhà phát triển sẽ thực sự viết các bài kiểm tra họ cần và họ sẽ kết nối chúng một cách chính xác."; Tất cả các khung kiểm tra là (1) không cần thiết để cài đặt, thường có các hướng dẫn cài đặt lỗi thời hoặc không hoàn chỉnh hơn các hướng dẫn hợp lệ cập nhật; (2) nếu bạn cam kết trực tiếp với khung kiểm tra, không có giao diện ở giữa, bạn đã kết hôn với khung đó, việc chuyển đổi khung không phải lúc nào cũng dễ dàng.
Dmitry

@Jan Hudec Nếu chúng tôi mong muốn có nhiều người viết bài kiểm tra đơn vị, chúng tôi phải có nhiều kết quả hơn trên google cho "Bài kiểm tra đơn vị là gì", hơn là "Kiểm tra đơn vị là gì". Không có điểm nào để thực hiện Kiểm thử đơn vị nếu bạn không biết Kiểm thử đơn vị là gì độc lập với bất kỳ Khung kiểm tra đơn vị hoặc định nghĩa nào về Kiểm thử đơn vị. Bạn không thể thực hiện Kiểm tra đơn vị trừ khi bạn có hiểu biết sâu sắc về Kiểm tra đơn vị là gì, vì nếu không, không có điểm nào thực hiện Kiểm tra đơn vị.
Dmitry

Tôi không mua đối số tiện lợi này. Viết mã kiểm tra là rất khó nếu bạn rời khỏi thế giới tầm thường của các ví dụ. Tất cả các mockup, thiết lập, thư viện, chương trình máy chủ mockup bên ngoài, v.v ... Tất cả đều yêu cầu bạn biết khung kiểm tra từ trong ra ngoài.
Lothar

@Lothar, vâng, đó là tất cả rất nhiều công việc và rất nhiều thứ để học, nhưng vẫn phải viết nhiều lần đơn giản vì bạn thiếu một vài tiện ích hữu ích làm cho công việc trở nên dễ chịu hơn nhiều và điều đó tạo ra sự khác biệt rõ rệt về hiệu quả.
Jan Hudec

27

Có vẻ như bạn đã sử dụng một khung, một khung làm tại nhà.

Giá trị gia tăng của các khung phổ biến hơn là gì? Tôi sẽ nói rằng giá trị họ thêm vào là khi bạn phải trao đổi mã với những người bên ngoài công ty của bạn, bạn có thể làm điều đó, vì nó dựa trên khuôn khổ được biết đến và sử dụng rộng rãi .

Mặt khác, một khung làm tại nhà, buộc bạn không bao giờ chia sẻ mã của mình hoặc tự cung cấp khung, điều này có thể trở nên cồng kềnh với sự phát triển của chính khung đó.

Nếu bạn đưa mã của mình cho đồng nghiệp, không có lời giải thích và không có khung kiểm tra đơn vị, anh ta sẽ không thể biên dịch mã.

Một nhược điểm thứ hai của các khung làm tại nhà là tính tương thích . Các khung kiểm tra đơn vị phổ biến có xu hướng đảm bảo khả năng tương thích với các IDE, hệ thống kiểm soát phiên bản khác nhau, v.v. Hiện tại, nó có thể không quan trọng lắm đối với bạn, nhưng điều gì sẽ xảy ra nếu một ngày nào đó bạn sẽ cần thay đổi thứ gì đó trong máy chủ CI của mình hoặc di chuyển đến một IDE mới hay một VCS mới? Bạn sẽ phát minh lại bánh xe?

Cuối cùng nhưng không kém phần quan trọng, các khung lớn hơn cung cấp nhiều tính năng hơn mà bạn có thể cần triển khai trong khung riêng của mình một ngày nào đó. Assert.AreEqual(expected, actual)không phải lúc nào cũng đủ. Nếu bạn cần:

  • Đo chính xác?

    Assert.AreEqual(3.1415926535897932384626433832795, actual, 25)
    
  • Kiểm tra void nếu nó chạy quá lâu? Việc thực hiện lại thời gian chờ có thể không đơn giản ngay cả trong các ngôn ngữ tạo điều kiện cho lập trình không đồng bộ.

  • kiểm tra một phương pháp dự kiến ​​sẽ ném một ngoại lệ?

  • Có một mã thanh lịch hơn?

    Assert.Verify(a == null);
    

    thay vào đó là tốt, nhưng nó không thể hiện rõ hơn ý định của bạn để viết dòng tiếp theo chứ?

    Assert.IsNull(a);
    

"Khung" chúng tôi sử dụng là tất cả trong một tệp tiêu đề rất nhỏ và tuân theo ngữ nghĩa của khẳng định. Vì vậy, tôi không quá lo lắng về những hạn chế mà bạn liệt kê.
Doug T.

4
Tôi coi các khẳng định là phần tầm thường nhất của khung kiểm tra. Người chạy thu thập và chạy các trường hợp kiểm tra và kiểm tra kết quả là phần quan trọng không tầm thường.
Jan Hudec

@ Tôi không làm theo. Người chạy của tôi là một thói quen chính phổ biến cho mọi chương trình C ++. Liệu một người chạy khung kiểm tra đơn vị làm một cái gì đó tinh vi và hữu ích hơn?
Doug T.

1
Khung của bạn chỉ cho phép các ngữ nghĩa của xác nhận và chạy thử nghiệm trong một phương thức chính ... cho đến nay. Chỉ cần đợi cho đến khi bạn phải nhóm các xác nhận của mình thành nhiều kịch bản, các kịch bản liên quan đến nhóm cùng nhau dựa trên dữ liệu được khởi tạo, v.v.
James Kingsbery

@DougT.: Vâng, người chạy bộ kiểm tra đơn vị đàng hoàng thực hiện một số điều hữu ích tinh vi hơn. Xem câu trả lời đầy đủ của tôi.
Jan Hudec

4

Như những người khác đã nói, bạn đã có khung làm việc tại nhà của riêng bạn.

Lý do duy nhất tôi có thể thấy để sử dụng một số khung kiểm tra khác là từ quan điểm "kiến thức chung" của ngành. Các nhà phát triển mới sẽ không phải học cách làm tại nhà của bạn (mặc dù, nó trông rất đơn giản).

Ngoài ra, các khung kiểm tra khác có thể có nhiều tính năng hơn mà bạn có thể tận dụng.


1
Đã đồng ý. Nếu bạn không gặp phải những hạn chế với chiến lược thử nghiệm hiện tại của mình, tôi thấy ít lý do để thay đổi. Một khung tốt có thể sẽ cung cấp khả năng tổ chức và báo cáo tốt hơn, nhưng bạn sẽ phải chứng minh công việc bổ sung cần thiết để tích hợp với cơ sở mã của bạn (bao gồm cả hệ thống xây dựng của bạn).
TMN

3

Bạn đã có một khung ngay cả khi nó là một khung đơn giản.

Những ưu điểm chính của một khung lớn hơn như tôi thấy là khả năng có nhiều loại xác nhận khác nhau (chẳng hạn như tăng giá trị), một thứ tự hợp lý cho các bài kiểm tra đơn vị và khả năng chỉ chạy một tập hợp các bài kiểm tra đơn vị tại một thời gian. Ngoài ra, mẫu thử nghiệm xUnit khá đẹp để theo dõi nếu bạn có thể - ví dụ: setUP () và tornDown (). Tất nhiên, điều đó khóa bạn vào khuôn khổ nói. Lưu ý rằng một số khung có tích hợp giả tốt hơn các khung khác - ví dụ như google giả và kiểm tra.

Sẽ mất bao lâu để bạn cấu trúc lại tất cả các bài kiểm tra đơn vị của bạn sang một khung mới? Ngày hoặc một vài tuần có thể đáng giá nhưng nhiều hơn có thể không quá nhiều.


2

Theo cách tôi thấy, cả hai bạn đều có lợi thế và bạn đang ở thế "bất lợi" (sic).

Ưu điểm là bạn có một hệ thống mà bạn cảm thấy thoải mái và phù hợp với bạn. Bạn rất vui khi nó xác nhận tính hợp lệ của sản phẩm của bạn và có lẽ bạn sẽ không tìm thấy giá trị kinh doanh nào khi cố gắng thay đổi tất cả các thử nghiệm của mình cho một thứ sử dụng khung khác. Nếu bạn có thể cấu trúc lại mã của mình và các thử nghiệm của bạn sẽ nhận được các thay đổi - hoặc tốt hơn nữa, nếu bạn có thể sửa đổi các thử nghiệm của mình và mã hiện tại của bạn thất bại các thử nghiệm cho đến khi nó được tái cấu trúc, thì bạn có tất cả các cơ sở của mình. Tuy nhiên...

Một trong những lợi thế của việc có API thử nghiệm đơn vị được thiết kế tốt là có rất nhiều hỗ trợ riêng trong hầu hết các IDE hiện đại. Điều này sẽ không ảnh hưởng đến những người dùng VI và những người dùng khó tính ngoài kia đã chế nhạo những người dùng Visual Studio ngoài kia, nhưng đối với những người sử dụng sử dụng một IDE tốt, bạn có khả năng gỡ lỗi các bài kiểm tra của mình và thực hiện chúng trong IDE chính nó. Điều này là tốt, tuy nhiên có một lợi thế thậm chí còn lớn hơn tùy thuộc vào khung bạn sử dụng và đó là ngôn ngữ được sử dụng để kiểm tra mã của bạn.

Khi tôi nói ngôn ngữ , tôi không nói về ngôn ngữ lập trình, mà thay vào đó tôi đang nói về một từ có tập hợp phong phú được gói gọn trong một cú pháp trôi chảy khiến mã kiểm tra đọc giống như một câu chuyện. Đặc biệt, tôi đã trở thành một người ủng hộ việc sử dụng các khung BDD . API DotNet BDD yêu thích của tôi là StoryQ, nhưng có một số mục đích khác có cùng mục đích cơ bản, đó là lấy một khái niệm ra khỏi tài liệu yêu cầu và viết nó theo mã tương tự như cách nó được viết trong thông số kỹ thuật. Tuy nhiên, các API thực sự tốt thậm chí còn đi xa hơn, bằng cách chặn mọi câu lệnh riêng lẻ trong một bài kiểm tra và cho biết liệu câu lệnh đó được thực hiện thành công hay thất bại. Điều này cực kỳ hữu ích, vì bạn có thể thấy toàn bộ thử nghiệm được thực hiện mà không cần quay lại sớm, điều đó có nghĩa là các nỗ lực sửa lỗi của bạn trở nên cực kỳ hiệu quả vì bạn chỉ cần tập trung sự chú ý vào các phần của thử nghiệm thất bại, mà không cần phải giải mã toàn bộ cuộc gọi sự nối tiếp. Một điều tuyệt vời khác là đầu ra thử nghiệm cho bạn thấy tất cả các thông tin này,

Để làm ví dụ về những gì tôi đang nói, hãy so sánh như sau:

Sử dụng khẳng định:

Assert(variable_A == expected_value_1); // if this fails...
Assert(variable_B == expected_value_2); // ...this will not execute
Assert(variable_C == expected_value_3); // ...and nor will this!

Sử dụng API BDD trôi chảy: (Hãy tưởng tượng rằng các bit in nghiêng về cơ bản là các con trỏ phương thức)

WithScenario("Test Scenario")
    .Given(*AConfiguration*) // each method
    .When(*MyMethodToTestIsCalledWith*, variable_A, variable_B, variable_C) // in the
    .Then(*ExpectVariableAEquals*, expected_value_1) // Scenario will
        .And(*ExpectVariableBEquals*, expected_value_2) // indicate if it has
        .And(*ExpectVariableCEquals*, expected_value_3) // passed or failed execution.
    .Execute();

Bây giờ được cấp cú pháp BDD dài hơn và dài hơn, và các ví dụ này bị chiếm dụng khủng khiếp, tuy nhiên đối với các tình huống kiểm tra rất phức tạp khi có nhiều thứ đang thay đổi trong một hệ thống do một hành vi hệ thống nhất định, cú pháp BDD cung cấp cho bạn rõ ràng mô tả về những gì bạn đang thử nghiệm và cách xác định cấu hình thử nghiệm của bạn và bạn có thể hiển thị mã này cho người không lập trình và họ sẽ hiểu ngay những gì đang diễn ra. Ngoài ra, nếu "biến_A" không thực hiện kiểm tra trong cả hai trường hợp, ví dụ Xác nhận sẽ không thực hiện quá trình xác nhận đầu tiên cho đến khi bạn đã khắc phục sự cố, trong khi đó, API BDD sẽ thực thi mọi phương thức được gọi trong chuỗi, và chỉ ra các phần riêng lẻ của tuyên bố đã bị lỗi.

Cá nhân tôi thấy cách tiếp cận này hoạt động tốt hơn nhiều so với các khung xUnit truyền thống hơn theo nghĩa là ngôn ngữ kiểm tra là ngôn ngữ giống như khách hàng của bạn sẽ nói về các yêu cầu logic của họ. Mặc dù vậy, tôi đã quản lý để sử dụng các khung xUnit theo kiểu tương tự mà không cần phải phát minh ra API thử nghiệm hoàn chỉnh để hỗ trợ các nỗ lực của mình và trong khi các xác nhận vẫn sẽ tự ngắt mạch một cách hiệu quả, chúng đọc sạch hơn. Ví dụ:

Sử dụng Nunit :

[Test]
void TestMyMethod()
{
    const int theExpectedValue = someValue;

    GivenASetupToTestMyMethod();

    var theActualValue = WhenIExecuteMyMethodToTest();

    Assert.That(theActualValue, Is.EqualTo(theExpectedValue)); // nice, but it's not BDD
}

Nếu bạn quyết định khám phá bằng cách sử dụng API thử nghiệm đơn vị, lời khuyên của tôi là hãy thử nghiệm một số lượng lớn các API khác nhau trong một thời gian và để giữ và cởi mở về cách tiếp cận của bạn. Trong khi cá nhân tôi ủng hộ cho BDD, nhu cầu kinh doanh của riêng bạn có thể yêu cầu điều gì đó khác biệt đối với hoàn cảnh của nhóm bạn. Tuy nhiên, điều quan trọng là tránh đoán thứ hai hệ thống hiện tại của bạn. Bạn luôn có thể hỗ trợ các thử nghiệm hiện tại của mình bằng một vài thử nghiệm bằng API khác nếu cần, nhưng tôi chắc chắn sẽ không đề xuất một thử nghiệm khổng lồ viết lại chỉ để làm mọi thứ giống nhau. Khi mã kế thừa không còn sử dụng, bạn có thể dễ dàng thay thế nó và các thử nghiệm của nó bằng mã mới và thử nghiệm bằng API thay thế, và điều này mà không cần đầu tư vào một nỗ lực lớn mà không nhất thiết phải cung cấp cho bạn bất kỳ giá trị kinh doanh thực sự nào. Đối với việc sử dụng API thử nghiệm đơn vị,


1

Những gì bạn có là đơn giản và hoàn thành công việc. Nếu nó làm việc cho bạn, tuyệt vời. Bạn không cần một khung kiểm tra đơn vị chính thống và tôi sẽ ngần ngại chuyển sang một thư viện kiểm tra đơn vị hiện có sang một khung mới. Tôi nghĩ rằng giá trị lớn nhất của các khung kiểm tra đơn vị là giảm bớt rào cản gia nhập; bạn chỉ cần bắt đầu viết bài kiểm tra, vì khung đã sẵn sàng. Bạn đã qua thời điểm đó, vì vậy bạn sẽ không nhận được lợi ích đó.

Lợi ích khác của việc sử dụng khung chính thống - và đó là lợi ích nhỏ, IMO - là các nhà phát triển mới có thể đã tăng tốc trên bất kỳ khung nào bạn đang sử dụng và do đó sẽ cần ít đào tạo hơn. Trong thực tế, với cách tiếp cận đơn giản như những gì bạn đã mô tả, đây không phải là một vấn đề lớn.

Ngoài ra, hầu hết các khung chính có các tính năng nhất định mà khung của bạn có thể có hoặc không có. Các tính năng này làm giảm mã hệ thống ống nước và giúp viết các trường hợp kiểm tra nhanh hơn và dễ dàng hơn:

  • Tự động chạy các trường hợp thử nghiệm, bằng cách sử dụng các quy ước đặt tên, chú thích / thuộc tính, v.v.
  • Nhiều xác nhận cụ thể hơn, để bạn không phải viết logic có điều kiện cho tất cả các xác nhận của mình hoặc bắt ngoại lệ để xác nhận loại của chúng.
  • Phân loại các trường hợp thử nghiệm, vì vậy bạn có thể dễ dàng chạy các tập hợp con của chú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.