Adding a Test¶
In this lesson we will
Write a unit test for the Storage archetype
Overview¶
Testing code is a fundamental best practice of writing any kind of software. While there are technically a few different varieties of tests, this lesson focuses on unit tests.
Add a Test¶
Cyclus uses the GoogleTest testing framework. Using the stubs, Cyclus automatically provides you with some basic archetype unit tests, specifically those that are in
$ Storage_unit_tests
...
[----------] 8 tests from TutorialFac/AgentTests
...
[----------] 8 tests from TutorialFac/AgentTests (114 ms total)
[----------] 3 tests from TutorialFac/FacilityTests
...
[----------] 3 tests from TutorialFac/FacilityTests (44 ms total)
...
The goal of any good unit test is to define an initial state and expected final state and then call a function and confirm that the observed final state matches the expected final state. For the Storage facility, one such state transition is as follows
Initial State: There is material in the
input
bufferFunction Call:
Tock()
Expected Final State: The
input
buffer is empty and the inventory buffer has increase by the same amount
Begin by opening the file src/storage_tests.cc
in your favorite text editor
and find the test for the Tock()
function. It should look something like
TEST_F(StorageTest, Tock) {
EXPECT_NO_THROW(facility->Tock());
// Test Storage specific behaviors of the Tock function here
}
You can set up the initial state by pushing a Material
object to the
input
buffer. An easy way to make a Material
object is to use the
NewBlankMaterial()
API.
Add the following lines before the Tock()
function call in the test
double qty = 42;
facility->input.Push(cyclus::NewBlankMaterial(qty));
You can then test the test’s initial condition
EXPECT_DOUBLE_EQ(facility->input.quantity(), qty);
EXPECT_DOUBLE_EQ(facility->inventory.quantity(), 0);
Then execute the Tock:
EXPECT_NO_THROW(facility->Tock());
Next, add the test for the final state after the call to Tock()
EXPECT_DOUBLE_EQ(facility->input.quantity(), 0);
EXPECT_DOUBLE_EQ(facility->inventory.quantity(), qty);
Now your test should look like
TEST_F(StorageTest, Tock) {
double qty = 42;
facility->input.Push(cyclus::NewBlankMaterial(qty));
EXPECT_DOUBLE_EQ(facility->input.quantity(), qty);
EXPECT_DOUBLE_EQ(facility->inventory.quantity(), 0);
EXPECT_NO_THROW(facility->Tock());
EXPECT_DOUBLE_EQ(facility->input.quantity(), 0);
EXPECT_DOUBLE_EQ(facility->inventory.quantity(), qty);
}
Finally, build and test, same as it ever was
$ ./install.py
$ Storage_unit_tests
Exercise¶
Can you come up with another unit test?
Note
The Tock()
test did not depend on any particular state. We always expect
the input-to-inventory transfer to take place, no matter what. Is that true
for your test?
Further Reading¶
After unit tests, the next step to take is testing your archetype in an actual simulation. You can find more on the Testing and Debugging Archetypes page.
One of the best ways to learn is by example. The Cycamore repository has examples of running regression tests that include the full execution stack – read an input file, run a simulation, and test an output file. There are also examples of integration tests that utilize the new MockSim testing feature in Cyclus.