2016-03-07 14 views
5

Tôi đang sử dụng CATCH v1.1 build 14 để kiểm tra đơn vị mã C++ của tôi.Kiểm tra đơn vị Catch.hpp: Cách tạo động các trường hợp thử nghiệm?

Là một phần của thử nghiệm, tôi muốn kiểm tra kết quả đầu ra của một số mô-đun trong mã của tôi. Không có một số mô-đun được thiết lập; nhiều mô-đun hơn có thể được thêm vào bất kỳ lúc nào. Tuy nhiên, mã để kiểm tra từng mô-đun là giống hệt nhau. Vì vậy, tôi nghĩ rằng nó sẽ là lý tưởng để đặt mã thử nghiệm trong một vòng lặp for. Trong thực tế, sử dụng catch.hpp, tôi đã xác minh rằng tôi có thể tự động tạo các Phần trong một Trường hợp thử nghiệm, trong đó mỗi Phần tương ứng với một mô-đun. Tôi có thể làm điều này bằng cách kèm theo các SECTION vĩ mô trong một vòng lặp for, ví dụ:

#include "catch.hpp" 
#include <vector> 
#include <string> 
#include "myHeader.h" 

TEST_CASE("Module testing", "[module]") { 
    myNamespace::myManagerClass manager; 
    std::vector<std::string> modList; 
    size_t n; 

    modList = manager.getModules(); 
    for (n = 0; n < modList.size(); n++) { 
     SECTION(modList[n].c_str()) { 
      REQUIRE(/*insert testing code here*/); 
     } 
    } 
} 

(Đây không phải là một ví dụ làm việc hoàn chỉnh, nhưng bạn sẽ có được ý tưởng.)

Đây là tiến thoái lưỡng nan của tôi. Tôi muốn kiểm tra các mô-đun một cách độc lập, như vậy nếu một mô-đun bị lỗi, nó sẽ tiếp tục thử nghiệm các mô-đun khác thay vì hủy bỏ thử nghiệm. Tuy nhiên, cách CATCH hoạt động, nó sẽ hủy bỏ toàn bộ Trường hợp kiểm tra nếu REQUIRE không thành công. Vì lý do này, tôi muốn tạo một Trường hợp thử nghiệm riêng biệt cho mỗi mô-đun, không chỉ là một Phần riêng biệt. Tôi đã thử đưa for vòng lặp của tôi ngoài TEST_CASE vĩ mô, nhưng mã này không biên dịch (như tôi dự kiến):

#include "catch.hpp" 
#include <vector> 
#include <string> 
#include "myHeader.h" 

myNamespace::myManagerClass manager; 
std::vector<std::string> modList; 
size_t n; 

modList = manager.getModules(); 
for (n = 0; n < modList.size(); n++) { 
    TEST_CASE("Module testing", "[module]") { 
     SECTION(modList[n].c_str()) { 
      REQUIRE(/*insert testing code here*/); 
     } 
    } 
} 

Nó có thể là có thể làm điều này bằng cách writing my ownmain(), nhưng tôi không thể nhìn thấy làm thế nào để làm nó chính xác. (Tôi có đặt mã TEST_CASE tôi trực tiếp vào main()? Nếu tôi muốn giữ mã TEST_CASE của tôi trong một tập tin khác nhau? Ngoài ra, sẽ có ảnh hưởng khác, Test Cases chuẩn hơn tôi?)

Tôi cũng có thể sử dụng CHECK macro thay vì REQUIRE các macro để tránh hủy bỏ Trường hợp kiểm tra khi mô-đun bị lỗi, nhưng sau đó tôi nhận được vấn đề ngược lại: Nó cố gắng tiếp tục thử nghiệm trên mô-đun nên đã không hoạt động sớm. Nếu tôi chỉ có thể đặt từng mô-đun trong Case Test riêng của mình, điều đó sẽ cho tôi một hành vi lý tưởng.

