LabVIEW Craft
  • Blog
  • About
  • Contact
  • Tools
  • Resources

TDD in LabVIEW - a Caraya Approach

7/3/2016

3 Comments

 
Omar Mussa
Picture
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:
  • Each frame has up to 2 rolls to knock down 10 pins
  • Game is 10 frames
  • Each roll is 0-10 pins
  • Spare = 10 + next roll
  • Strike = 10 + next two rolls
  • Bonus rolls - in the 10th frame the player gets to roll until Strike or Spare score is complete (up to three total rolls)
We will initially use the Martin based OOP design - which has a game class with two methods: roll and score.

​First, I c
reate a TDD project (I’m calling mine “Bowling”):
Picture
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.
Picture
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.
Picture
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:
Picture
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”.
Picture
The Pre-Build Action.vi code looks like this:
Picture
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.
Picture
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”.
Picture
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.
Picture
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.
Picture
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:
Picture
Let’s go ahead and implement code to pass the test in the roll method for now.
Picture
Rerun the VI (still in TDD mode) and boom - pass:
Picture
One additional optional step is to make the assertion clearer through the Assert label like this:
Picture
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.
Picture
When we run the VI now and we get a test that passes:
Picture
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.
Picture
This code is effectively testGutterGame (from Martin) but it is integrated into the actual score method.  The result is still passing:
Picture
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:​
Picture
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:
Picture
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:
Picture
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.
Picture
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.
Picture
As expected it fails, now let’s implement some code to handle the strike case:
Picture
The "not strike" case simply inverts the FirstRollFlag:
Picture
The test now passes:
Picture
But the perfect game result is still:
Picture
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:
Picture
Picture
Picture
And the perfect game now passes:
Picture
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).
Picture
Refactoring the score method to fix the spare case:
Picture
One last step we should take - let’s re-run the “Pre-Build Action.vi” to validate all of our tests pass:
Picture
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.
tdd_bowling_via_caraya.zip
File Size: 161 kb
File Type: zip
Download File

3 Comments
Raymond Farmer link
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.
Why didn't you refactor the Roll.vi and remove all the TDD code as you did with the Score.vi?
The 'TDD VI Template.vit' needs some changes, 1) put the 'error in' control back to default size (it just looked messy). 2) In the False case statement, wire the error wire from left to right not from the bottom entry. When I change the 'Assert...' vi and deleted the unused error wire I ended up with a broken wire. This would not have happened if wired from left entry point.
There must have been a step missing in you report because when I run testOneSpare.vi for the first time it passed and I didn't have to change the Score.vi code.
I downloaded tdd_bowling_via_caraya.zip and run the Pre-Build Action.vi, it had a broken arrow because it couldn't find TDD -Get All Test Paths.vi. Yet my version had no problems.
The problem was a pathname problem - 'Program Files' not 'Program Files (x86)'.
Anyway I found it a usefully exercise as it showed the differences between the different Unit Test tools. I have as yet tried using Caraya without TDD. But using your Template did make it simpler and easier to use.

Reply
Omar Mussa
8/30/2016 07:39:33 pm

Great feedback Raymond:
1 - You are right - I didn't notice that it was non-default size - will fix in GitHub (https://github.com/omarmussa/TDD-Caraya) first - feel free to do a pull request if you'd like to contribute ...

2 - Also a good idea - I will change that as well. It makes more sense that the bottom input is the test vector and that the error should flow left to right.

3 - The testOneSpare.vi that ships is the final version with (score.vi fully implemented) so we expect it to pass.

4 - I just reopened the zip on a different machine and got the same missing path for the "TDD - Get All Test Paths" - I'll fix the example and re-upload - good catch!

The easiest way to skip Caraya tests from the TDD Pre-Build template would be to filter the filepaths for tests you do not want to run. I would personally use the OpenG "Filter 1D Array" and pass in an array of paths to skip if I wanted to do this.

Thanks for the great feedback - sorry for the slow response.

Reply
Cooking Katie link
11/26/2020 06:36:28 am

Good readiing this post

Reply



Leave a Reply.

    RSS Feed

    Tags

    All
    Abstract Messaging
    Actor Framework
    Agile
    AI
    Asynchronous Programming
    Best Practices
    C#
    Complexity
    Continuations
    Control Values By Index
    Craftsmanship
    Dependency Inversion
    Dependency Viewer
    Futures/Promises
    Genetic Algorithm
    Liskov Substitution
    Malleable VIs
    Mediator Pattern
    .NET
    Object Oriented
    Object-Oriented
    Packed Project Library
    Parallelism
    Polymorphism
    Pub/Sub
    RawCap
    Root Loop
    Scrum
    Task Parallel Library
    TCP/IP
    TDD
    Test Engineering
    UML
    Unit Test
    VI Scripting
    VI Server
    WireShark

    Archives

    April 2019
    July 2018
    October 2017
    March 2017
    February 2017
    January 2017
    December 2016
    July 2016
    June 2016
    May 2016
    April 2016
    March 2016
    February 2016
    January 2016
    December 2015
    November 2015
    October 2015
    August 2015
    February 2015
    January 2015

    LabVIEW Blogs

    Eyes on VIs
    LabVIEW Hacker
    VI Shots
    LabVIEW Artisan
    Software Engineering for LabVIEW
    ​Wiresmith Techology
Picture
Powered by the Panda Ant

  • Blog
  • About
  • Contact
  • Tools
  • Resources