Rafhael Marsigli Logo
Menu
Contact Me

The Beginner's Guide to Software Testing

6 min read
The Beginner's Guide to Software Testing

The Beginner's Guide to Software Testing

Every application that exists needs to be tested before going to production. That's much better than pressing the deploy button and having Slack start playing the fifth symphony of chaos a few moments later. Customer X can't complete task Y, service Z is down... Of course, you can't predict the future, but you can get pretty close.

Writing tests is a boring, repetitive task, but it's incredibly necessary and important. That little game that simulates the World Cup to show off to your friends probably won't need any tests at all, but if there's a paying customer at the end of the equation, there must be tests: they're the digital certificate of safety and quality for your project.

Learn a bit about the history of testing below, or jump straight to what each type is for. This is an introductory article about testing. In a future post, I can talk about performance testing, load testing, security testing, usability testing, regression testing, acceptance testing, CI/CD, test coverage, and other topics!

Who Are They, Where Do They Live, What Do They Eat?

We're not going to talk about the 1950s–1970s. Back then, projects were practically built directly on hardware, and testing was almost unthinkable. But in the 1990s, Kent Beck created SUnit (which eventually became the unit tests we know today). For the first time in history, developers had a standard way to write code that could test other code. This gave rise to TDD (Test-Driven Development), which introduced the idea that testing is not the end of the process, but part of it — a component of the architecture itself.

After the 2000s, as hardware and software capabilities continued to advance and scale, running manual tests for large projects became unsustainable. That's when the concept of the Testing Pyramid emerged, defining the order and importance of each testing layer.

Today, what we have is called Shift Left: a testing pipeline where checks happen as early as possible, including static analysis, compilation, and more.

All Types of Tests

Static Analysis

This is the first layer. Before running any tests at all, static analysis performs a complete inspection of your code, looking for logical flaws and contract violations. JavaScript has ESLint, TypeScript has the TypeScript compiler, PHP has PHPStan, Go has go vet and staticcheck, and Rust has its own compiler with the famous Borrow Checker.

Unit Tests

By far the most widely used type of test. With unit tests, you validate the smallest logical unit in isolation. That unit might be, for example, a function or a class. There is no contact with external APIs or production systems. Instead, you use mock data — fake information used to validate business rules.

Shipping calculations, discount rules, and service behavior are examples of validations that are extremely fast and well suited to unit tests.

Fun Fact: PHP has PHPUnit and Pest, while JS/TS has Vitest and Jest. Go and Rust, on the other hand, treat testing as a first-class citizen. No external framework is required, as both languages include a complete testing structure out of the box.

Integration Tests

Unlike unit tests, integration tests communicate with real databases, queues, caches, or file systems. The goal here is to verify, for example, whether a repository correctly saves data to PostgreSQL, or whether a message successfully reaches RabbitMQ.

"Wait, does that mean they'll test directly against my production project?" Relax. No. The most common approach is to run tests inside an isolated disposable Docker container (often known as a TestContainer), where seeds are generated, or within a dedicated testing environment.

End-to-End (E2E) Tests

At this layer, the goal is to simulate a real user. E2E tests execute 30, 40, 50, or even more different operations and validate critical user flows. For example, you can simulate a customer visiting a website and performing multiple actions, including logging in, adding products to a shopping cart, entering credit card information, calculating shipping costs, and so on.

Remember that I mentioned shipping calculations as a unit test example. Here it's different: an E2E test simulates the user accessing the page and calculating shipping directly through the user interface. These are my favorite tests, and I'm very grateful for the AI era because now I mostly just need to verify that they were written correctly and are working properly 🙏.

Smoke Tests

After deployment, a verification is performed to ensure everything is functioning correctly. A simple script might ping a few routes and check whether they return HTTP 200 responses. If a route returns a 500 error, an automatic rollback can be triggered while the development team investigates what happened.

Fun Fact: The term "smoke test" comes from a famous hardware engineering joke that goes something like: "You plugged it in... did any smoke come out?"

These tests are designed to be simple and fast. Even if they don't seem impressive, they provide an important extra layer of verification because they validate how the project behaves on the real production hardware.

Mutation Tests

Okay, not many people use these. I've never used them myself! But knowing about them never hurts. Mutation testing is the internal affairs department of software testing. When someone writes on the wall, "Who tests the tests?", mutation testing shows up and loudly replies: "I DO!"

To verify whether tests are truly effective and that coverage isn't giving a false sense of security, mutation testing deliberately introduces bugs into the codebase, such as replacing a simple + with a - or inverting a boolean. After causing this intentional damage, it runs the test suite and expects failures. If everything still passes, a major red alert goes off: the tests are blind and need urgent review.

It Depends on the Situation

Writing tests does increase development time, but it dramatically reduces headaches later (trust me, skipping tests is practically asking for trouble). Want to avoid hotfixes, rework, and production issues affecting real people? Only testing can minimize those risks.

Yes, tests can be boring, but they're absolutely crucial.

Tags

Share with those you love