Có cách đơn giản để tự động tạo các trường hợp thử nghiệm trong CATCH không? Nếu vậy, bạn có thể cho tôi một ví dụ về làm thế nào để làm điều đó? Tôi đọc qua tài liệu CATCH và tìm kiếm trực tuyến, nhưng tôi không thể tìm thấy bất kỳ dấu hiệu nào về cách thực hiện điều này.

Trả lời

0

Nghe có vẻ như Catch có thể di chuyển về phía thử nghiệm dựa trên thuộc tính, mà tôi hy vọng sẽ cho phép một cách tự động tạo ra các trường hợp thử nghiệm. Trong thời gian chờ đợi, đây là những gì tôi đã làm.

Tôi đã tạo tệp .cpp với một đơn TEST_CASE cho một mô-đun duy nhất và biến toàn cầu cho tên mô-đun. (Vâng, tôi biết các biến toàn cục là ác, đó là lý do tôi cẩn thận và sử dụng nó như là phương sách cuối cùng):

module_unit_test.cpp:

#include "catch.hpp" 
#include <string> 
#include "myHeader.h" 

extern const std::string g_ModuleName; // global variable: module name 

TEST_CASE("Module testing", "[module]") { 
    myNamespace::myManagerClass manager; 
    myNamespace::myModuleClass *pModule; 
    SECTION(g_ModuleName.c_str()) { 
     pModule = manager.createModule(g_ModuleName.c_str()); 
     REQUIRE(pModule != 0); 
     /*insert more testing code here*/ 
    } 
} 

Sau đó, tôi tạo ra một thực thi mà sẽ chạy thử nghiệm này trên một mô-đun duy nhất được chỉ định trên dòng lệnh. (Tôi đã thử lặp qua Catch::Session().run() bên dưới, nhưng Catch không cho phép nó chạy nhiều lần.) Tệp đối tượng từ mã bên dưới module_test.cpp và từ mã kiểm tra đơn vị ở trên module_unit_test.cpp được liên kết khi tạo tệp thực thi.

module_test.cpp:

#define CATCH_CONFIG_RUNNER 
#include "catch.hpp" 
#include <string> 
#include <cstdio> 

std::string g_ModuleName; // global variable: module name 

int main(int argc, char* argv[]) { 
    // Make sure the user specified a module name. 
    if (argc < 2) { 
     std::cout << argv[0] << " <module name> <Catch options>" << std::endl; 
     return 1; 
    } 

    size_t n; 
    char* catch_argv[argc-1]; 
    int result; 

    // Modify the input arguments for the Catch Session. 
    // (Remove the module name, which is only used by this program.) 
    catch_argv[0] = argv[0]; 
    for (n = 2; n < argc; n++) { 
     catch_argv[n-1] = argv[n]; 
    } 

    // Set the value of the global variable. 
    g_ModuleName = argv[1]; 

    // Run the test with the modified command line arguments. 
    result = Catch::Session().run(argc-1, catch_argv); 

    return result; 
} 

Sau đó, tôi các vòng lặp trong một thực thi riêng biệt (không liên quan đến các tập tin đối tượng từ các mã trên):

module_test_all.cpp:

#include <cstdlib> 
#include <vector> 
#include <string> 
#include "myHeader.h" 

int main(int argc, char* argv[]) { 
    std::string commandStr; 
    int result, status = 0; 
    myNamespace::myManagerClass manager; 
    std::vector<std::string> modList; 
    size_t m, n; 

    // Scan for modules. 
    modList = manager.getModules(); 

    // Loop through the module list. 
    for (n = 0; n < modList.size(); n++) { 
     // Build the command line. 
     commandStr = "module_test " + modList[n]; 
     for (m = 1; m < argc; m++) { 
      commandStr += " "; 
      commandStr += argv[m]; 
     } 

     // Do a system call to the first executable. 
     result = system(commandStr.c_str()); 

     // If a test fails, I keep track of the status but continue 
     // looping so all the modules get tested. 
     status = status ? status : result; 
    } 

    return status; 
} 

Có , nó xấu, nhưng tôi đã xác nhận rằng nó hoạt động.

1

Có một cách để đạt được những gì bạn đang tìm kiếm, nhưng tôi muốn quan sát rằng bạn đang đi về việc này một cách sai lầm: -

