1. Matt Oswald
  2. xUnit++

Commits

Matt Oswald  committed c0ba169

adding Tests, Suites, and Attributes documentation

  • Participants
  • Parent commits 7807591
  • Branches default

Comments (0)

Files changed (3)

File Home.wiki

View file
  • Ignore whitespace
 
 ==== Documentation ====
 [[InstallAndSetup.wiki|//Installation and Setup//]]
+[[Tests.wiki|//Tests//]]
+[[SuitesAndAttributes.wiki|//Suites and Attributes//]]
+[[Assertions.wiki|//The Assert methods//]]
+[[RunningTests.wiki|//Running tests//]]
 [[Compare.wiki|//How does xUnit++ compare to ...?//]]
 
 == About xUnit++ ==

File SuitesAndAttributes.wiki

View file
  • Ignore whitespace
+== Suites ==
+
+Suites are a broad method of grouping your tests. All tests within a {{{SUITE}}} block are contained within the same C++ namespace, and can be selected based on the name of the suite using the standard test runner.
+
+{{{
+#!c++
+SUITE(SomeSuite)
+{
+
+FACT(SomeFact)
+{
+}
+
+}
+}}}
+
+== Attributes ==
+
+Attributes are a much more fine-grained method of grouping tests. The packaged test runner can optionally select which tests to run based on key-value pairs. Every test (whether within a suite or not) can be decorated with up to eight key-value pair attributes:
+
+{{{
+#!c++
+ATTRIBUTES(TestName, ("Category", "Server"))
+FACT(TestName)
+{
+    // do some work that apparently fits in a "Server" category
+}
+}}}
+
+There is one special attribute key, //"Skip"// (capitalization required). Any tests with "Skip" as an attribute key will be ignored at run time and the attribute value will be printed as the reason for skipping the test.
+
+{{{
+#!c++
+ATTRIBUTES(TestName, ("Skip", "Takes way too long to complete. Need to refactor"))
+TIMED_FACT(TestName, 0)
+{
+    // do some long-running work that makes the test process take too long:
+    std::this_thread::sleep_for(std::chrono::seconds(10));
+}
+}}}

File Tests.wiki

View file
  • Ignore whitespace
+== Tests ==
+
+There are two types of tests: Facts and Theories. Facts are one-shot tests that assert (hopefully) one thing. Theories are tests that either can not be completely verified with a single assertion, or need to have multiple inputs verified separately.
+
+==== Facts ====
+
+{{{
+#!c++
+FACT(WidgetDefaultConstructorDestructorNeverThrows)
+{
+    Assert.DoesNotThrow([]() { Widget w; });
+}
+}}}
+
+A Fact is a statement about your code under test: //"I assert the default Widget constructor, and the destructor, will never throw exceptions."//
+
+{{{FACT}}} is a macro that takes care of registering your test with the internal test collection. The only parameter is the test name.
+
+There are other variants, {{{TIMED_FACT}}}, {{{FACT_FIXTURE}}}, and {{{TIMED_FACT_FIXTURE}}}.
+
+{{{FACT_FIXTURE}}} and {{{TIMED_FACT_FIXTURE}}} take a struct or class //fixture// type as their second argument. Your test will publicly derive from that class, and a new instance will be created for each test. Use the fixture's constructor and destructor as setup and teardown functions.
+
+{{{
+#!c++
+struct MyFixture
+{
+    MyFixture() { /* setup */ }
+    ~MyFixture() { /* teardown */ }
+
+    int someData;
+};
+
+FACT_FIXTURE(MyTest, MyFixture)
+{
+    int temp = someData;
+}
+}}}
+
+{{{TIMED_FACT}}} and {{{TIMED_FACT_FIXTURE}}} both take an extra parameter for test time (in milliseconds). If the test does not complete within the allotted time period, it will be aborted and the test will fail. Passing {{{-1}}} as the time limit means use the default, and is exactly equivalent to {{{FACT}}} or {{{FACT_FIXTURE}}}. Passing {{{0}}} means "no time limit", which will override the default time limit and let a known long-running test complete.
+
+{{{
+#!c++
+TIMED_FACT(ShortTest, 5)
+{
+    // test code that must complete within 5 milliseconds
+}
+}}}
+
+==== Theories ====
+
+{{{
+#!c++
+THEORY(WidgetDestructorNeverThrows, (Widget *w),
+    std::make_tuple(new Widget((int)0)),
+    std::make_tuple(new Widget(new Foo())),
+    std::make_tuple(new Widget(nullptr))
+)
+{
+    Assert.DoesNotThrow([&]() { delete w; });
+}
+}}}
+
+A Theory is rather more abstract. In this example, we want to assert that a fully-constructed, valid Widget will not throw exceptions when destroyed. Widget, however, has multiple methods for constructions, and the destructors for objects created through each of these need to be tested. To do this, the {{{THEORY}}} macro takes two named parameters: the test name and the parameters to be passed to the test (combined they form the function signature), and then a collection of several {{{std::tuple}}}s that make up the test data.
+
+There are several forms of {{{THEORY}}} as well.
+
+* {{{DATA_THEORY}}} accepts a data provider function object that returns {{{std::vector<std::tuple>>}}} instead a list of inline data. This lets you define more complex methods of providing the test data. For example, your test data could come from an external database. You can also pass in lambdas as the test parameter, and with that we could re-write the two above tests thusly:
+
+{{{
+#!c++
+std::vector<std::tuple<std::function<void()>>> DataProvider()
+{
+    std::vector<std::tuple<std::function<void()>>> data;
+
+    data.emplace_back(std::make_tuple([]() { Widget w; });
+    data.emplace_back(std::make_tuple([]() { Widget w((int)0); });
+    data.emplace_back(std::make_tuple([]() { Widget w(new Foo()); });
+    data.emplace_back(std::make_tuple([]() { Widget w(nullptr); });
+
+    return data;
+}
+
+DATA_THEORY(WidgetConstructorsAndDestructorNeverThrows, (std::function<void()> makeAndDestroyWidget), DataProvider)
+{
+    Assert.DoesNotThrow(makeAndDestroyWidget);
+}
+}}}
+
+* {{{TIMED_THEORY}}} and {{{TIMED_DATA_THEORY}}} both take a timeout value in milliseconds as their last named parameters. This functions identically as it does for {{{TIMED_FACT}}}: {{{0}}} indicates there is no timeout, and {{{-1}}} means use the default.