How to Test Software - Part 1: Functional Testing
Testing your software is a continuous process. No matter how much testing you do, you can bet that your product will break at some point because when you’re not testing it yourself, your customers are creating scenarios you never even imagined.
While trying to catch every bug is futile, you should be doing everything you can to check your products. This two-part article covers the basics of software testing. From the various types to how they’re done.
We can break testing down into two categories:
Functional - Tests to check that the base functionality of the software works as specified.
Non-functional - Tests that evaluate the software’s performance and usability.
In the first part of this blog, we will be dealing with functional testing.
Unit Testing
This is the most basic of tests. A unit (or component) is the smallest testable part of software. A unit test validates whether a component works as it’s designed to. Given a set of inputs, a unit should produce a predictable set of outputs. It should also be able to handle failure or misuse with grace.
Automation is standard for this type of testing. Many languages have unit testing frameworks to quick-start your testing productivity. Developers should perform unit testing as they’re building the software. They should repeat them anytime they change the source code.
Being diligent with unit testing ensures you catch bugs early and save time later on.
Example
We have an e-commerce system. When a user sees an item in the store they’re interested in, they can click on it. This takes them to the product page, where they can see the item’s details and price.
Integration Testing
A piece of software is never a single component. Integration tests check that components still function as expected once they’re combined. They tend to focus on how data flows between different software modules.
There are three main approaches to Integration Testing:
Big Bang: All components are tested as a whole. Whilst this method is efficient early on, it can prove time-consuming to localise any faults you find.
Top-Down: Following a system’s architectural structure, testing starts at the highest level of the application (e.g., the user interface). It then moves down to the subsystems. This provides a logical environment to test in, but it also means that basic functionality is tested last.
Bottom-up: The opposite of top-down. Testing starts at the bottom of the control flow. This approach is useful as it follows the direction of development. However, it can be easy for interface issues to arise late in the testing cycle.
Which method you choose comes down to a variety of factors. Whether that’s how much time and budget you have, the complexity of the system, or how important its use is.
Example
Our system has a shopping basket page where users can store items they intend to purchase. If they click the ‘Add to Basket’ button on the product page, the item should be added to, stored, and displayed in the user’s basket. This represents the integration of a product page and the shopping basket.
End-to-End Testing
Once everything is working together, it’s time to start putting the software through its paces. An end-to-end test replicates how a real user might use the application in its complete state. It tests the application flow from start to end under a real-world scenario.
End-to-end testing ensures a user can go through the system and utilise all its different components. It tests that a user can achieve the ultimate point of the software. Whether that’s buying a t-shirt or performing a salary review. It also confirms that accurate information is communicated throughout the system and verifies all the necessary dependencies.
You can automate this type of testing using various tools. However, it’s worth remembering that it should replicate a real-world scenario. Humans don’t work like programmed bots who click everything like a perfect use case. So, it’s worth doing manual testing to ensure you capture more typical user behaviour.
Example
A user goes through the entire journey of buying a product - from logging in, browsing the store, and adding an item to the basket to going through the checkout, communicating that order to the store, and logging out.
User Acceptance Testing
This is the part where all your previous testing should pay off. User Acceptance Testing (UAT) is where the client or end-user tests the software. They do this by comparing the software’s functionality to the actual business requirements. In doing so, they make sure that it is fit for purpose. It is generally the last stage of testing before the system goes live.
Much like End-to-End testing. The client will focus on tests that validate the entire business flow. This casts the widest net to catch bugs and inadequacies. Clients will also compare the system to the specifications to make sure that it has been delivered as promised.
Smoke Testing
Congratulations. You've completed all your pre-launch testing, and the site is live - but that doesn’t mean the testing journey is over. A system is a ‘living document’. It will receive changes, upgrades, and fixes throughout its life cycle. All these will need testing, too.
A smoke test is the first test run after a system update. It involves checking that the main functionality of a system is still working.
The reason for this is two-fold. Firstly, for minor updates, it’s used as a marker for whether more in-depth testing is necessary. Secondly, if you already plan to do more testing, it ensures that no high-level issues will hamper progress.
Smoke tests are easy to automate because they are routine and straightforward. Running this type of test often is industry best practice and one of the most cost-effective ways of catching issues. The next step is to run a regression test.
Regression Testing
A regression test consists of re-running previous tests after a system receives changes. This could be anything from a few components to the entire system. The tests confirm that the new changes have not caused issues with functionality that was already working.
The good news is that this type of testing doesn’t usually require creating any new tests. Testers can run through the scripts that have been previously used. The bad news is that regression testing can be a massive undertaking, especially if done manually. A fundamental database change can call for a re-test of your entire system. If the system is large and complex, this can take a significant amount of time. However, it is completely necessary.
In Closing
Due to the complexity of software, it must be tested in as many ways as possible. From the low-level unit tests to the large-scale end-to-end tests, make sure the entire system works as a whole.
That’s why software developers should make testing a fundamental part of their process. Implementation should start from the very beginning and run for the product’s entire lifetime. That’s the only way to ensure reliable software.
Stay tuned for part 2, where we’ll be discussing non-functional testing.