Unit tests là để nhằm kiểm tra mỗi đơn vị, tức là bạn viết một thành phần và một thử nghiệm để xác minh hành vi chính xác của thành phần đó. Nếu sau này bạn quyết định thay đổi một thành phần theo một cách nào đó, bạn cập nhật bài kiểm tra tương ứng.

Nếu bạn tổng hợp tất cả các thử nghiệm của mình cho tất cả các thành phần của bạn vào cùng một tệp, sẽ trở nên khó khăn hơn khi cô lập đơn vị hoạt động khác.

Nếu bạn muốn yếu tố ra các thử nghiệm của một thành phần bởi vì nó là cơ bản giống nhau trên tất cả các thành phần của bạn, bạn có thể làm một trong các cách sau:

1.Giải nén các bài kiểm tra chung vào một tập tin tiêu đề riêng biệt

Bạn có thể # define tên kiểu của thành phần mà bạn muốn kiểm tra và sau đó bao gồm một tập tin tiêu đề với tất cả các bài kiểm tra trong đó:

// CommonTests.t.h 
#include "catch.hpp" 
TEST_CASE("Object Can be instantiated", "[ctor]") 
{ 
    REQUIRE_NOTHROW(COMPONENT component); 
} 

// SimpleComponent.t.cpp 
#define COMPONENT SimpleComponent 
#include "CommonTests.t.h" 

Đây là đơn giản để làm, nhưng có một nhược điểm - khi bạn chạy thử nghiệm Á hậu, bạn sẽ có các bài kiểm tra trùng lặp (theo tên) để bạn chỉ có thể chạy tất cả các bài kiểm tra, hoặc bằng thẻ.

Bạn có thể giải quyết điều này bằng cách xâu chuỗi tên thành phần và thêm/thêm nó vào tên trường hợp thử nghiệm.

** 2. Gọi xét nghiệm thông thường bởi parameterising thành phần **

Đặt xét nghiệm thông thường của bạn thành một file riêng biệt và gọi các phương pháp thử nghiệm chung trực tiếp:

// CommonTests.t.h 
void RunCommonTests(ComponentInterface& itf); 

// CommonTests.t.cpp 
void RunCommonTests(ComponentInterface& itf) 
{ 
    REQUIRE(itf.answerToLifeUniverseAndEverything() == 42); 
} 

// SimpleComponent.t.cpp 
#include "SimpleComponent.h" 
#include "CommonTest.t.h" 
#include "catch.hpp" 

TEST_CASE("SimpleComponent is default-constructible", "[ctor]") 
{ 
    REQUIRE_NOTHROW(SimpleComponent sc); 
} 

TEST_CASE("SimpleComponent behaves like common components", "[common]") 
{ 
    SimpleComponent sc; 
    RunCommonTests(sc); 
} 
+0

Tôi đang sử dụng đề xuất đầu tiên của bạn và điều đó giúp tôi đạt được hầu hết mọi cách. Lý tưởng nhất, tôi muốn tự động phát hiện các mô-đun trong thời gian chạy và tạo ra một trường hợp thử nghiệm cho mỗi, nhưng tôi bắt đầu nghĩ rằng nó không phải là có thể với Catch. Một số nền: Các mô-đun xác định các lớp dẫn xuất từ ​​một lớp cơ sở trừu tượng mà tôi định nghĩa. Chúng tạo ra các kết quả đầu ra khác nhau, nhưng quy trình kiểm tra đầu ra là như nhau. Các nhà phát triển khác thêm các mô-đun riêng của họ, vì vậy sẽ rất tuyệt nếu họ có thể sử dụng chức năng kiểm tra của chúng tôi để kiểm tra các mô-đun của họ để tuân thủ. Có lẽ tôi đang làm sai, nhưng tôi không thấy một cách tốt hơn. –

+1

Có nhiều cuộc thảo luận về điều này trên Google Group: https://groups.google.com/forum/#!searchin/catch-forum/section/catch-forum/mRBKqtTrITU/FoHEoMn3SN8J – JBRWilkinson

Các vấn đề liên quan