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

Do You Unit Test?

5/26/2016

3 Comments

 
Jon McBee
A study performed in 2008 by the NCBI found that standard advice encouraging flossing for the prevention of cavities and gum disease is not supported by scientific evidence. This conclusion wasn't drawn because scientists discovered that flossing was not a worth while preventative measure, but rather because scientists found that people were doing it incorrectly. Additionally, a study done in 2013 by the ADA reported that 50% of Americans do not floss daily in the first place. When done correctly flossing can help prevent cavities and gum disease; yet 50% of us choose not to do it at all and the majority of the rest end up doing it incorrectly.
Picture
​If dental experts agree that proper flossing can reduce dental defects, and if proper flossing takes less time than looking at Facebook, then why don't people do it? I can think of many possible reasons:  
  1. People are flossing with too much ferocity, leaving their mouths a bloody mess (Using the tool incorrectly)
  2. People don't like wrapping the floss around their fingers; cutting circulation and potentially severing fingertips in extreme situations (Improper tooling)
  3. People have an established habit of brushing for 46 seconds and moving on with their routine (The tool doesn't fit into the workflow)
Picture
Luckily for me, my mother-in-law is my dental hygienist. So regardless of my affection for flossing, I do it out of the immeasurable anxiety associated with having my mother-in-law evaluating my dental hygiene twice a year. We don't all have this luxury. 
Picture
I think a nice parallel can be drawn between flossing as a best practice for maintaining dental health and software unit testing as a best practice for maintaining software projects. In fact, I believe that the reasons people don't floss may help explain why people don't unit test their software: 

Doing it Incorrectly

  1. People don't know how to do it correctly (and doing it incorrectly was painful) 
  2. People aren't using the right tools for the job (wrapping floss around their fingers when they could use a simple flossing stick)
  3. People don't believe that they have enough time to do it (it doesn't fit their workflow) 
We can classify how we test software into three categories, Unit Testing, Integration Testing, and System Testing. Compiling code and running it to see how it behaves as a whole is system testing. Writing test code that examines interactions between system resources like a database or DAQ card is integration testing.  ​Writing a piece of code to test the most granular units of your application is unit testing. 
Picture
It is easy to think of a unit as a VI or a method, but it is better to think of a unit as a single unit of work. A unit of work could be a public member of an API that happens to have several private methods that it calls internally. The unit test would be designed to test the functionality that the public method promises without knowing that any of the private methods exist. This means that if you have 2000 VIs in your project you don't need to unit test each and every one. You only need to unit test VIs that are publicly scoped.
Picture
Private methods in classes are implementation details that are consumed by public methods. Testing public methods that call private methods allows us to indirectly test the private method's functionality. There are two benefits here to this approach to testing: 

Doesn't Fit the Workflow

  1. Unit tests are less brittle because we can refactor implementation details without breaking tests on the public API
  2. Testing private methods becomes an immediate code smell; it may be time to apply the Single Responsibility Principle and refactor the private method into its own class
There are a few different ways to bring unit testing into your workflow. The least intrusive approach is to start writing unit tests as you refactor an existing code base. As you refactor code you can identify good units for test and start to build up a test suite. Over time you will have implemented tests for the areas of your codebase that are most likely to change, which is exactly what you need.
Picture
A more tightly integrated way to add unit testing to your workflow is by adopting Test Driven Development (TDD). In TDD the test is written first and the expectation is that it will initially fail. The developer then writes the minimum amount of code necessary to get the test to pass. TDD makes testing a first class citizen of the design and development process. 
  1. Red: Create a test and make it fail.
    1. Imagine how the new code should be consumed and write the test as if the code already existed.
    2. Create the production code stub; writing just enough code to allow the software to run.
    3. Run the test, expecting test failure. This ensures that your test is calling the correct code and that the code is not working by accident. This is a meaningful failure and you expect it to fail.
  2. Green: Make the test pass by any means necessary.
    1. Fill in the production code stub with real logic to make the test pass. Keep it simple. Some advocate hard-coding the expected return value to first verify that the test correctly detects success. This varies from practitioner to practitioner. 
    2. If you've written the code so that the test passes as intended, you are finished. You do not have to write more code based on speculation. The test is the objective definition of "done." The phrase "You Ain't Gonna Need It" (YAGNI) is often used to veto unnecessary work. If additional functionality is needed then another test is needed. Make this one test pass and move on
    3. After the test passes you may want to run all other tests written up to this point to build confidence that everything in your application is still working.
  3. Refactor: Change the code to remove duplication in your project and to improve the design while ensuring that all tests still pass.
    1. Remove duplication caused by the addition of the new functionality.
    2. Make design changes to improve the overall solution.
    3. After each refactoring, rerun all the tests to ensure that they all still pass.
  4. Repeat: Each cycle should be very short, and a typical hour should contain many Red/Green/Refactor cycles.
