2010-05-06 25 views
5

Bạn sẽ kiểm tra đơn vị như thế nào do_int_to_string_conversion?Đơn vị thử nghiệm một chức năng có mục đích là các tác dụng phụ

#include <string> 
#include <iostream> 

void do_int_to_string_conversion(int i, std::string& s) { 
    switch(i) { 
    case 1: 
     s="1"; 
     break; 
    case 2: 
     s="2"; 
     break; 
    default: 
     s ="Nix"; 
    } 
} 

int main(int argc, char** argv){ 
    std::string little_s; 

    do_int_to_string_conversion(1, little_s); 
    do_int_to_string_conversion(2, little_s); 
    do_int_to_string_conversion(3, little_s); 

} 
+0

Bạn chỉ cần đảm bảo rằng hiệu ứng mong muốn đã xảy ra sau cuộc gọi? – jball

+2

Tôi giả sử "tác dụng phụ" trong ví dụ này đề cập đến đầu ra cho std :: cout. –

+0

Tôi phải hỏi, tuy nhiên, tại sao bạn không chỉ trả về một chuỗi thay vì đi qua trong một tham chiếu? Bạn hoàn toàn thay thế cái mà bạn vượt qua, vì vậy bạn không nhận được lợi ích nào từ việc tránh sao chép (vì bạn không tránh nó), và nếu bạn trả về chuỗi, bạn có thể hưởng lợi từ RVO (tối ưu hóa giá trị trả về) . –

Trả lời

9

Tôi cho rằng đây chỉ là một ví dụ. Tại sao bạn không thể khẳng định giá trị của little_s sau mỗi cuộc gọi?

do_int_to_string_conversion(1, little_s); 
assert_are_equal("1", little_s); 
+1

Ý tưởng tuyệt vời. – David

-1

Bạn có thể sử dụng thứ gì đó như Expect để chuyển một số đầu vào và xác minh rằng đầu ra của nó là gì.

3

Nếu bạn thực sự cần phải đảm bảo rằng các đầu ra đã được viết, bạn cần phải phá vỡ sự phụ thuộc của bạn trên std::cout và sử dụng std::ostream khác trong bài kiểm tra.

Điều này có thể đơn giản như một biến toàn cầu:

#if PRODUCTION 
std::ostream my_output = std::cout; 
#else 
std::ostream my_output = std::ostringstream; 
#endif 

void setup() 
{ 
    my_output = std::ostringstream; 
} 

void print_hello() 
{ 
    my_output << "hello"; 
} 

void test_hello_was_printed() 
{ 
    print_hello(); 
    ASSERT("hello" == my_output.str()); 
} 

Hoặc một cái gì đó tương tự như hiệu ứng đó.

+0

Đây cũng là một ý tưởng hay và tôi không cân nhắc. Quyền std :: cout là không cần thiết [lỗi của tôi để bao gồm nó]. – David

3

Tôi muốn thay đổi do_int_to_string_conversion để nó chỉ thực hiện một việc (chuyển đổi thành chuỗi).

void do_int_to_string_conversion(int i, std::string& s) { 
    switch(i) { ... } 
} 

Điều này không có tác dụng phụ, vì vậy bạn có thể viết một bài kiểm tra đơn vị đơn giản xác minh (các) đầu ra.

Nếu tôi cần một hàm in kết quả chuyển đổi, tôi sẽ đặt nó trong một hàm riêng biệt và tôi sẽ tham số hóa luồng đầu ra.

void output_int(int i, ostream &stream) { 
    std::string s; 
    do_int_to_string_conversion(i, s); 
    stream << s; 
} 

Để kiểm tra đơn vị đó, tôi sẽ chuyển một đối tượng std :: stringstream và kiểm tra kết quả.

+0

@Adrian: +1 - bạn đã đánh bại nó. –

11

Thay vì lo lắng về cách kiểm tra chức năng như nó đứng, tôi sẽ thiết kế lại chức năng để làm việc một cách hợp lý hơn một chút, và kiểm tra phiên bản được thiết kế lại để thay thế.

Ngay bây giờ, hàm dường như có ba trách nhiệm riêng biệt (và chỉ một chút liên quan): thực hiện chuyển đổi, sửa đổi chuỗi được cung cấp bên ngoài và ghi một số dữ liệu vào luồng. Luồng mà nó viết (std::cout) cũng được mã hóa cứng - một sự cố đang chờ xảy ra (ví dụ: chuyển đổi sang môi trường GUI có thể không quan trọng).

Tôi bắt đầu bằng 1) chia thành các hàm lôgic và 2) cung cấp luồng dưới dạng thông số.

std::string convert_int(int val) { 
    switch (val) { 
     case 1: return "1"; 
     case 2: return "2"; 
     default: return "Nix"; 
    } 
} 

std::ostream &write_string(std::ostream &os, std::string const &s) { 
    return os << s; 
} 

Tôi đã không bao gồm bất cứ điều gì để (đặc biệt) sửa đổi một chuỗi bên ngoài cung cấp - rõ ràng là bạn có thể gán giá trị trả về từ convert_int như bạn thấy phù hợp, và giá trị của chuỗi đó được thông qua năm wasn' t được sử dụng anyway.

Thật vậy, write_string là một ứng cử viên tốt để được loại bỏ hoàn toàn, nhưng vì bạn có loại khả năng cơ bản đó, chúng tôi sẽ giữ lại nó cho thời điểm này. Thử nghiệm những điều này là tương đối đơn giản - đối với convert_int, chúng tôi xem xét chuỗi trả về và so sánh với những gì chúng tôi mong đợi. Đối với write_string, chúng tôi có thể chuyển một số stringstream thay vì thông thường ostream - sau đó chúng tôi có thể sử dụng .str() để nhận kết quả đó dưới dạng chuỗi và (lại) so sánh với những gì chúng tôi mong đợi.

+1

Chuỗi trong trường hợp này là một proxy cho một cái gì đó lớn hơn, và bên ngoài với những thứ mà tôi đang làm việc. Bạn đã hỏi trong phần bình luận của câu hỏi cuối cùng mà tôi hỏi rằng tôi đưa vào một ví dụ điển hình, nhỏ gọn về vấn đề này. Đây là đoạn mã này. – David

+1

@ David: okay, đó không thực sự là một vấn đề lớn. Cho rằng vấn đề, sửa đổi các đối tượng (của bất cứ loại nào) đã được thông qua bởi tham chiếu không nhất thiết phải là một vấn đề. Bạn vẫn muốn thiết kế các chức năng để làm * một * điều mặc dù. Nếu nó sửa đổi một đối tượng, hãy tạo một đối tượng "sạch", sửa nó, sau đó kiểm tra kết quả - mặc dù nếu mục đích chính của nó là sửa đổi một đối tượng, bạn có thể muốn nghĩ xem liệu nó có phải là thành viên của nó không vật. –

+0

Tôi đã đặt câu hỏi này cho một người bạn khác của tôi và anh ấy có phản ứng tương tự với các bạn. Bao gồm cả viết cho std :: cout là xấu trên một phần của tôi bởi vì nó nhầm lẫn mọi thứ. Vẫn đang làm việc về các kỹ năng hỏi-hỏi của tôi. Chuỗi trong trường hợp này là một proxy cho cấu trúc dữ liệu được ánh xạ-bộ nhớ lớn, sống ở giao diện với những gì chúng ta đang làm việc và chồng của fortran cũ. Các int lập chỉ mục danh sách những điều có thể ghi vào vị trí đó. – David

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