Skip to main content

Unit Testing - What to Test

This I wrote to answer a question that came up when we were discussing our software process and I was training developers on how to unit test.

It seems a simple enough question, but I kept pondering it and delving deeper until I realized I needed to write this monograph.

What unit tests should we write? How do we know what to test?


Ideally, unit tests should cover every path through the code. It should be your chance to see every path through your code works as expected and as needed. If you are practicing Test Driven Development then it's implied everything gets a test.

In the real world, you might not be allowed to test everything - for instance, if the testing suite ends up taking a week to run, then the world will have changed by the time it finishes and the test results will be obsolete.

Unit testing at it's basic is testing an object, a method - the smallest unit of your code that it can test independently.

It should test the inputs "goes into" and corresponding outputs "goes out of".  It should test every variant of input that will exercise every code path and even try to get to exit it's code paths entirely - throw an exception, SIGINT, stack overflow, whatever. It's no time to be nice to your code.  Test boundaries - test what happens when you are outside the boundaries - inputs outside what you expect, exceptions from called objects, unexpected types - strings instead of numbers, floats instead of ints.

It should be automated and fast so that whenever that "thing" (your small unit of software)  changes, you can run the test and make sure it still works as expected - as it did prior to the change.

A good and complete suite of unit tests should:
·       Free you to do exploratory testing at a higher level, manually or however
·       Give you confidence to change, refactor fearlessly - if all the same gazinta's produce the same gazouta's for a unit, you're good!

Test code should be treated as well as all the rest of your application code - it's not hack code, junk code, throwaway code because if it is, it doesn't belong in the source tree.

Designing for testability:
  • Adhering to separation of concerns help make code testable - focused functions that do only one thing are easier to test
  • When accessing other objects, try to segregate or wrap the calls so that mocking them becomes easier
  • Segregate validation code so that it can be easily tested
  • Think of patterns that foster testability like the Facade pattern

In our Javascript ecosystem, here are some further specific hints:
  • Look at anonymous functions - very often these can be turned to named functions and makes them testable units. If you have a 10+ line anonymous function, it's probably doing something non-trivial - so it could probably use a test.
  • As you are writing functions, look for ways to make them composable (See https://codewords.recurse.com/issues/four/lazy-composable-and-modular-javascript for more details on concept and techniques).
  • Know what your functions return - or in other words, be explicit about what your functions return. Don't surprise the caller.
  • Try to create functions without side effects - otherwise try to make the side effects singular in purpose and scope for each function.  Test the side effects.
  • Test Failure - make sure a .catch on a .then gets tested.
  •  Perhaps  the use of flogs, such as '__DEV__' will help you segregate test code you want to disappear in production.

References:
xUnit Test Patterns - Website of book by same name. Published by Addison-Wesley - well worth purchasing. Accessed Jan 19, 2018 - an excellent resource on xUnit test patterns - goes over mocks, test doubles, lots of helpful patterns.
Robert Martin of First Class Tests - Accessed Jan 19, 2018 - This article in particular makes a very good case on why you should treat your test code as a 'First Class Citizen' - an equal to the application code it supports. It's a mind shift - an important one.
Collected Wisdom of Martin Fowler - Accessed Jan 19, 2018 - lot's viewpoints on how to get value out of testing - not a one time read, more of an on-going resource.
 

Comments

Popular posts from this blog

You don't really know who you're talking to online...

The following is a story that I think highlights the assumptions that get you into trouble online... https://www.proofpoint.com/us/blog/threat-insight/i-knew-you-were-trouble-ta456-targets-defense-contractor-alluring-social-media This is particularly scary since we found so much utility in online connections during the pandemic and out of necessity, started trusting more online. Please note the timeline for this breach - it was a long, slow process, a key factor in many 'cons'. "Build trust" is a key first step, once someone has identified you as a party. You think...you're convinced you know who your talking to, but if you don't triangulate the identity with some non-online, ideally in-person information, you shouldn't trust. And even if you do get what seems like real-life confirmations of identity, you must look at questioning motives, needs, and keeping danger at arms-length. Online includes email, texting (sms), application chatbots, voice communicati...

Threat Modeling Manifesto

Secure Your Code with Threat Modeling As a software developer, security should be a top priority. By proactively identifying and addressing potential vulnerabilities, you can significantly reduce the risk of breaches and data loss. What is Threat Modeling?   Threat modeling is a systematic approach to identifying, assessing, and mitigating security threats. It involves looking at your system from a hacker's perspective to uncover weaknesses and devise strategies to protect against attacks. See the  OWASP Cheat Sheet   Why is Threat Modeling Important? Proactive Security: By anticipating potential threats, you can take steps to prevent them. Risk Mitigation: Identify and address vulnerabilities before they can be exploited. Regulatory Compliance: Adhere to industry standards and regulations. Enhanced Security Posture: Strengthen your overall security posture. How to Get Started with Threat Modeling   The Threat Modeling Manifesto provides a valuable framewor...

Where threat modeling can shine - an example from the EU MDCG-2019

From the  EU  MDCG 2019-16 Guidance on Cybersecurity for medical devices, December 2019 , this is the guidance on foreseeable risks.  Medical device manufacturers should ensure that a medical device is designed and manufactured in a way that ensures that the risks associated with reasonably foreseeable environmental conditions are removed or minimised. This may include the infield monitoring of the software’s vulnerabilities and the possibility to perform a device update (outside the context of a field safety corrective action) through, for example delivering patches to ensure the continued security of the device. During the risk management process, the manufacturer should foresee or evaluate the potential exploitation of those vulnerabilities that may be a result of reasonably foreseeable misuse. This, however, may depend on the specific situation. For example, using an unsecured memory-stick to enter data into a medical IT system can be considered “reasonably foreseeabl...