Software testing

Jiri Caga
8 min readApr 12, 2024

What is Software Testing?

Software testing is a systematic process of evaluating a software application or system to detect any discrepancies between expected and actual results. It involves executing the software with the intent of finding defects or errors, and ensuring that it meets the specified requirements and quality standards.

Why are tests needed?

  1. Detecting Bugs and Errors: Testing helps identify defects or bugs in the software, such as functionality issues, logic errors, or performance bottlenecks. By uncovering these issues early in the development process, teams can address them before they escalate into more significant problems that may be costly to fix.
  2. Ensuring Quality and Reliability: Tests ensure that the software meets the desired level of quality and reliability. By validating its functionality, performance, security, and usability, testing helps build trust among users and stakeholders, enhancing the overall user experience.
  3. Meeting User Expectations: Testing ensures that the software meets user expectations and requirements. By validating against user stories, specifications, and use cases, teams can verify that the software behaves as intended and delivers the desired features and functionalities.
  4. Reducing Risks: Testing helps mitigate risks associated with software failures. By identifying and fixing defects early, teams can minimize the likelihood of system crashes, data corruption, security breaches, and other potential issues that may lead to financial losses or damage to reputation.
  5. Optimizing Performance: Testing evaluates the performance and scalability of the software under different conditions and workloads. By measuring response times, throughput, and resource utilization, teams can optimize the software’s performance to ensure it can handle expected loads and scale effectively as needed.
  6. Compliance and Regulation: In certain industries, such as healthcare, finance, or aviation, compliance with regulatory standards and requirements is essential. Testing helps ensure that the software meets these standards and complies with regulations, reducing legal risks and potential penalties.

Type of tests

Unit testing

Unit tests are a fundamental type of testing in software development that focus on verifying the functionality of individual units or components of a software application. A unit typically refers to the smallest testable part of the software, such as a function, method, or class. Unit testing is performed by developers as part of the coding process and is often automated to ensure efficiency and accuracy.

Here are some key characteristics of unit tests:

1. Isolation: Unit tests are designed to be isolated from other parts of the software. This means that each test should focus on testing a specific unit of code in isolation, without dependencies on external systems or components. Isolation is typically achieved using techniques such as mocking or stubbing to simulate external dependencies.

2. Independence: Unit tests should be independent of each other, meaning that the outcome of one test should not affect the outcome of another. This allows developers to run unit tests in any order and easily identify the cause of failures when they occur.

3. Repeatable: Unit tests should be repeatable, meaning that they should produce the same result every time they are executed. This ensures consistency in testing and helps developers quickly identify and fix defects.

4. Fast: Unit tests are designed to be fast, with execution times typically ranging from milliseconds to seconds. Fast-running tests enable developers to quickly iterate on code changes and receive immediate feedback on the impact of those changes.

5. Focused on Behavior: Unit tests focus on testing the behavior of individual units of code, rather than the implementation details. This allows developers to refactor code with confidence, knowing that as long as the behavior remains unchanged, the tests will continue to pass.

6. Automated: Unit tests are often automated using testing frameworks and tools, such as JUnit for Java, NUnit for .NET, or pytest for Python. Automation helps ensure that tests can be run frequently and consistently throughout the development process, without requiring manual intervention.

Overall, unit testing plays a crucial role in software development by helping developers identify and fix defects early in the development process, improving code quality, and reducing the likelihood of bugs in production. By writing thorough and well-designed unit tests, developers can build robust and reliable software applications that meet user requirements and expectations.

Example of unit tests:

import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class MultiplyTest {

@Test
public void testMultiplyPositiveNumbers() {
int result = Multiply.multiply(3, 4);
assertEquals(12, result);
}

@Test
public void testMultiplyNegativeNumbers() {
int result = Multiply.multiply(-2, -5);
assertEquals(10, result);
}

@Test
public void testMultiplyPositiveAndNegativeNumbers() {
int result = Multiply.multiply(-3, 6);
assertEquals(-18, result);
}
}

Integration testing

Integration testing is a level of software testing where individual units or components of a software application are combined and tested as a group. The purpose of integration testing is to verify that the interactions and interfaces between these units function correctly and to uncover any defects that may arise from the integration process.

Here are some key aspects of integration testing:

  1. Integration of Components: In integration testing, individual units or modules of the software are combined and tested together to ensure that they work seamlessly as a unified system. This may involve integrating different layers of the software stack (e.g., presentation layer, business logic layer, data access layer) or integrating external systems and services.
  2. Identifying Interface Issues: Integration testing focuses on testing the interactions and interfaces between components to ensure that they communicate and exchange data correctly. This includes verifying that input and output parameters are passed correctly, data is transmitted and processed accurately, and error handling mechanisms function as expected.
  3. Testing Integration Scenarios: Integration testing involves testing various integration scenarios to ensure comprehensive coverage. This may include testing different combinations of components, testing edge cases and boundary conditions, and simulating real-world usage scenarios to validate the behavior of the integrated system under different conditions.
  4. Tools and Techniques: Integration testing can be performed using a combination of manual testing and automated testing techniques. Automated testing frameworks and tools are often used to automate the execution of integration tests and streamline the testing process. Mocking and stubbing techniques may also be employed to simulate the behavior of external dependencies and isolate components for testing.

Example of integration test:

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import static org.junit.Assert.assertEquals;

