What Is Software Testing? Unit and E2E Tests
We explain the difference between unit, integration and end-to-end tests, how much of each to write and what test automation brings to a project.

Software testing is the set of checks performed to verify that a program works as expected and contains no bugs. Tests can be run by hand or automatically in code. The three most common types are unit tests, integration tests and end-to-end (E2E) tests; in a healthy project they are balanced in a pyramid.
What Is Software Testing and Why Is It Needed?
Software testing is the way to prove that the code you wrote really does what you want. A discount code being calculated correctly on an e-commerce site, a hotel system not allowing two bookings for the same room, or a form rejecting bad data are all guaranteed by tests.
In projects without tests, every new feature risks breaking something that used to work. This is called a regression. Automated tests reduce that risk; when a change is made, they show within minutes what broke.
The moment a bug is found determines its cost. A bug caught during development is fixed in a few minutes; a bug found by a customer in the live system costs money, time and trust.
Test Types: Unit, Integration, E2E
Test types are separated by the area they cover and how fast they run. Testing small pieces is fast and cheap; testing the whole system is slow but realistic.
| Type | Scope | Speed | Maintenance cost |
|---|---|---|---|
| Unit test | A single function / component | Very fast | Low |
| Integration test | Parts working together | Medium | Medium |
| E2E test | The whole user flow | Slow | High |
What is a unit test?
A unit test tests a single function or class in isolation from the outside world. The database, network or file system does not come into play; if needed, they are simulated with fake objects (mocks). It runs in milliseconds and pinpoints exactly where the bug is.
Integration test
An integration test verifies that several parts work correctly together: an application with a database, one service with another, a system with an external API. The endpoints we describe in our what is an API article are tested most at this level. These tests are critical in flows such as payment, shipping and marketplace integration.
E2E test
An E2E test (end-to-end test) uses the system like a real user: it opens the browser, adds a product to the cart, goes to the payment step and checks that the order was created. It is the most realistic test, but also the slowest and most brittle.
The Test Pyramid: How Much of Each Test?
The test pyramid is a common model describing how much of each test type to write. At the base are many fast unit tests, in the middle fewer integration tests, and at the top a small number of E2E tests covering only the critical flows.
Inverting the pyramid, that is, trying to verify everything with E2E tests, is a common mistake. In that case test time grows, tests break for no reason, and over time the team stops trusting the tests.
There is no exact ratio; the type of project decides. In calculation-heavy software unit tests weigh more, while in a project that connects to many external systems the share of integration tests grows.
An Example With Jest and Playwright
In the JavaScript world, Jest and Vitest are common choices for unit tests, and Playwright and Cypress for E2E tests. Below you can see a simple unit test written for a discount function.
If you want to catch type errors before even writing a test, see our what is TypeScript article too; a type system lightens the load on tests somewhat but does not replace them.
// indirim.js
export function indirimUygula(tutar, oran) {
if (oran < 0 || oran > 100) throw new Error('Invalid rate');
return Math.round(tutar * (100 - oran)) / 100;
}
// indirim.test.js
import { indirimUygula } from './indirim.js';
test('20 percent discount is calculated correctly', () => {
expect(indirimUygula(200, 20)).toBe(160);
});
test('invalid rate throws an error', () => {
expect(() => indirimUygula(200, 120)).toThrow();
});import { test, expect } from '@playwright/test';
test('product is added to the cart', async ({ page }) => {
await page.goto('https://ornek-magaza.test/urun/zeytinyagi');
await page.getByRole('button', { name: 'Sepete Ekle' }).click();
await expect(page.getByTestId('sepet-adet')).toHaveText('1');
});Test Automation and the CI Process
Test automation means tests run automatically on every code change rather than by hand. When code is pushed to the repository, a CI (continuous integration) pipeline runs the tests; if any test fails, the change is not released.
This setup becomes even more valuable as the team grows. Everyone is held to the same quality bar, and bugs are caught before they reach the customer.
- The developer makes the change on a branch and pushes it to the repository.
- The CI pipeline installs dependencies and runs the unit and integration tests.
- E2E tests for critical flows run in the test environment.
- If all tests pass, the code goes live after code review.
Other Types of Testing
Working correctly alone is not enough; software also needs to be fast, secure and usable. That is why there are other types of tests besides functional tests.
- Performance testing: Measures how the system behaves under load. The page speed side is also part of speed and performance management work.
- Security testing: Looks for vulnerabilities such as unauthorized access and injection.
- Usability testing: Checks whether real users can use the interface without difficulty; it is a natural part of the UI/UX design process.
- Accessibility testing: Checks suitability for screen reader and keyboard users.
- Visual regression testing: Compares screenshots to catch unexpected design shifts.
- Compiled module tests: Pieces such as the Wasm modules we describe in our what is WebAssembly article also need their own test pipeline.
Common Testing Mistakes
Writing tests is a skill; badly written tests can become a burden instead of a help. The most common mistakes are these.
- Fixating on coverage percentage: A high figure does not mean good tests; what matters is that critical behavior is tested.
- Testing the internal structure of the application: When the code is refactored, tests break for no reason. Test behavior.
- Brittle E2E tests: Use constructs that wait for an element to appear instead of fixed wait times.
- Tying tests to live data: Tests should prepare their own data and clean it up afterward.
- Disabling a failing test and forgetting it: This quickly destroys trust in the tests.
Where Should You Start Testing?
Adding hundreds of tests at once to a project with none is not realistic. A more efficient path is to start with the riskiest areas and progress gradually. Even writing one test for every bug you fix builds a serious safety net within a few months.
At the start, aim not for perfect coverage but for reliable tests. A few tests that give consistent results every time are worth more than many unstable ones.
- List the functions that deal with money, stock and user data.
- Write unit tests for those functions first.
- Add integration tests at the points that connect to external systems.
- Set up E2E tests for the two or three most critical user flows.
- Connect the tests to the CI pipeline and do not release with a red test.
What Do Tests Bring to a Business?
For a business owner, testing is an invisible but tangible investment. That is why putting critical flows under test matters when developing custom software solutions. A payment page that crashes on campaign day or a store that shows the wrong stock costs far more than testing would have.
When order, invoice and stock flows travel between several systems, the chance of errors grows. That is why building a tested, predictable structure in e-commerce integration and e-commerce platform projects lowers maintenance costs in the long run.
Frequently Asked Questions
01Do you need to write tests for a small project?
You do not need to test every line, but critical functions that deal with money or data must be tested. Starting small lets you make changes with confidence later, when the project grows.
02Is manual testing completely unnecessary?
No. Exploratory manual testing finds problems that automation did not anticipate. Repetitive checks should be automated, and the human eye should be reserved for evaluating new features and the experience.
03What is TDD?
Test-driven development (TDD) is a method where you first write a failing test and then write the code that makes it pass. It simplifies design and prevents untested code from being left behind; however, it takes a team time to get used to this discipline.
04Should I use Jest or Vitest?
Both are mature tools for unit tests and their syntax is similar. Vitest integrates more easily in Vite-based projects; in existing large projects Jest stands out for its wide adoption.
Software testing is the way to catch bugs before your customers do and to grow software with confidence. A balanced structure that builds on unit tests and secures critical flows with E2E tests is the lowest-cost path in the long run. For tested, maintainable software, explore Aurora's software solutions and integration services.


