Learn to Run Unit Tests: Beginner-friendly Unit Testing Tutorial
There’s always the first step: the first time you wrote a code, the first time you accomplished your goal, and the first time you failed a project. You never forget your first time, and we hope you will not forget your first tests. You might have already written a few tests, and you might even remember them as unmaintainable, slow, awkward, and bad. (Most people do). On a more upbeat note, you might have had a great first experience with unit tests, and you didn’t experience any failed tests, and you are reading this post to see what you might be missing.
This guide will give you an overview of unit testing approaches and frameworks. We will also touch on unit test types and basic concepts that are associated with unit testing. Let us begin by defining what a unit test should be:
- What is unit testing’s simple definition?
- Basic concepts of unit testing
- What can I do with unit tests?
- Unit test methods
- Unit testing techniques
- Example of unit tested: Mock objects
- Unit testing and Test Driven Development (TDD)
- Run test with test explorer from a visual studio
- How to run tests in parallel
- Unit test runners
- Writing unit tests
- Integration test vs. unit test
- Testing python in visual studio code
What is Unit Testing’s Simple Definition?
Unit testing is a code written to test a code. It is used to ensure the quality of the working product. Unit testing is what you do as a developer to make sure your code is working as desired. Other developers also utilize your tests to learn how to use your code. Developers can read and run your tests to understand how it works. A full suite of unit tests is usually more helpful than documentation or large amounts of comments.
Lastly, your unit tests ensure that changes do not unintentionally break existing code via regression testing. Typically, a regression defect is a bug that breaks existing working code, and your unit tests are run to check for regression defects.
AAA: Arrange, Act, Assert
Unit testing typically follows a common strategy- AAA, Arrange, Act, Assert.
- First, arrange all preconditions for your test to run
- Second, Act or execute your code
- Lastly, asset the correct things happened
Basic Concepts of Unit Testing
Here are seven basic concepts of unit testing:
- Test case: A test case means the individual unit of testing. It is used for checking for a particular response to a specific set of inputs.
- Test fixture: A test fixture symbolizes the preparation required to perform one test or set of tests and any linked cleanup actions. This might entail, for instance, creating proxy or temporary directories, databases, or starting a server process.
- Test runner: This component orchestrates the execution of tests and offers the outcome of the system under test to the user. The runner might use a textual interface, graphical interface, or return an exceptional value to show the tests’ results.
- The return values: This is a test suite that supports all the tests defined for each name.
- A test suite: This is a collection of test suites, test cases, or both. It is utilized to aggregate tests that should be executed together.
- Continuous integration: It’s a development practice where developers integrate code into a shared repository frequently, preferably several times a day.
- Public void: This means that the testing method is visible and can be called from other objects of other types
What Can I do with Unit Tests: Benefits of a Tested Code
Unit testing aims to check individual units of your source code separately. Think of a unit as the smallest part of code that can be tested in isolation, for instance, a class method or a free function.
Unit testing helps:
- Code documentation: Debugging, running, or even just reading tests can give a lot of information about how the initial code works so that you can utilize them as implicit documentation. Typically, code documentation is a drag, and it shows, mostly by how little code documentation gets written. Unit testing can simplify the documentation burden by encouraging better coding practices and leaving behind pieces of code that show what your product is doing.
- Avoid regressions: When you have a suite of unit tests, you can run it iteratively to ensure that the application performance is as desired every time you add new functionality or introduce changes.
- You can use test results to modularize your code: Since code’s testability depends on its design, unit tests facilitate breaking it into specialized easy-to-test pieces.
- It assists the developers in understanding the testing code base class and allows them to make changes faster.
Note that there are a few uses of unit testing that you will want to avoid when possible. Creating integration tests that cross-system borders and touch 3rd party systems or databases can be done; however, this quickly results in a test suite that takes longer and longer to run with each added test. There are many test frameworks out there that specialize in higher-level testing. To test larger pieces of your product at a time, you may want to investigate those other frameworks.
Another risky area is end to end tests. These usually require careful ordering, mock dependencies on other tests, and careful setup to get your system in a particular test-ready state. Similar to integration testing, there are many tools to select from made just for this purpose. You can do these things with frameworks; however, it might quickly become more work than it’s worth.
Unit Test Methods
There are three main ways to run the tests:
- Run an individual unit test
- Run an individual test suite (usually as a set of tears for a class)
- Run all unit test suites at once
Organization of Unit Tests
Contrarily to other rosetta tests, unit tests are compiled, extra code baked directly into the codebase at test/source/main/Rosetta. The inside of this test directory reflects the src directory, so that code on specific classes lives in the same place. Generally, testrun.py is the master script that RUNS the unit test checks.
Running your tests using Command Lines Interface
The unittest module can be utilized from the command line to run tests checks from classes, modules, or even individual test methods:
- Python-m unittest test-module.TestClass.test_method
- Python-m unittest test_module.TestClass
- Python-m unittest test_module1 test_module2
Generally, you can pass in a list with any combination of fully qualified class, module names, or method names.
Note that test modules can be specified by file path as well:
Python -m unittest tests/test_something.py
This enables you to utilize the shell file name completion to specify the test module. The file specified (test package) should still be importable as a module. The path is changed to a module name by getting rid of the ‘.py’ and changing path separators into. ‘’. To execute a test file that is not importable as a module, you should execute the file directly instead.
You can run tests with more detail by passing in the -v flag: python -m unittest -v test_module
Re-using old test codes
Some users will find that they have an existing test code that they would like to run from unittest without changing every old test function to a TestCase subclass.
Therefore, unittest offers a FunctionTestCase class. This subclass of TestCase can be utilized to wrap an existing test function. Tear-down and set-up functions can also be offered.
Given the following test function:
Def testSomething ():
something = makeSomething()
assert something.name is not None
You can create an equivalent test case instance as follows, with tear-down and optional set-up methods:
testcase = unittest.FunctionTestCase(testSomething,
It is worth noting that even if FunctionTest Case can be utilized to rapidly change an existing test base over to a unittest-based system, the method is not commended. Taking the time to establish proper TestCase subclasses makes future test refactorings infinitely simpler.
The existing tests might have been written utilizing the doctest module in some instances. If so, doctest offers a DocTestSuite class that can automatically build unittest—testSuite instances from the existing doctest-based tests.
Compiling Unit Tests
To create the unit tests, run scans with cat+test. You should not specify targets (such as bin) while doing so. Unit tests are intended to be built and run in debug mode since this catches more errors. For a build to succeed, the underlying Rosetta libraries must exist (you must have built without cat+test first). Nonetheless, the existence of binaries is not significant. Typically, here is how test compiling looks like:
scons.py -j #numproc mode=debug
scons.py -j #numproc mode=debug cat=test
Test compiling is an order of magnitude faster than the main codebase (this is actually a bad sign indicating that we do not have good unit test coverage).
Run a Single Unit Test or Test Suites
This method is beneficial if you work on unit test debugging and save time by skipping tests.
To run all unit tests from a single test suite, deploy the –one option to test/run.py.
Sometimes it is vital to run a test by hand. To do this, first locate its executable. All unit test executables are currently located in the build/test/… directory; executable files are named by including .test to the unit test suite name. It’s worth noting the path may vary based on the platform you are working on: for instance, for 32-bit Linux compiled with GCC, it will be: ‘build/test/debug/linux/2.6/32/x86/gcc’.
Additionally, the test executable should be running from the build directory. This is vital since some unit tests will try to locate extra files required using relative paths. For instance, below is a command to run only the core test executable:
./core.test –database /path/to/rosetta/main/database –mute core
If you want to run one test or just one suite, you will need to supply the name of the test function or the name of the suite as the first argument to the test executable.
Unit Testing Techniques
The unit testing techniques are mainly classified into three parts:
- Black box testing
- White box testing
- Gray box testing
|Black box testing||Entails testing of user interface together with output and input|
|White-box testing||White box testing involves testing the functional behavior of the software application.|
|Gray box testing||Gray box testing is used to execute test suites, test cases, test methods, and perform risk analysis.|
Code coverage techniques used in unit testing include:
- Finite state machine coverage
- Condition coverage
- Branch coverage
- Decision coverage
- Statement coverage
Unit Testing Tools
There are many automated tests software solutions accessible to assist with unit testing. Here are a few examples:
- Junit tool: Junit is a free-to-use testing tool utilized for the Java programming language. It offers assertions to identify the test method. This tool first tests data and is then inserted into the piece of code.
- JMockit: This is an open-source Unit testing tool. It is a code coverage tool with path and line metrics. It enables mocking API with verification syntax. It offers Line coverage, Path Coverage, and Data Coverage.
- NUnit: The NUnit tool is a widely used unit-testing framework used for all .net languages. This is an open-source tool that enables writing scripts manually. NUnit supports data-driven tests which can run in parallel.
- PHPUnit tool: PHPUnit is a unit testing tool for PHP programmers. It takes small sections of code, known as units, and tests each of them separately. The tool also enables developers to use pre-define assertion methods to assert that a system behaves in a certain manner.
- EMMA: This is an open-source toolkit for analyzing and reporting code written in Java language. Emma supports coverage types such as method, line, basic block. EMMA is Java-based, without external library dependencies, and can access the source code.
Example of Unit Tested: Mock Objects
Unit testing depends on mock objects being created to test sections of code that aren’t part of complete software. Mock objects fill in for the missing sections of the program. For example, you might have a function that needs objects or variables created yet. In unit testing, such will be accounted for in the form of mock objects developed mainly for the purpose of the unit testing done on that part of the code.
Unit testing and Test Driven Development (TDD)
Unit testing in Testdriven development entails extensive use of unit testing frameworks. A unit test framework is used to create automated unit tests. Unit testing frameworks aren’t unique to TDD; however, they are vital to it. Here we look at some of what TDD brings to the world of unit testing:
- Quick and easy integration is made possible
- All public classes in the applications are tested
- Depend heavily on testing frameworks
- You will write unit tests before the code
Run Test with Test Explorer from a Visual Studio
You can utilize test explorer in running tests from visual studio or third-party unit test projects. You can also deploy test explorer to group multiple tests into categories, filter the test list, and save, create tests and run playlists of tests. You can also utilize Test Explorer to debug unit tests and, in Visual Studio Enterprise, analyze code coverage.
Test explorer can run tests from many test projects in an application and from test classes that are a section of the production code projects. Test projects can utilize different unit test frameworks. When the code under test is written .NET, the test project can be written in any language that also targets .NET despite the language of the target code. Typically, Native C/C++ code projects should be tested using a C++ unit test framework.
To run tests in an explorer, choose the individual tests you want to run, open the right-click menu for a chosen test, and select run selected tests (or press CTRL +R, T). Choose the individual tests you want to run, open the right-click menu for a chosen test, and select run selected tests (or press Ctrl + R, T). Then view the source code of a test method to display the source code for a test method in the visual studio editor, choose the test and then select Open Test on the right-click menu (or just press F12).
Choose add to playlist > NewPlaylist. On the right-click menu. Then still on the right-click menu, select add to the playlist and then select the playlist that you would like to add the tests too. On the right-click menu, select Add to Playlist > New Playlist. On the right-click menu, select Add to Playlist > New Playlist.
How to Run Tests in Parallel
PHPUnit and Lavarel execute your tests sequentially within a single process by default. Nonetheless, you might significantly lower the amount of time it takes to run your tests by running multiple tests simultaneously across multiple processes. To get started, make sure your software application is based on version ^5.3 or greater of the collision/ nunomaduro package. Then, incorporate the –parallel option when executing the test Artisan command:
Php artisan test –parallel
Generally, Laravel will create as many processes as available CPU cores on your machine by default. Nonetheless, you might adjust the number of processes deploying the –processes option:
PHP artisan test –parallel –processes=4
Unit Test Runners
Unit test runners automatically discover the tests in your test code, run all tests or a particular selection, and then report the results. They come as IDE extensions and as stand-alone command-line utilities. You can utilize the latter in build scripts, so integration builds to fail when merge breaks existing code. Unit tests frameworks have their runners; however, you can also find dedicated runners that can discover and run tests written deploying many frameworks.
Structuring a simple test
Before you dive into writing tests, you will want first to make a couple of decisions:
- What do you want to test?
- Are you writing an integration test or a unit test?
Then let the structure of a test loosely follow this workflow:
- Develop your inputs
- Execute the code that is being tested, capturing the output
- Compare the output with your expected results
For this solution, you are testing sum(). Generally, there are many behaviors in sum() you could check, like:
- Can it add up a list of whole numbers?
- Can it total a set or tuple?
- Can it add up to a list of floats?
- What happens when you provide it with a wrong value, like a string or a single integer?
- What if one of the values provided is negative? How does it behave?
To write a new test case, deploy the make: test Artisan command. Typically tests will be placed in the tests/ feature directory by default:
Php artisan make:test UserTest
If you want to create a test within the tests/Unit directory, you might utilize the –unit option when you are executing the make: test command:
PHP artisan make:test UserTest –unit
If you want to create a Pest PHP test, you might provide the –best option to the make: test command:
PHP artisan make:test UserTest –unit –pest
PHP artisan make:test UserTest –pest
After you have executed the test, you can define test methods as you usually would utilizing PHPUnit.
Unittest supports simple test discovery. To be compatible with test discovery, all test files must be packages or modules (inclusive of namespace packages) that are importable from the top-level directory of the project (this indicates that their filenames should be valid identifiers). Test discovery is executed in TestLoader.discover() but can also be used from the command line.
Unit Tests Vs Integration Test
Another essential thing to consider is the difference between unit testing and integration testing. A unit test in software engineering aims to verify the behavior of a comparatively small piece of software, independently from other parts. Unit tests are narrow in scope and cover all cases, ensuring that every single part works correctly. Contrary, integration tests demonstrate the different parts of a system work together in the real-life environment. These tests validate complex cases (we can think of integration testing as a user performing some high-level operation within our system) and often require an external resource, such as web servers or databases, to be present.
A sensible combination of unit tests and integration tests ensures that every unit works correctly, independently from others and that all these units act nicely when integrated, giving you a high level of confidence that the system works as anticipated. Another type of testing is functional tests. While integration tests and unit tests give you confidence that your application works. Functional tests look at the application from the user’s point of view and test that the system works as desired.
Testing Python in Visual Studio Code
Python tests are Python classes that reside in separate files from the tested code. Each test framework specifies the structure and naming of test files and tests. When writing a python test, you might occasionally need to step through and analyze tests in the debugger, either since the tests themselves have a code defect you need to track down or better understand why an area of code being tested is failing.
This section will demonstrate how to analyze the test: set a breakpoint on the line in the right-clock on the gutter decoration next to the function definition and choose to debug Test, or alternatively select the Debug Test icon next to that test in the Test Explorer. You can utilize the following commands from the Command Palette to debug tests: Test: Debug All Tests- launches the debugger for all tests in your workspace. Next, debug tests in the current file- launches the debugger for the tests you have defined in the file you have opened in the editor.
Next test: debug test at cursor- launches the debugger only for the method of having your cursor focused on the editor.
Additionally, you can use the Debug Test icons in Test Explorer to launch the debugger for all tests in a chosen scope and all discovered unit tests. You can also transform the default behavior of clicking on the gutter decoration to debug tests rather than run by changing the setting value to The debugger works the same for tests as for other Python code, inclusive of breakpoints and variable inspection.
This configuration will be utilized when you run tests: Debug All Tests, Test: Debug unit tests in Current File, and Test: Debug Test at Cursor commands.
Test fails (test failed): Debug tests that failed in the most current test run.
Test: Debug last runs debug tests implemented in the most recent test run.
Test: Debug Test at Cursor Debug is the method of having your cursor focused on the editor.
Similar to Python: Debug Test Method…
Test: Debug Tests in Current File Debug tests in the file are currently focused on the editor.
AssertEquals:Test that first and second are equal. If the values do not compare equal, the test will fail.
Test: Now rerun Last Run Debug tests that were implemented in the most recent test run.