To run unit tests with Check, we must create some test cases,
aggregate them into a suite, and run them with a suite runner. That's
a bit of overhead, but it is mostly one-off. Here's a diff for the
new version of check_money.c. Note that we include stdlib.h to
get the definitions of EXIT_SUCCESS and EXIT_FAILURE.
--- tests/check_money.2.c 2006-11-21 18:06:36.760265712 -0500
+++ tests/check_money.3.c 2006-11-21 18:06:36.825255832 -0500
@@ -1,3 +1,4 @@
+#include <stdlib.h>
#include <check.h>
#include "../src/money.h"
@@ -13,8 +14,27 @@
}
END_TEST
+Suite *
+money_suite (void)
+{
+ Suite *s = suite_create ("Money");
+
+ /* Core test case */
+ TCase *tc_core = tcase_create ("Core");
+ tcase_add_test (tc_core, test_money_create);
+ suite_add_tcase (s, tc_core);
+
+ return s;
+}
+
int
main (void)
{
- return 0;
+ int number_failed;
+ Suite *s = money_suite ();
+ SRunner *sr = srunner_create (s);
+ srunner_run_all (sr, CK_NORMAL);
+ number_failed = srunner_ntests_failed (sr);
+ srunner_free (sr);
+ return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
Most of the money_suite() code should be self-explanatory. We are
creating a suite, creating a test case, adding the test case to the
suite, and adding the unit test we created above to the test case.
Why separate this off into a separate function, rather than inline it
in main()? Because any new tests will get added in
money_suite(), but nothing will need to change in main()
for the rest of this example, so main will stay relatively clean and
simple.
Unit tests are internally defined as static functions. This means that the code to add unit tests to test cases must be in the same compilation unit as the unit tests themselves. This provides another reason to put the creation of the test suite in a separate function: you may later want to keep one source file per suite; defining a uniquely named suite creation function allows you later to define a header file giving prototypes for all the suite creation functions, and encapsulate the details of where and how unit tests are defined behind those functions. See the test program defined for Check itself for an example of this strategy.
The code in main() bears some explanation. We are creating a
suite runner object of type SRunner from the Suite we
created in money_suite(). We then run the suite, using the
CK_NORMAL flag to specify that we should print a summary of the
run, and list any failures that may have occurred. We capture the
number of failures that occurred during the run, and use that to
decide how to return. The check target created by Automake
uses the return value to decide whether the tests passed or failed.
Now that the tests are actually being run by check_money, we
encounter linker errors again we try out make check. Try it
for yourself and see. The reason is that the money.c
implementation of the money.h interface hasn't been created
yet. Let's go with the fastest solution possible and implement stubs
for each of the functions in money.c:
--- src/money.1.c 2006-11-21 18:06:37.357174968 -0500
+++ src/money.3.c 2006-11-21 18:06:37.457159768 -0500
@@ -0,0 +1,26 @@
+#include <stdlib.h>
+#include "money.h"
+
+Money *
+money_create (int amount, char *currency)
+{
+ return NULL;
+}
+
+int
+money_amount (Money * m)
+{
+ return 0;
+}
+
+char *
+money_currency (Money * m)
+{
+ return NULL;
+}
+
+void
+money_free (Money * m)
+{
+ return;
+}
Note that we #include <stdlib.h> to get the definition of
NULL. Now, the code compiles and links when we run make
check, but our unit test fails. Still, this is progress, and we can
focus on making the test pass.