public class CustomerServiceIntegrationTest {

private static final String JDBC_URL = "jdbc:mysql://localhost:3306/test";
private static final String USER = "username";
private static final String PASSWORD = "password";

private DataSource dataSource;
private CustomerService customerService;

@Before
public void setUp() throws SQLException {
dataSource = createDataSource();
customerService = new CustomerService(dataSource);
createTestDatabase();
insertTestData();
}

@After
public void tearDown() throws SQLException {
dropTestDatabase();
}

@Test
public void testGetAllCustomers() throws SQLException {
List<Customer> customers = customerService.getAllCustomers();
assertEquals(2, customers.size());
assertEquals("1", customers.get(0).getId());
assertEquals("Alice", customers.get(0).getName());
assertEquals("2", customers.get(1).getId());
assertEquals("Bob", customers.get(1).getName());
}

private DataSource createDataSource() {
// Create and return a DataSource configured to connect to the test database
}

private void createTestDatabase() throws SQLException {
// Create the test database and necessary tables
}

private void insertTestData() throws SQLException {
// Insert test data into the database
}

private void dropTestDatabase() throws SQLException {
// Drop the test database
}
}

E2E testing

End-to-end (E2E) testing is a type of software testing that evaluates the entire application flow from start to finish, simulating real user scenarios. In E2E testing, the application is tested as a complete system, including all integrated components, dependencies, and external systems, to ensure that it behaves as expected from the user’s perspective.

Here are some key aspects of E2E testing:

  1. Scenario-based Testing: E2E tests are typically based on real-world usage scenarios and user workflows. Test scenarios are designed to cover the entire application flow, including user interactions, data input/output, navigation between screens/pages, and integration with external systems.
  2. Black-box Testing: E2E tests are often performed from an external perspective, treating the application as a black box. Testers interact with the application through its user interface (UI) or API endpoints, without direct access to the underlying code or implementation details.
  3. Cross-component Testing: E2E tests evaluate the interactions and interfaces between different components of the application, including frontend (UI), backend services, databases, external APIs, and third-party integrations. They ensure that all components work together seamlessly to deliver the intended functionality.
  4. Realistic Environment: E2E tests are typically executed in an environment that closely resembles the production environment, including the same infrastructure, configurations, and data. This helps ensure that the test results accurately reflect how the application will perform in production.
  5. Automation: While manual testing can be used for E2E testing, automation is often preferred for its efficiency and repeatability. E2E tests are commonly automated using testing frameworks and tools that simulate user interactions and verify expected outcomes automatically.
  6. Validation of Business Requirements: E2E tests validate that the application meets its functional and non-functional requirements, as defined by the business and user expectations. They ensure that the application behaves correctly, performs reliably, and delivers a satisfactory user experience.
  7. Detection of Integration Issues: E2E tests help identify integration issues and end-to-end flows that may not be apparent in isolation. They uncover defects related to data flow, communication between components, error handling, and boundary conditions that may arise only when the entire system is tested together.

Example of E2E test verify create :

POST http://{{host}}/api/users
Content-Type: application/json

{
"name": "John Doe",
"email": "john.doe@example.com"
}

> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 201, "Response status is not 201");
});

%}

Other tests: System testing, Acceptance testing, Regression testing, Performance testing, Security testing. Usability testing …

Testing tools for developers

Junit

JUnit is a popular open-source testing framework for Java programming language, primarily used for unit testing. It provides a simple and flexible framework for writing and running repeatable automated tests, making it easier for developers to verify the correctness of their code.

Web: https://junit.org/junit5/

AssertJ

AssertJ is a popular Java library for fluent and expressive assertions in unit tests. It provides a rich set of assertion methods that enable developers to write clear, readable, and maintainable test code. AssertJ aims to improve the readability and maintainability of test code by providing a more expressive API compared to the built-in assertion methods provided by JUnit or other testing frameworks.

Web: https://assertj.github.io/doc/

Mockito

Mockito is a popular open-source Java mocking framework used for creating and managing mock objects in unit tests. Mockito allows developers to simulate the behavior of dependencies, external systems, or objects that are difficult to test in isolation. By creating mock objects, developers can isolate the code under test and verify its interactions with its dependencies in a controlled manner.

Web: https://site.mockito.org/

H2 base

H2 is an open-source relational database management system (RDBMS) written in Java. It is designed to be lightweight, fast, and embeddable, making it suitable for use in various Java applications, including testing and development environments.

Web: http://h2database.com/

WireMock

WireMock is an open-source library for stubbing and mocking HTTP-based services. It allows developers to create mock HTTP servers that simulate the behavior of real services, making it easier to test applications that depend on external APIs or services.

Web: https://wiremock.org/

IntelliJ IDEA http client

The IntelliJ IDEA HTTP client is a powerful tool built into the IntelliJ IDEA integrated development environment (IDE) that allows developers to interact with HTTP services directly within their IDE. It provides a convenient way to send HTTP requests, inspect responses, and test API endpoints without leaving the IDE or relying on external tools.

Web: https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html

Summary

By understanding the different types of tests and utilizing the right testing tools, developers can ensure the robustness and reliability of their software applications. Whether it’s verifying individual units of code, testing interactions between components, or validating end-to-end application flows, comprehensive testing is essential for delivering high-quality software products.

--

--

Jiri Caga

Freelance Java Developer focusing on support/development backend enterprise applications in fintech area.