Notice that with TDD you will end up writing a test case for every piece of code that you create. This may seem to contradict my recommendation of only testing publicly scoped methods and VIs. In TDD you will use test cases to guide the development of public methods. These public methods will grow as they are written to pass their tests, and through refactoring you will identify parts of public methods that should be turned into private methods. In this way TDD coerces the private methods to emerge within your code very organically. ​
  1. Create Test Case A
    1. Create Method A that passes Test Case A
    2. Refactor Method A
  2. Create Test Case B
    1. Create Method B that passes Test Case B
    2. Refactor Method B
      1. Notice code duplication between Method A and Method B
      2. Turn duplicate Code into Method C
      3. Mark Method C as Private
If both methods A and B have duplicate code that was pulled out and into Method C, then Method C will be a private method used by both A and B. In this situation a unique test case does not exist for method C. However, by testing A and B method C is effectively covered. As long as the all tests of the public interface for our code pass we can be confident that any private methods can be considered an internal implementation detail.

Improper Tooling

If your current software test strategy involves writing a wrapper around a piece of code and saving it to a folder named "Sandbox" then you have improper tooling. At the very least your tool should automate the running of your tests and generate a pass/fail report for you. 
There are three main unit test tools in the LabVIEW ecosystem.  The Unit Test Framework Toolkit from National Instruments, the VI Tester from JKI, and Caraya from JKI.  You might also choose to build your own test infrastructure. Whatever approach you do decide to take, a solid test framework will make adopting the process easier. 

Conclusion

​If you are using your own test strategy I would love to know what prompted you to create it! Comment below as to why you favor your approach over the JKI and NI tools. 
So is software testing like flossing? I looked for statistics on the number of software developers who do some form of unit testing and couldn't find anything. I put a poll up on my blog and 55% of the people who took part said that they would like to unit test but don't currently do so. If you fall in this 55% I want to know what is stopping you. Just like flossing it will be hard to start. You may even be spitting blood for awhile! However, you will be saving yourself all sorts of pain down the road.
Picture
There is plenty more to be said about this topic; check out the list of resources I have included below for further reading. I am also planning on following this post with a tag team discussion of TDD with Russell Blake at G Systems. I would love to get your thoughts on unit testing, unit test tools in the LabVIEW ecosystem, TDD, and your experience with all of the above. If you have anything to add please do so in the comments section below or email me at the link at the top of this page. 

Resources

​I can't wait to hear your thoughts!
  1. Unit Test vs. Class Test
  2. The Domain Discontinuity
  3. How TDD can make your team happier
  4. The Cycles of TDD
  5. Professionalism and Test-Driven Development
  6. Introduction to Unit Testing (Don’t Worry, Your Secret is Safe with Me)
  7. Testing private methods, TDD and Test-Driven Refactoring
  8. Guidelines for Test-Driven Development
  9. LabVIEW Unit Test Framework Toolkit
  10. JKI VI Tester
  11. JKI Caraya

Jon McBee is a Principal Software Engineer at Cambridge NanoTech and is a Certified LabVIEW Architect, Certified LabVIEW Embedded Developer, Certified TestStand Developer, an NI Certified Professional Instructor, a LabVIEW Champion, and a Certified ScrumMaster
3 Comments
Fabiola De la Cueva link
5/26/2016 11:48:29 am

Great article Jon.
You had me fooled for a second, I thought I was going to learn how to finally floss correctly ;)

I specially agree that writing a unit test before writing the code helps define when we are done with a feature. I find that when working with junior developers, it is better to give them a description of the task and a unit test for them to know when they are done.

I invite people who are interested in Unit Testing in LabVIEW to join the Unit Testing community (I don't think I can paste a link, so go to NI website, look for the Unit Testing Group or perhaps Jon can add a link to the blog post itself).

At the Unit Testing group, you will find videos on how to use some of the tools mentioned in this article, information about other tools that have emerged over time and discussions on how/why to unit tests.

Regards,
Fab

Reply
James McNally link
5/26/2016 12:10:02 pm

Great introduction, I like the analogy.

I use a TDD development flow and love it. It is great having good (not complete!) test coverage to give you confidence in the solution and I don't believe I would ever get around to writing tests if I tried to do it afterwards.

Reply
Sam
4/9/2019 03:05:36 pm

"I don't believe I would ever get around to writing tests if I tried to do it afterwards."

Not only that, but writing the tests ahead of time also helps you to write code that actually IS testable.

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