
Tosca tester
Unit testing is a type of software testing that focuses on individual small pieces of code or components of a software system. The goal of unit testing is to verify that each small part – unit of the software works as it should and meets the requirements. Unit testing is usually performed by developers and is done at the beginning of the development process before the code is integrated and tested as a whole.
Unit tests are automated and are performed whenever code changes to ensure that the new code does not break existing functionality. Unit tests are designed to verify the smallest possible unit of code, such as a function or method, and test it in isolation from the rest of the system. This allows developers to quickly identify and fix potential problems early in the development process, improving the overall quality of the software and reducing the time required for later testing.
When discussing unit testing, it is essential to distinguish differences between unit tests and integration tests. While unit tests focus on testing individual components or units of code in isolation, integration tests verify the interactions between these units and ensure that they work together as expected.
In the STLC (SDLC) or V model, unit testing is the first level of testing performed before integration testing.
There are 2 types of unit testing: manual and automated.
Manual unit testing is a practical approach where testers write and execute test cases without the help of automation or unit testing tools. This type of unit testing is often more flexible and can be more concise in certain contexts. However, it is generally more time-consuming and prone to human error.
Automated unit testing:
The developer writes a piece of code in the application just to test the feature. Later, the test code is commented out and finally removed when the application is deployed. Using an automation framework, the developer programs criteria into the test to verify the correctness of the code. During test case execution, the framework logs failed test cases. Many frameworks also automatically flag and report these failed test cases in summary. Depending on the severity of the failure, the framework may stop further testing.
The unit testing workflow is as follows: 1) Creating test cases 2) Review 3) Basic evaluation 4) Execution of test cases
There are 3 types of unit testing methods. They are:
Black Box Testing: this testing technique is used when testing components for input, user and output parts.
White Box Testing: this technique is used to test the functional behaviour of a system by inputting the input and checking the output of the functionality, including the internal structure of the design and the code of the modules.
Gray Box Testing: this technique is used in the execution of relevant test cases, test methods and test functions, and in the analysis of the code performance of modules.
Test fixtures are the components of a unit test responsible for preparing the environment needed to execute the test case. These are the prerequisites and settings you need to run the test cases. They are also called “test context” and create initial states for the unit under test to ensure more controlled execution. They are very important because they provide a consistent environment to repeat the testing process.
For example, let’s say we have a blogging application and we want to test the post creation module. Test fixtures should include:
A unit test case is simply a piece of code designed to verify the behaviour of another unit of code, ensuring that the unit under test performs as expected and produces the desired results. Here is a unit test example of a function that calculates the sum of two numbers a and b:
use PHPUnit\Framework\TestCase;
class MathTest extends TestCase {
public function testSum() {
// Premenné
$a = 5;
$b = 7;
$expectedResult = 12;
// Funkcia
$result = Math::sum($a, $b);
// Assertion - tvrdenie
$this->assertEquals($expectedResult, $result);
}
}
The assertion used in this code is $this->assertEquals($expectedResult, $result); it verifies that a + b actually equals the expected result 12.
Test runner is a framework for organizing the execution of multiple unit tests and also for reporting and analyzing test results. It can search the codebase or directories to find test cases and then execute them. What’s great is that the test runner can run tests in order of priority while managing the test environment and handling setup/teardown operations. With the test runner, the unit under test can be isolated from external dependencies.
Test data should be carefully selected to cover as many scenarios as possible for a given unit, thus ensuring high test coverage. In general, we should prepare data for:
Mocking and stubbing are essentially surrogates for the actual dependencies of the unit under test. When unit testing, developers need to focus on testing a specific unit in isolation, but in certain scenarios they will need two units to perform a test.
For example, we can have a User class that depends on an external EmailSender class to send email notifications. The User class has a sendWelcomeEmail() method that calls the EmailSender to send a welcome email to a newly registered user. If we want to test the sendWelcomeEmail() method in isolation without actually sending the email, we can create a dummy object of the EmailSender class. The developer will then not have to worry about whether the external unit (EmailSender) works well or not. The unit under test is indeed tested in isolation.
Here are some commonly used unit test automation tools:
A unit can be almost anything you want – a line of code, a method, or a class. In general, however, smaller is better. Smaller tests give you a much more detailed view of how your code works. There’s also the practical aspect that when you’re testing very small units, tests can be run quickly, like a thousand tests per second.
Consider this code sample:
def divider (a, b)
return a/b
end
Using Ruby, these small tests might look like this:
class smallTest < MiniTest::Unit::testCase
def tiny_test
@a=9
@b=3
assert_equal(3, divider(a, b))
end
end
This example is too simple, but it gives an idea of what I mean by small. If you want to see the direct procedure of creating a unit test, click here on unit test tutorial.
Unit tests usually consist of three phases:
The basic thing to consider when writing a test is the choice of test title. Good test names improve the readability of the code both for the programmer and for others who may work on the code in the future. There are standard naming conventions (read more here – unit test naming convention) that can be used in unit testing.
Keeping test codes as simple as possible is key to maintaining their correctness. Unit test codes can also have bugs, especially at high levels of complexity.
The deterministic test always gives the same result regardless of the input, as long as the code does not change. This minimizes the occurrence of false positives and false negatives. Tests must be deterministic because a test that presents variable results cannot be trusted.
Each test should be used to test one use case. A particular test program should test a single block of code. This verifies the output and gives a better insight into the cause of the detected errors without any doubt to where they originated.
Developers should fully and thoroughly test the software application to the greatest extent possible. However, this is not always feasible due to time and financial requirements. Nevertheless, developers must try to perform unit tests of the program as much as possible.
Slow tests are difficult for developers to perform. They slow down the process and cannot be used frequently. But it is true that test speed is subjective and depends on the subject being tested, but any test that takes more than an hour and 15 minutes can be classified as slow.
Although unit tests can be performed manually, current procedures support an automated method of testing. It has proven to be not only more efficient and cheaper but also more time-saving.
Tests created months ago may no longer be valid. It is very important to perform tests regularly, as changes in requirements and code can cause problems that don’t match what they should test. Neglecting to update tests can lead to false or misleading results.
Solution: regular testing is essential, preferably using automated unit testing. You should also make sure you are compliant.
One of the major mistakes in unit testing is not finding the right balance. Testing every line of code can be exhaustive and time-consuming, while testing too few parts can cause critical bugs to be overlooked. It is essential to focus on testing the most important features and edge cases.
Solution: use techniques such as code coverage analysis and risk assessment to identify the parts of the code that need the most testing. Prioritize testing complex features, error-prone areas and user-centric features.
The use of incorrect or inappropriate data for testing can lead to misleading results. Such examples are false positives or negatives. For example, conducting tests using null values could mask real errors or different scenarios. Similarly, running tests with too similar and very random data could reduce the efficiency and transparency of your testing processes.
Solution: it is essential to use practical and inclusive data that includes a variety of situations and inputs. Use data generators and test data checking to prevent repetition.
A significant pitfall in unit testing is checking incorrect aspects or evaluating something that is not a unit. Evaluating non-unit components such as database queries, web service calls, or user interfaces leads to unreliable and slow tests that rely on external elements.
Solution: use mocking and stubs for efficient unit testing. These techniques simulate unit dependencies, creating a controlled environment that isolates the unit from the larger system.
Omitting test reviews means that it increases the likelihood of test errors. Code reviews increase the quality of unit tests in the team.
Solution: for optimal results, perform code reviews directly, either in person or via screen sharing.
Inserting logic into unit tests makes them harder to read and maintain and increases the risk of errors. If your unit test contains logic, it suggests that you may not be creating the right unit tests.
Solution: if there is a bug in the test units, it needs to be removed: Limit the number of assertions in each test. If you have too many assertions, maintenance becomes difficult.
One reliable way to have unit tests that take forever to run is to write unit tests that do things like write files to disk or pull information from databases. So avoid using external elements because it will slow down the test and also because when you do that, you’re not actually writing unit tests. Unit tests are targeted checks that isolate code and assert how it should behave. Check things like “if I pass 2 and 2 to the add(int, int) method, does it return 4?”. This is the scope of the unit test.
Solution: Don’t test databases, write to disk and so on, test only small parts of the code.
As you can see, unit testing can be very challenging. But component testing is always necessary at some level. That’s for sure.
If you want to test your knowledge of unit tests or are preparing for an interview, be sure to check out this list of frequently asked questions and answers – unit test questions for interview.
If you speak German and are looking for an IT tester or IT automation tester job, take a look at our employee benefits and respond to our latest job offers!