2009-02-27 42 views
33

Khi áp dụng thử nghiệm đơn vị cho một số mã C, chúng tôi gặp phải sự cố rằng một số hàm tĩnh không thể được gọi tại tệp thử nghiệm mà không sửa đổi mã nguồn. Có cách nào đơn giản hay hợp lý để khắc phục vấn đề này?Cách kiểm tra hàm tĩnh

+0

Bản sao có thể có của [Làm cách nào để kiểm tra lớp có phương thức riêng, trường hoặc lớp bên trong?] (Https://stackoverflow.com/questions/34571/how-do-i-test-a-class-that -a-riêng-phương pháp-lĩnh vực-hoặc-bên trong-lớp học) – Raedwald

Trả lời

42

Tôi có bộ phận thử nghiệm. Trong những trường hợp nghiêm trọng - như cố gắng thử nghiệm một hàm tĩnh, tôi sử dụng:

#include "code_under_test.c" 
...test framework... 

Tức là, tôi bao gồm toàn bộ tệp có chức năng được thử trong khai thác thử nghiệm. Đây là phương sách cuối cùng - nhưng nó hoạt động.

+0

Tôi nghĩ rằng điều này sẽ làm việc mà không có bất kỳ tác động. –

+0

Tôi không hiểu điều này. Có thể ai đó xin vui lòng giải thích. Cảm ơn! –

+1

@BhupeshPant: Bạn không hiểu gì? Nếu một hàm là 'tĩnh', nó không thể truy cập được bên ngoài đơn vị dịch (khoảng, tệp nguồn), nơi nó được định nghĩa. Giải pháp trong câu trả lời đảm bảo rằng hàm được kiểm tra được đưa vào cùng với mã kiểm tra nó bằng cách sao chép mã nguồn được kiểm tra vào tệp kiểm tra nó thông qua lệnh '#include" code_under_test.c "'. Trình biên dịch nhìn thấy mã được kiểm tra và mã thực hiện kiểm thử tất cả như một đơn vị dịch, vì vậy mã kiểm thử có thể gọi hàm tĩnh, cái gì đó nếu không sẽ là không thể. –

2

Bạn có thể thêm chức năng không tĩnh để gọi hàm tĩnh, sau đó gọi hàm không tĩnh.

static int foo() 
{ 
    return 3; 
} 

#ifdef UNIT_TEST 
int test_foo() 
{ 
    if (foo() == 3) 
    return 0; 

    return 1; 
} 
#endif 
+0

Điều đó thay đổi nguồn, phải không? –

+0

Có, nó sửa đổi nguồn, nhưng không loại bỏ "tĩnh" từ foo. Nếu không, câu trả lời là khá nhiều mà bạn không thể gọi là tĩnh từ bên ngoài. Tôi đã tự do. –

5

Không - bạn không thể trực tiếp kiểm tra hàm tĩnh mà không sửa đổi nguồn ít nhất một chút (đó là định nghĩa tĩnh trong C - không thể gọi hàm từ một hàm khác).

Bạn có thể tạo một hàm riêng biệt trong tệp thử nghiệm mà chỉ cần gọi hàm tĩnh?

Ví dụ:

//Your fn to test 
static int foo(int bar) 
{ 
    int retVal; 
    //do something 
    return retVal; 
} 

//Wrapper fn 
int test_foo(int bar) 
{ 
    return foo(bar); 
} 

Chúng ta thường không kiểm tra chức năng tĩnh của chúng tôi trực tiếp, mà là đảm bảo rằng logic họ thực hiện được kiểm tra đầy đủ bởi các xét nghiệm khác nhau của hàm gọi.

+2

Đó không phải là thử nghiệm đơn vị thực sự nếu bạn không kiểm tra chức năng trực tiếp. –

11

Bạn có thể cung cấp thêm thông tin về lý do bạn không thể gọi hàm này không?

Dịch vụ này không khả dụng vì nó ở chế độ riêng tư đối với tệp .c? Nếu vậy, bạn đặt cược tốt nhất là sử dụng trình biên dịch có điều kiện cho phép truy cập vào hàm để cho phép các đơn vị biên dịch khác truy cập nó. Ví dụ

SomeHeaderSomewher.h

#if UNIT_TEST 
#define unit_static 
#else 
#define unit_static static 
#endif 

foo.h

#if UNIT_TEST 
void some_method 
#endif 

Foo.cpp

unit_static void some_method() ... 
6

Đối với kiểm tra đơn vị, chúng tôi thực sự có mã kiểm tra bên trong file nguồn và chúng tôi có thể biên dịch nó khi kiểm tra. Điều này cho phép các bài kiểm tra đơn vị truy cập đầy đủ vào tất cả các hàm và các biến cấp tệp (tĩnh hoặc theo cách khác).

Bản thân bài kiểm tra đơn vị không tĩnh - điều này cho phép chúng tôi gọi các bài kiểm tra đơn vị từ một chương trình siêu thử nghiệm đơn vị kiểm tra tất cả các đơn vị biên dịch.

Khi chúng tôi gửi mã, chúng tôi có điều kiện thực hiện các kiểm tra đơn vị nhưng điều này không thực sự cần thiết (nếu bạn muốn chắc chắn bạn đang giao hàng chính xác cùng mã bạn đã kiểm tra).

Chúng tôi luôn thấy vô giá khi có các bài kiểm tra đơn vị ở cùng vị trí với mã bạn đang kiểm tra vì nó rõ ràng hơn là bạn cần cập nhật các bài kiểm tra nếu và khi mã thay đổi.

+1

Tôi làm điều đó khi kiểm tra đơn vị đủ nhỏ để vừa. Khi các bài kiểm tra đơn vị trở nên lớn hơn nguồn kiểm tra, tôi phải tách riêng các chương trình thử nghiệm, có thể liên kết với đối tượng (khi chỉ sử dụng phụ thuộc bên ngoài) hoặc sử dụng mẹo trong câu trả lời khi chơi với các hàm tĩnh. –

2

chức năng tĩnh là chức năng trợ giúp cơ bản cho các chức năng công khai (tức là tiếp xúc).Vì vậy, IMO, kiểm tra đơn vị của bạn nên gọi giao diện công cộng với các đầu vào thực hiện tất cả các đường dẫn trong hàm tĩnh.

Đầu ra (giá trị trả về/tác dụng phụ) của chức năng công cộng nên được sử dụng để kiểm tra ảnh hưởng của tĩnh.

Điều này có nghĩa là bạn cần phải có cuống thích hợp để 'bắt' những tác dụng phụ này. (ví dụ: nếu một hàm gọi tệp IO, bạn cần phải cung cấp sơ khai để ghi đè các hàm lib của tệp IO). Cách tốt nhất để làm điều này bằng cách làm cho mỗi bộ kiểm thử một dự án riêng biệt/thực thi và tránh liên kết tới bất kỳ hàm lib bên ngoài nào. Bạn có thể thử ngay cả các hàm C, nhưng nó không đáng để thử.

Dù sao, đây là phương pháp tôi đã sử dụng cho đến nay và nó hoạt động cho tôi. Chúc may mắn

2

Nếu bạn đang ở trong môi trường Unix, bạn có thể bao gồm trong tệp thử nghiệm tiêu đề bổ sung yourheader_static.h với khai báo các hàm tĩnh của bạn và dịch tập tin obj code_under_test.o thành toàn cầu hóa biểu tượng cục bộ. Chúng sẽ hiển thị như thể chúng là các hàm không tĩnh.

0

Có 2 cách để thực hiện việc này.

  1. Bao gồm các tập tin nguồn c vào file nguồn kiểm tra đơn vị, vì vậy phương pháp tĩnh tại là trong phạm vi của đơn vị tập tin nguồn thử nghiệm và có thể được gọi.

  2. Thực hiện lừa:

#define static

vào đầu của tập tin đơn vị nguồn thử nghiệm.

Nó sẽ chuyển đổi từ khóa static thành "không có gì" hoặc tôi có thể nói, nó xóa static khỏi mã nguồn c của bạn.

Trong một số công cụ kiểm tra đơn vị, chúng tôi có thể sử dụng cấu hình tùy chọn "pre-vi xử lý" để làm thủ thuật này, chỉ cần đặt trong cấu hình:

static=

Công cụ này willl chuyển đổi tất cả static từ khóa để "không có gì "

Nhưng tôi phải nói, bằng cách sử dụng những cách này làm cho phương pháp static (hoặc biến) mất ý nghĩa cụ thể của nó.

+1

Đột nhiên hiển thị nhiều hàm và biến được khai báo tĩnh ở phạm vi tệp có thể dẫn đến xung đột không nên xảy ra. Nghiêm trọng hơn, đột nhiên chuyển đổi các biến được khai báo tĩnh ở phạm vi hàm (bên trong một hàm) thành không tĩnh thường hoàn toàn phá hủy các hàm. Do đó, thủ thuật '#define static/* as nothing * /' không đáng tin cậy. –

+0

Vâng, bạn nói đúng. Nó chỉ là một thủ thuật trong trường hợp chúng tôi đã xem xét những gì chúng ta cần kiểm tra, chỉ là nội dung của hàm tĩnh hoặc tất cả những thứ liên quan đến nó. – Nik

+1

Điều thú vị là '#define static/* là không có gì * /' chỉ là cmocka (khung kiểm thử đơn vị được phát triển từ cmockery của Google) đề xuất (đó là những gì đã cho tôi suy nghĩ thứ hai về việc sử dụng nó) – hmijail

1
#define static 

Đây là một ý tưởng rất tồi. Nếu bạn có một biến được khai báo cục bộ cho một hàm, nó sẽ thay đổi hành vi của hàm. Ví dụ:

static int func(int data) 
{ 
    static int count = 0; 

    count += data; 
    return count; 
} 

Bạn có thể gọi hàm từ các thử nghiệm đơn vị, như func() sẽ được xuất khẩu, tuy nhiên chức năng cơ bản của mã sẽ được sửa đổi.

--kurt

1

Nếu bạn đang sử dụng Ceedling và cố gắng sử dụng #include "code_under_test.c" phương pháp, kiểm tra xây dựng sẽ thất bại bởi vì nó sẽ tự động cố gắng xây dựng "code_under_test.c" một lần khi #included và cũng vì đó là mục tiêu của thử nghiệm.

Tôi đã có thể thực hiện việc này bằng cách sửa đổi nhỏ mã code_under_test.c và một vài thay đổi khác. Quấn toàn bộ tập tin code_under_test.c với việc kiểm tra này:

#if defined(BUILD) 
... 
#endif // defined(BUILD) 

Thêm phần này vào khai thác thử nghiệm của bạn:

#define BUILD 
#include "code_under_test.c" 

Thêm BUILD xác định để Makefile hoặc cấu hình dự án của bạn tập tin:

# Makefile example 
.. 
CFLAGS += -DBUILD 
.. 

Tập tin của bạn bây giờ sẽ được xây dựng từ môi trường của bạn và khi được bao gồm trong khai thác thử nghiệm của bạn. Ceedling sẽ không thể xây dựng tệp lần thứ hai (đảm bảo tệp project.yml của bạn KHÔNG định nghĩa BUILD).

1

Tất cả các câu trả lời được đề xuất ở trên (ngoại trừ một số ít) đề xuất việc biên dịch có điều kiện yêu cầu sửa đổi nguồn. Như vậy mà không phải là một vấn đề, nó chỉ sẽ thêm lộn xộn (chỉ để thử nghiệm). Thay vào đó bạn có thể làm một cái gì đó như thế này.

nói chức năng của bạn để được kiểm tra được

static int foo(int); 

Bạn thực hiện một tập tin header gọi testing_headers.h mà sẽ có các nội dung -

static in foo(int); 
int foo_wrapper(int a) { 
    return foo(a); 
} 

Bây giờ khi biên dịch tập tin c của bạn để thử nghiệm bạn có thể lực lượng bao gồm tiêu đề này từ các tùy chọn trình biên dịch.

Đối với tiếng kêu và gcc, cờ là --include. Đối với trình biên dịch Microsoft C, nó là /FI.

Điều này sẽ yêu cầu hoàn toàn 0 thay đổi đối với tệp c của bạn và bạn sẽ có thể viết một trình bao bọc không tĩnh cho hàm của bạn.

Nếu bạn không muốn một trình bao bọc không tĩnh, bạn cũng có thể tạo một con trỏ hàm toàn cục không được khởi tạo thành foo.

Sau đó, bạn có thể gọi hàm bằng cách sử dụng con trỏ hàm toàn cầu này.

0

Chỉ cần để thêm vào câu trả lời chấp nhận bởi Jonathan Leffler, và xây dựng trên đề cập khác của một hàm wrapper:

  1. Tạo một file nguồn kiểm tra, như trong test_wrapper_foo.c, nơi foo.c là bản gốc .
  2. Trong test_wrapper_foo.c:
#include "foo.c" 

// Prototype 
int test_wrapper_foo(); 

// wrapper function 
int test_wrapper_foo() { 
    // static function to test 
    return foo(); 
} 

Giả sử rằng các chức năng foo tĩnh trong foo.c có nguyên mẫu: int foo (void);

  1. build test_wrapper_foo.c qua makefile của bạn thay vì foo.c (lưu ý rằng điều này sẽ không phá vỡ bất kỳ phụ thuộc vào chức năng trong foo.c bởi các chức năng bên ngoài khác)

  2. Trong tập lệnh thử nghiệm đơn vị của bạn, hãy gọi test_wrapper_foo() thay vì foo().

Cách tiếp cận này để nguyên nguồn gốc, trong khi cho phép bạn truy cập chức năng từ khung kiểm tra của mình.

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