Rolling the turkey takes some skill in bowling - since this article is the third in the TDD series, I feel a tad bit of pressure to execute! In this article I’m going to create a parallel solution to the bowling game using the Caraya test framework by JKI that will converge with the final solution in the previous articles based on the Kata solution written by Robert C. Martin. Here are the tools I’m planning on using to make this happen (installed via VIPM): JKI Caraya package JKI Caraya TDD Template package - this is a TDD project template I came up with while working with Russell Blake and a few others at the 2016 CLA Summit. Note - please ensure that you install v1.0.0.7 or greater of the package since there is a major improvement to the TDD template that supports this blog post. The source code for this project is here if you’re interested in contributing or customizing for yourself. Why use the TDD-Caraya project template and not just use Caraya? I created the TDD-Caraya to specifically make it faster and easier to create unit tests and run them without requiring a parallel workflow to developing LabVIEW code. This article will highlight how the Kata is easier to complete using the TDD template. Ok back to design. Reviewing the rules of bowling as I see them from a software designer perspective:
First, I create a TDD project (I’m calling mine “Bowling”): Next I create the Game class. I'm going to copy the “TDD VI Template.vit” method into the class and refactor it a bit so that I can use it for creating class methods. Now let’s create the two methods from the TDD template (right click on the .vit in the project and select “Create from Template”): roll.vi and score.vi (best practice create icons using text for these VIs manually right away) Note that both of these VIs already contain our initial TDD tests and they already fail - this is a TDD-Caraya feature. We can run them by setting the TDD flag (run set TDD with TRUE). Run the VI “Set TDD Flag.vi” (note that by default “TDD Mode” is set to TRUE). Notice that the test code in the template is now enabled. Now if we run any of these VIs, it will execute a Caraya test that fails! Run any of the methods you just created and you get a fail like this: What’s cool about this? We didn’t have to create any harness or anything special - effort was minimal and we have failing tests so we are officially doing TDD in a LabVIEW way. The TDD Template also enables one other development feature for running all tests. We can run the tests from the “Pre-Build Action.vi”. The Pre-Build Action.vi code looks like this: Basically, this TDD-Caraya template VI automagically finds Caraya Tests that are in your project and runs them and generates a report where the results are stored. For typical applications, you shouldn’t have to change this VI. In addition, it will automatically run when you try to build an executable and break the build if any test fails. However, we can tweak the template temporarily if desired to run the tests with the GUI by setting the interactive flag to true. What’s nice about this tweak is that we can quickly check if any test fails as we develop by running the “Pre-Build Action.vi”. Currently all of our tests fail, which is what we want. So what next? Let's start with a simple question about our API. Can the roll fail? It knocks down pins and that’s it right? Let’s create an array of rolls in the object. So a roll means: append pins to the array of rolls. Test first means: check that after roll is complete, size of pins increases by 1. In our case, we can easily implement this test code in the roll method In Place Element structure. Now when we run the Roll method in TDD mode (Run“Set TDD Flag.vi” first if needed - for example if you ran the “Pre-Build Action.vi” which ends by setting the TDD Flag to False) our test fails: Let’s go ahead and implement code to pass the test in the roll method for now. Rerun the VI (still in TDD mode) and boom - pass: One additional optional step is to make the assertion clearer through the Assert label like this: Because this step is optional and the project is small, I won’t be using it in the Kata but it can be helpful in bigger projects or when the method is not a good description of the function/test. So we’re effectively done with the roll method. Time to implement the “score.vi”. From a first principle approach, we know that we are going to have to add the pins knocked down to calculate the score and we will also have to add bonuses from strikes and spares. Let's implement this initially as a summation and then work through the other scoring cases for bonuses. When we run the VI now and we get a test that passes: Is this what we want? We currently have an issue with ‘rolls’ - we actually don’t know what the value of ‘rolls’ is when we run the test, so we can’t really call this test case good even though it passes. Let’s first force some rolls of value 0 and then run the test code. Note this is very similar to the TDD Template “Example Add Numbers.vi”. Let’s update the code to directly test 20 rolls of value 0. This code is effectively testGutterGame (from Martin) but it is integrated into the actual score method. The result is still passing: This code works better than the previous step because at least the rolls array value is not empty. Now let's add a test for all ones as well... well now we’re hitting a design constraint of the TDD-Caraya Template. We can’t test multiple test vectors from the TDD template directly. This isn’t really a problem - we just need to refactor our test "inside out" to be a more traditional unit test separate from the code under test. We can also decide whether we want to leave the TDD template code in the method itself (as a default test case) or not. As part of this refactor, we replaced the hard-coding of the private ‘rolls’ data with calling the “Roll” method. We now have a testGutterGame.vi that looks like this: Notice that we no longer need the conditional disable structures around the inputs and the test assertions, so they have been removed. We also removed the check to see if the code is running as a subVI (no check for Exec.State = “Run top level”). We don’t need this anymore because the test will never run as a subVI. Also, it should be pretty easy to reuse this VI for testing other cases with minor modifications. Did we lose anything by starting with the TDD template in this example? We basically gained the ability to do some initial testing in place and then refactored that test back to what the traditional Caraya test would look like. I'd argue that this forced us through a TDD workflow and resulted in a better developer experience then just starting with the API and coding tests separately. For this project, we’re now going to remove the TDD template code from the score method, so Game.score.vi diagram now looks like this: Let’s decide to add all of our tests to the project in a virtual folder called “Tests” - this will help us find and run tests (manually and programmatically). Ok now let’s get rolling. Let’s create testAllOnes.vi We can quickly create this from the testGutterGame.vi. The code looks like this: It passes because our gutter game and all ones game both are implemented at this point. Now let’s create a perfect game test. Note - in the original Kata, the author chose to go from testAllOnes to testOneSpare - personally I think the more logical next step is to testAllTens aka testPerfectGame - because this leads us in a direction that will point us to our first failure quickest so I am going to take that route. This test fails which makes sense because we still have no special code for scoring a spare or a strike. Let’s leave this test failing for the moment and focus on scoring a single strike. First, let’s create testOneStrike.vi. We’ll roll 10,3,4 and check that it equals 24. As expected it fails, now let’s implement some code to handle the strike case: The "not strike" case simply inverts the FirstRollFlag: The test now passes: But the perfect game result is still: This failure is because the new score is 330 instead of 300. So we must not be handling the bonus frame right. Let’s fix it by ending the game at the 10th frame. To do this, we’ll add a frame counter and check if we’re in the bonus frame or not. The score.vi now looks like this: And the perfect game now passes: You may notice that there is more wire labelling in this version - that is an outcome of the refactoring and realizing the logic wasn’t trivial so I needed to make the code more readable and maintainable. Now, let’s complete the game and create testOneSpare.vi. We’ll roll 5,5,3 and check the score (expect 16). Refactoring the score method to fix the spare case: One last step we should take - let’s re-run the “Pre-Build Action.vi” to validate all of our tests pass: Yay we’re done! It would be a good idea to reset the “Interactive” flag to “FALSE” on the “Pre-Build Action.vi” at this point. Additional tests (validating that 10th frame spare works, etc) can be easily added later as needed for either greater coverage and/or for adding new features. Try doing this Kata yourself or go through a similar exercise on a small project the next time you have one - ideally you’ll find that your code and your design both get better over time. You can download the source code for the final version of the project attached to this post, I highly recommend that you try to do the Kata yourself. Omar Mussa is the Director of Software Engineering at EUV Tech and is a Certified LabVIEW Architect.
3 Comments
8/18/2016 08:44:21 am
Is there away of not having a test run when the Pre-Build Action.vi is run. Under UTF you select the Skip Test under the Test Properties. In JKI VI Test you have to disable the test case. I still had the Example Add Numbers.vi in my project which was showing up in the test report.
Reply
Omar Mussa
8/30/2016 07:39:33 pm
Great feedback Raymond:
Reply
Leave a Reply. |
Tags
All
Archives
October 2019
LabVIEW Blogs |