What Is Unit Testing?
Agile 101
Agile Methodologies
Agile Practices
Agile Metrics
Agile Artifacts
Scaled Agile
Unit Testing: A Tutorial on What It Is, How To Do It + Tools To Use
What Is Unit Testing?
Unit testing is essential. Without it, you’re walking on eggshells all the time. That’s what you hear, at least. But why is that? And what is it, anyway?
Let’s dive in.
Unit testing is about verifying the behavior of the smallest units in your application.
Technically, that would be a class or even a class method in object-oriented languages, and a procedure or function in procedural and functional languages.
Functionally, it may be a set of closely related classes. Like a `Deer` and its supporting `Head,` `Tail,` and `Locomotion` classes.
What you consider a unit of work is whatever makes sense to you. There are no hard and fast rules. As long as it helps you understand and think about your application.
That’s different for what a unit test is.
What Makes a Unit Test a Unit Test?
Michael Feathers (author of “Working Effectively with Legacy Code”) put the distinction at the process and system level.
Unit tests need to run in isolation because they need to run fast.
The entire unit test suite for an application needs to run in minutes, preferably seconds. You’ll see why later.
That’s why unit tests can’t use any external processes or systems. No I/O operations of any kind (database, file, console, network) except logging test failures and perhaps reading the default feature toggle configuration at the start of a test run.
If your test code (or the libraries it uses) does I/O or accesses anything outside its process, it is not a unit test, but an integration test.
What Is the Purpose of Unit Testing?
Many say that the purpose of unit testing is to validate that each unit of work behaves as designed, expected, or intended. And that you can run your unit tests every time someone makes a change. With just the push of a button.
But the real purpose of unit testing is to provide you with almost instant feedback about the design and the implementation of your code.
Creating unit tests, or rather the ease or difficulty with which you can create them tells you precisely how testable your code is. How well you designed it. And if you listen to it, instead of using hacks to overcome any difficulty, everyone wins.
Here’s how.
What Are the Benefits of Unit Testing?
Unit testing has many benefits.
I’m sure you’re wondering by now how to get all of this.
How To Write Unit Tests (Using Best Practices)
Creating unit tests is the same as developing any code, but there is a difference.
You create functional application code to solve a problem for your customers.
You create unit tests to solve the problems that arise in developing that application code.
Guess what that means.
Yes, you are your own customer! And of course, you want to make your life as easy as possible
So, make the most of these best practices
When a test depends on how another test changed the environment (values of variables, contents of collections, etc.), you’ll have a hard time keeping track of the starting conditions for each test. More importantly, when you get unexpected results, you’ll forever wonder whether test conditions or production code caused them.
Use setup methods and nested utility classes if you like, but avoid test class hierarchies and general utility classes. It’ll save you having to hunt through several base classes or utility class units to find the values a test uses.
When an action should have more than one effect, test each of them in a different method. When you feel tempted to use more than one assert, ask yourself whether you’re asserting the most significant fact.
Resist the urge to use hacks and “automagic.” Hacks only ever address symptoms, and automagic reduces the transparency you need to figure out why a test fails that should succeed or, worse, why a test succeeds that should fail.
Tackle what’s causing the pain using the most straightforward means possible. Quite often, you can make simple changes to make at least part of a class more testable. Working effectively with Legacy Code by Michael Feathers is an excellent resource for this.
Common Pitfalls in Unit Testing
Simple mistakes can trip you up badly. Worse, they can lull you into a false sense of security.
These are the mistakes you want to avoid
Such a test will never fail! If that’s okay because you’re merely verifying no exceptions occur, make that explicit with an assert and an appropriate message.
It’s an indication that you’re testing after the fact (instead of a test-first approach), and you’re creating headaches for yourself in keeping track of which tests are complete and should succeed and which tests fail because they’re incomplete.
When you don’t start with a failing test, you won’t know whether it succeeds because you have an error in your test or because the functional code is correct.
Ideally, you want to run all unit tests at every stage of a red-green-refactor cycle, whether you use that with or without a test-first approach such as TDD and BDD.
Slow tests interrupt your flow.
It’s okay, by the way, to use a unit test framework (see below) to write slow(er) tests, but they’re not unit tests, and you want to keep them in a separate test suite.
That’ll give you headaches when tests fail in one environment and succeed in another.
Stay with me now. You’re almost there.
Here are some tools and techniques for you to use.
Unit Testing Tools and Techniques
Unit Test Frameworks
Unit test frameworks provide everything you need to create unit tests
You can find one or more unit test frameworks for almost every programming language out there.
Unit Test Runners
Unit test runners discover the tests in your test code automatically, run all tests or a specific selection, and then report the test results.
They come as IDE extensions and as stand-alone command-line utilities. You can use the latter in build scripts, so integration builds fail when a merge breaks existing code.
Unit test frameworks have their own runners, but you can also find dedicated runners that can discover and run tests written using multiple frameworks.
Mocking Libraries
Mocking libraries allow you to create test doubles, or fakes, easily.
You use these to provide a class under test with instances of their collaborating classes. This way, you can easily customize the collaborators’ behavior to the needs of your test.
Dependency Injection / Inversion of Control
Inversion of Control, or Dependency Injection, is a design pattern you use to break hard ties between classes.
Instead of class A instantiating a class B5, you provide it with an instance of the most abstract B5 ancestor that gives A the methods and properties it needs.
It makes unit testing a class with collaborators easier because it’s much easier to provide it with fakes.
Unit Testing Resources
So, Go. Unit Test Your Way To Confident Coding
So, that’s it—everything you need to know to start your unit testing career.
You’ll never have to walk on eggshells again when you change your code.
Check out Nimble Now!
Humanize Work. And be Nimble!