Once you write code for a software application, how can you tell the code works? There are different ways to test code but unit testing is one common method.
A good unit test confirms a single assumption about a unit of work in your software application. The unit of work could be a single function or a set of methods in a class. The single assumption is whatever the unit of work is designed to do.
What is a Unit Test?
Unit testing is a set of code, data to test with the code, and details about how the code is used and operated upon. If you test the smallest most useful bits or unit of your code in isolation, and they all work as designed, then your software application should work.
For example, let’s say you have a function that outputs a positive integer (number) or zero when any number is passed into the function. A unit test might test the number output from the function is a positive integer or zero. The unit test might pass a negative integer and zero to the function to see how the function responds. If your function produces a positive integer or zero and does not generate errors when a negative integer is passed into the function, then your code has passed the unit test.
Unit tests are run by a programmer as they test their new or updated code before submitting it for review. The unit tests are created before code is written or during the programming process.
When unit tests are created before code is written, they can work well as design documents for the code and the software application. The tests help identify gaps in the design, for example, missing requirements based on connections to other bits of code in the application. The connections to other code might not be obvious without creating a unit test or actually coding.
Creating these tests before start of coding also provides programmers with the opportunity to challenge requirements and discuss the best way to do the work.
A Simple Example of a Unit Test
What does a unit test actually look like? Here’s a simple function, in a made up language, that adds two numbers when you pass them to the function, followed by a simple unit test:
function addTwoNumbers(int one, int two) var num = one + two return num import test.code.here runUnitTest testAddTwoNumbers() UnitTest = new newUnitTest() result = UnitTest.addTwoNumbers(1,2) testEquals('1+2',result)
Here’s how this made up function and unit test code works:
- If your code calls this function with
addTwoNumbers(3,4)it will return the assigned parameter value of
oneplus the assigned value of
two(line 2), which is 3 + 4 or 7. The result of
one + twois assigned to the variable
numand the value of this variable
- In line 5, the first unit test step is to import the unit test code suite, for example, JUnit.
test.code.hereis whatever core unit test library is called first. You might import other libraries in the test suite.
- The next step is to call the unit test code suite function to run a test, in this mythical case,
runUnitTestin line 6. To create a test, we create a function to run as part of the test function, in this case,
testAddTwoNumbers()is a function to run when
- Within the
testAddTwoNumbers()function, starting on line 7, we create a new object with
newUnitTest()in line 8 to hold all classes and other details from the unit test suite code. We assign this object, with all its classes and details, to a new variable called
UnitTest. To be clear,
newUnitTest()is a function within the unit testing library, in this case
test.code.herefrom line 5, which creates objects that contain unit test functionality from the library.
- We then create a result variable, in line 9, and assign it the value of running the
addTwoNumbers()function with parameters
2, all within our
UnitTest()object created on line 8.
- Finally, in line 10, we use the
testEquals()function within the unit test code suite — in this case, the
test.code.herefile imported in line 5 at the top of our unit test code — to compare the result of adding
1+2with the result from running
addTwoNumbers(defined at lines 1-3) with the parameters
addTwoNumbers() with the parameters 1 and 2 should return 3. If the
testEquals(‘1+2’,result) code returns
true, the unit test returns nothing. If the return result is false, however, the unit test code and libraries will generate an error.
Key Properties of Unit Tests
What are some key properties of a good unit test? Your test should be able to be automated, run as a separate process without manual intervention, for example, with a script called at specific times of day. The test should have control over all the inputs and outputs into the code being tested. There should be no need to run your test in a specific order with other unit tests. The test should work as is. There should be no access to a database or file required. And the usual requirements for code apply: the test should be readable, easy to maintain, and produce a result you trust.
As a practical matter, a good unit test also might include a descriptive failure message. When the unit tests are run automatically, and fail, the test output logs filled with failure messages should help the programmer avoid having to find and study the unit test to discover the reason for a failure.
Unit tests also can be run automatically, for example, as part of building the software application every day. This helps programmers find and fix bugs quickly on a daily basis. However, if programmers run unit tests as they complete their code, the automated unit tests should pass.
One interesting question with unit testing has to do with databases. You might think a unit test would work best if the code connects to a database. However, this can introduce errors and distract from the goal of these tests: to confirm code works as designed. Unit tests work better if the database inputs and outputs are faked as part of the unit test. This allows the programmers to precisely determine whether or not the code produces the intended results without the distraction of working with a database.
Testing the connection to the database, and the live interactions between your code and the database, can be saved for integration testing. Where unit testing confirms small parts of your code work as planned, integration testing confirms your software application works as planned. Unit testing is about chunks of code. Integration testing is about architecture and structure of all your code.
Unit and integration testing are parts of test driven development, also called TDD. Testing drives the design and creation of code and provides a useful start point for programming. The alternatives range from design documents to design while programming. Both can provide useful insights into how best to create software. But test driven development is more stable and often yields more nuanced insights.
Common Unit Testing Terms
Here are a few terms you’ll find when you research and use unit testing in your code:
Under Test refers to the code being tested, for example, a function or class you write a unit test for is under test.
Fixture is any precondition used as part of the test. This might be a string value or integer passed into your code as a parameter. Fixtures reduce the complexity of your unit test and put focus on code under test.
Test Coverage is the amount of code actually tested with your unit test. Because unit tests focus on small chunks of code, test coverage doesn’t include the integration of your function or class with other parts of your code, or how your application integrates with a database.
Mocks or Stubs are canned data used in a unit test to mimic the results of other parts of your code without having to call that code as part of your unit test. These help focus your unit test on the smallest useful bits of your code. For example, if your code pulls a bunch of data from a database, you might have a stub that is simply a list of items typically returned from the database — say, the text Fred, Flintstone, 123 Bedrock Lane, Bedrock — and fed into your code. Your unit test code would assign the list of items to a variable which would then be used by your code as if it were connected to a real database.
Unit Testing Frameworks
What is Unit Testing and How Do You Do It?
What Makes a Good Unit Test?
It’s Not about the Unit Test
A Simple Unit Test
I simplified his real world example to create something less distracting. This example uses JUnit.
An experienced programmer discusses the nuances and complexities of unit testing.