Unit testing
Unit testing, component or module testing, is a form of software testing by which isolated source code is tested to validate expected behavior.
Unit testing describes tests that are run at the unit-level to contrast testing at the integration or system level.
History
Unit testing, as a principle for testing separately smaller parts of large software systems, dates back to the early days of software engineering. In June 1956 at US Navy's Symposium on Advanced Programming Methods for Digital Computers, H.D. Benington presented the SAGE project. It featured a specification-based approach where the coding phase was followed by "parameter testing" to validate component subprograms against their specification, followed then by an "assembly testing" for parts put together.In 1964, a similar approach is described for the software of the Mercury project, where individual units developed by different programmers underwent "unit tests" before being integrated together. In 1969, testing methodologies appear more structured, with unit tests, component tests and integration tests collectively validating individual parts written separately and their progressive assembly into larger blocks. Some public standards adopted in the late 1960s, such as MIL-STD-483 and MIL-STD-490, contributed further to a wide acceptance of unit testing in large projects.
Unit testing was in those times interactive or automated, using either coded tests or capture and replay testing tools. In 1989, Kent Beck described a testing framework for Smalltalk in "". In 1997, Kent Beck and Erich Gamma developed and released JUnit, a unit test framework that became popular with Java developers. Google embraced automated testing around 2005–2006.
Unit
A unit is defined as a single behaviour exhibited by the system under test, usually corresponding to a requirement. While a unit may correspond to a single function or module or a single method or class, functions/methods and modules/classes do not necessarily correspond to units. From the system requirements perspective only the perimeter of the system is relevant, thus only entry points to externally visible system behaviours define units.Execution
Unit tests can be performed manually or via automated test execution. Automated tests include benefits such as: running tests often, running tests without staffing cost, and consistent and repeatable testing.Testing is often performed by the programmer who writes and modifies the code under test.
Unit testing may be viewed as part of the process of writing code.
Testing criteria
During development, a programmer may code criteria, or results that are known to be good, into the test to verify the unit's correctness.During test execution, frameworks log tests that fail any criterion and report them in a summary.
For this, the most commonly used approach is test - function - expected value.
Test case
Test double
Parameterized test
A parameterized test is a test that accepts a set of values that can be used to enable the test to run with multiple, different input values. A testing framework that supports parametrized tests supports a way to encode parameter sets and to run the test with each set.Use of parametrized tests can reduce test code duplication.
Parameterized tests are supported by TestNG, JUnit, XUnit and NUnit, as well as in various JavaScript test frameworks.
Parameters for the unit tests may be coded manually or in some cases are automatically generated by the test framework. In recent years support was added for writing more powerful tests, leveraging the concept of theories, test cases that execute the same steps, but using test data generated at runtime, unlike regular parameterized tests that use the same execution steps with input sets that are pre-defined.
Code visibility
Test code needs access to the code it is testing, but testing should not compromise normal design goals such as information hiding, encapsulation and the separation of concerns. To enable access to code not exposed in the external API, unit tests can be located in the same project or module as the code being tested.In object oriented design this still may not provide access to private data and methods. Therefore, extra work may be necessary for unit tests. In Java and other languages, a developer can use reflection to access private fields and methods. Alternatively, an inner class can be used to hold the unit tests so they have visibility of the enclosing class's members and attributes. In the.NET Framework and some other programming languages, partial classes may be used to expose private methods and data for the tests to access.
It is important that code solely for accommodating tests does not remain in the production code. In C and other languages, compiler directives such as
#if DEBUG... #endif can be placed around such additional classes and indeed all other test-related code to prevent them being compiled into the released code. This means the released code is not exactly the same as what was unit tested. The regular running of fewer but more comprehensive, end-to-end, integration tests on the final release build can ensure that no production code exists that subtly relies on aspects of the test harness.There is some debate among developers, as to whether it is wise to test private methods and data anyway. Some argue that private members are a mere implementation detail that may change, and should be allowed to do so without breaking numbers of tests. Thus it should be sufficient to test any class through its public interface or through its subclass interface, which some languages call the "protected" interface. Others say that crucial aspects of functionality may be implemented in private methods and testing them directly offers advantage of smaller and more direct unit tests.
Agile
Sometimes, in Agile software development, unit testing is done per user story and comes in the later half of the sprint after requirements gathering and development are complete. Typically, the developers or other members from the development team, such as consultants, will write step-by-step 'test scripts' for the developers to execute in the tool. Test scripts are generally written to prove the effective and technical operation of specific developed features in the tool, as opposed to full fledged business processes that would be interfaced by the end user, which is typically done during user acceptance testing. If the test-script can be fully executed from start to finish without incident, the unit test is considered to have "passed", otherwise errors are noted and the user story is moved back to development in an 'in-progress' state. User stories that successfully pass unit tests are moved on to the final steps of the sprint - Code review, peer review, and then lastly a 'show-back' session demonstrating the developed tool to stakeholders.Test-driven development
In test-driven development, unit tests are written before the related production code is written. Starting with a failing test, then adds just enough production code to make the test pass, then refactors the code as makes sense and then repeats by adding another failing test.Value
Unit testing is intended to ensure that the units meet their design and behave as intended.By writing tests first for the smallest testable units, then the compound behaviors between those, one can build up comprehensive tests for complex applications.
One goal of unit testing is to isolate each part of the program and show that the individual parts are correct. A unit test provides a strict, written contract that the piece of code must satisfy.
Early detection of problems in the development cycle
Unit testing finds problems early in the development cycle. This includes both bugs in the programmer's implementation and flaws or missing parts of the specification for the unit. The process of writing a thorough set of tests forces the author to think through inputs, outputs, and error conditions, and thus more crisply define the unit's desired behavior.Reduced cost
The cost of finding a bug before coding begins or when the code is first written is considerably lower than the cost of detecting, identifying, and correcting the bug later. Bugs in released code may also cause costly problems for the end-users of the software. Code can be impossible or difficult to unit test if poorly written, thus unit testing can force developers to structure functions and objects in better ways.More frequent releases
Unit testing enables more frequent releases in software development. By testing individual components in isolation, developers can quickly identify and address issues, leading to faster iteration and release cycles.Allows for code refactoring
Unit testing allows the programmer to refactor code or upgrade system libraries at a later date, and make sure the module still works correctly. The procedure is to write test cases for all functions and methods so that whenever a change causes a fault, it can be identified quickly.Detects changes which may break a design contract
Unit tests detect changes which may break a design contract.Reduce uncertainty
Unit testing may reduce uncertainty in the units themselves and can be used in a bottom-up testing style approach. By testing the parts of a program first and then testing the sum of its parts, integration testing becomes much easier.Documentation of system behavior
Some programmers contend that unit tests provide a form of documentation of the code. Developers wanting to learn what functionality is provided by a unit, and how to use it, can review the unit tests to gain an understanding of it.Test cases can embody characteristics that are critical to the success of the unit. These characteristics can indicate appropriate/inappropriate use of a unit as well as negative behaviors that are to be trapped by the unit. A test case documents these critical characteristics, although many software development environments do not rely solely upon code to document the product in development.
In some processes, the act of writing tests and the code under test, plus associated refactoring, may take the place of formal design. Each unit test can be seen as a design element specifying classes, methods, and observable behavior.