Viewpoint Systems, Inc.
800 West Metro Park
Rochester, NY 14623
Phone: 585.475.9555
Fax: 585.475.9645
Viewpoint Data Management, LLC.
800 West Metro Park
Rochester, NY 14623
Phone: 585.475.9555
Fax: 585.475.9645
Viewpoint News, January 2009
In case you missed it see Part 1 - Motivations
In this second article in our series we explore a framework for doing TDD with LabVIEW.
My goals in a unit test framework are that I want to :
Luckily for us a framework already exists to meet all of our goals: NI TestStand. TestStand out of the box can accomplish all of our needs, and the best part is we do not need to be TestStand experts to leverage TestStand to do TDD in LabVIEW.
In this article we are going to explore writing tests using TestStand 4.1 as our testing framework and LabVIEW 8.6. Using TestStand for our framework allows us to interface with earlier versions of LabVIEW as TestStand does not care what version of LabVIEW the VIs were written in.
In TestStand we will be using the standard "Sequential Model" to write our tests against. If you do not know what the "Sequential Model" is do not worry about it, it is the default sequence model TestStand uses when opened for the first time. The model is not perfect for doing TDD, but by using the default model it will allow us to get up and running quickly. We will be writing our first test steps within two minutes of opening TestStand.
For this article we will be architecting a clean sheet solution using the following problem:
Design a VI that will tell us if a right triangle is formed from three sides of various integer lengths.
Thinking back to our days of high school geometry we can use the Pythagorean theorem to solve this question, a^2 + b^2 = c^2. Now that we have a general idea of the algorithm we need let us start applying TDD to the problem.
What should our first test be? There are several that come to mind. We could supply the lengths of the sides of a triangle that we know works, like 3, 4, 5. We could ensure that our function reports false if any of the sides are 0, or if any of the sides are negative. What about the order of the sides? The question does not state the order of the integers to be supplied, thus we need to also ensure that our function works for the input 3,4,5 as well as inputs 4,5,3 and 5,4,3.
For our first test we are going to ensure all values are greater than zero. In this iteration, we are going to write just enough code to ensure the test case passes, no more no less.
Start TestStand. We are greeted with Figure 1, a blank sequence file.
Figure 1 - Empty Sequence File in TestStand
We are going to start by adding our first test by dragging a "Pass/Fail Test" from the "Insertion Palette" on the left hand side of the screen into the "Main Sequence" as shown in Figure 2.
Figure 2 - Adding a Pass/Fail Test to our Main Sequence
At the bottom of the page we are going to set the name of the test and add a comment to describe what we are testing. Name this step "Provide negative values for input", and add a comment "Test to ensure if negative values are provided as input the result of the function is false." Figure 3 shows TestStand with these values filled in.
Figure 3 - Providing a Name and Comment to our Test
Now our test code. We are going to let TestStand generate the Pass/Fail VI, this will allow TestStand to manage the input and output parameters to the test VI without us having to intervene. Down in the "Step settings" at the bottom of the page, click on the tab "Module". On this screen we are going to click the button "Create VI". TestStand is going to ask us where we want to save our new VIs. Figure 4 shows a typical directory structure I have adopted.
Figure 4 - Typical Project Layout for TDD from TestStand
Save the new VI in the "Test VIs" folder and name it "Negative Values.vi". TestStand creates a simple VI front panel shown in Figure 5 and block diagram in Figure 6.
Figure 5 - Front panel of Test VI
Figure 6 - Block Diagram of Test VI
Now we are going to create the VI that will perform the actual work of deciding if given three integers they form a right triangle. I am going to choose "File/New VI" and save my VI named "Right Triangle Checker" in the "Source VI" directory. I am going to lay down an error in and error out on my front panel, along with three numeric controls, calling them creatively enough "A", "B", and "C". I am going to have one output Boolean indicator and I am going to call "Is Right Triangle?". I am going to change the representation on "A", "B", and "C" from doubles to longs and I am going to connect all of my controls up to the connector pane. The resulting front panel is shown in Figure 7.
Figure 7 - Front Panel of Right Triangle Checker VI
Add just enough code to our VI under test to ensure it passes. Figure 8 is the code we are going to initially use to test and ensure all inputs are not negative.
Figure 8 - Writing Just Enough Code For Our VI to do Something Useful
Now we have just enough code to make our Test VI run. I am going to drop an instance of my "Right Triangle Checker.vi" into my test VI "Negative Values.vi" and connect up all of the inputs. In our test VI we need to design the logic needed to validate the test conditions. In this case we want the test to pass if our function returns false when negative numbers are supplied to our VI. For our assertion we are going to invert the output from our "Right Triangle Checker" and connect it to the "PASS/FAIL" flag of our test VI. Lastly we are going to provide report text that will be useful in the event of a test failure. Figure 9 shows the final layout of our test VI.
Figure 9 - Block Diagram of our Test VI Test for Negative Input
Back to TestStand. We have one test we want to run, we are going to hit the large "Play" button at the top of the screen. This will run through all the tests in our sequence. Since we have not modified the "Sequential Model" we are going to be asked for a serial number for the UUT. We are going to bypass this step and hit "Ok". Once our test VI has executed we will be greeted by a popup from TestStand shown in Figure 10.
Figure 10 All Test Sequences Passed
Behind the popup is Figure 11, a listing of all of our test steps and their pass/fail results.
Figure 11 - Pass/Fail Results for our Test Steps
Clicking the "Ok" button on the all test passed dialog will again bring us back around to entering a serial number for the UUT. Clicking stop will print up the report seen in Figure 12.
Figure 12 - Test Report Produced after Running all Tests
In the summary report we can see each test that ran, the results of which ones passed and which ones failed.
We have just created our first test VI and have done our first iteration of TDD. Now that we are on a roll let us add another test. This time test for a valid right triangle using the inputs 3, 4, 5.
Following what we did before to add a test to the system:
Figure 13 shows the block diagram with our newly implemented Pythagorean Theorem algorithm.
Figure 13 - Code to Implement Pythagorean Theorem for the inputs 3, 4, 5
The TestStand results screen shows all of our tests passed. Now we are on a roll!
For our next test lets ensure a triangle with lengths 5,4,3 also reports a valid right triangle. Following the steps above, add a test step to feed 5,4,3 to the "Right Triangle Checker.vi" and, without modifying the "Right Triangle Checker.vi" code, we press the "play" button in TestStand. Since we have not modified our VI we would expect this new test to fail which we can see in Figure 14 and Figure 15.
Figure 14 - Failed Test Sequence
Figure 15 - Tests that Passed and Failed while Testing our VI
From the report screen in Figure 16 we can see which test failed and any report text we added to further explain the failure.
Figure 16 - Report After a Failed Test
We now have a test to test for our new functionality that fails. This is great news! We can now modify our VI under test and return to TestStand to check to see we are passing our new test, and ensure we are still passing our old tests. If we follow the mantra of modify VI, test VI, verify results every time we change our VI we can isolate changes we just made to the VI to any failing tests. Having this framework in place allows us to confidently makes changes, knowing that we could quickly go back and re-run all of our prior test quickly to verify our VI is still functioning as we would expect.
There are several tests we have not implemented, try implementing: