Two team members working on their laptops, while a third manages a star rating and checklist.
Coauthored by John Marlett and Chris Hand
The best software solutions enable a user to accomplish the task they set out to do practically without noticing the software product. The more you have to think about what you’re doing when using software, the less you are thinking about solving hard problems, pushing the boundaries of creativity, or simply completing your routine tasks efficiently. The most popular software products reliably solve a problem with a foundation of quality that delights users. Think of your favorite mobile apps, computer software, or websites. When you use these, you’re not worried about whether you’ll find another bug or be unable to perform basic tasks. You perform your task seamlessly, as the application itself remains in the background enabling your workflow without intruding into your conscious thought process.
At Softrams, we build quality into our most basic development processes, knowing that a good human experience with software cannot be achieved unless it’s foundational to how systems are built. Each product team discusses quality at the inception of feature requirements, allowing all members of the team to understand and have confidence in the solution they are building. how they will have confidence they are building a good solution. Testing is considered part of the Definition of Done for features and stories, and the team doesn’t consider them completed and delivered until they implement not only the desired functionality but also proper testing to guarantee code quality.
Types of Testing
In software there are many levels of testing that a team can use to build confidence that what they build is correct and will fulfill a user’s needs. Below are some of the many different types of testing you may hear about in software development.
Unit tests, which are highly localized to validate that a small piece (a unit, such as a function or small cluster or functions) of code will behave properly when it is run.
Integration tests are like unit tests, but will validate that multiple units, parts of a system, or entire systems will work correctly together under certain circumstances.
End to end tests test a piece of functionality, or a system from a user’s perspective. These tools usually involve some program that interacts with a UI and lend tremendous value by being able to test every part of a system similar to how a user would.
User acceptance testing is often a final step in software development where end users will use the system being developed to provide feedback before launch.
Testing Code with Code
Let’s examine two of the examples above, unit tests and integration tests. These are both examples of software developers writing code that tests their code.
A common pitfall for developers is to receive requirements and start writing code specifically meant for the inputs and outputs discussed during product discovery without considering larger implementation impacts. This can lead to duplicate code and repository bloat. Conversely, the mental exercise of writing unit tests alongside functional code can help developers think more creatively about how the system, initiated by a user, will interact with the solution they’ve created. This results in a better, more robust solution ultimately giving a better experience for the user.
Additionally, testing the features being written encourages developers to think more creatively about the code itself. Examples of this are unit tests and integration tests. In the most basic sense both a unit test and integration test are code that developers write to test their code. For instance, if a developer writes some code to add two numbers together, a series of unit tests may validate that if you pass the code certain values, it will return the correct sum. Very often if a function is overextended and confusing, the exercise of figuring out what it does and why in order to test it will give the developer key insight into how to split the function up into smaller ones with clear uses. For many teams, the greatest value from developer led unit testing is found in productivity and code quality gains for the developers themselves. Well tested code tends to be higher quality and better documented, making it easier to maintain. Additionally, when developers extend heavily tested code, they can make isolated changes and include relevant tests with the confidence that existing functionality is stable if the tests pass. Integration tests are similar, but test multiple parts of a system interacting together, which can lend a tremendous amount of value in catching bugs early in the product development process.
These types of tests can be written and run very quickly, which means that you are able to change existing functionality and verify it in a short amount of time. Those times when a developer runs a test to find that something broke can save an entire team time in investigation, troubleshooting, and fixing code in the future, making even these simple tests a development time power up that contribute to higher confidence across the team in delivery estimates and functional stability.
End to End Testing
While it’s extremely valuable to increase confidence in e code written by developers via unit and integration testing, the end user’s experience is the final goalpost in producing a superior product. End to end testing gives a team or customer ultimate confidence that a user is able to complete the desired workflow by running through the flows themselves and validating the results
There are many kinds of end-to-end tests, but here we will focus on UI end to end tests, which interact with the user interface of an application and are meant to replicate user workflows and validate certain steps that work the way you expect. These tests typically require tooling and frameworks to help interact with an application, which have advanced dramatically over the past decade. At Softrams, we prefer tools that allow us to write and run tests mirroring expected user workflows as closely as possible. A primitive script for a test may look something like this:
Test Scenario: Validate Monthly Report Shows Expected Values
- User logs in
- Navigate to reports page
- Access monthly report
- Run report for August 2021,
- Validate total sales revenue equals $21,000
I can then schedule these tests to be run at arbitrary intervals, such as nightly, or when I make changes to the system. In doing so, I can confidently enhance this feature, develop new ones, or upgrade dependent services knowing that existing functionality is being tested reliably and consistently, and if a bug is introduced, or a dependency is broken it will be caught quickly.
While it’s obvious this type of testing can be extremely valuable, they can also have a high cost of development and maintenance. Because of this, it’s advisable to be very targeted with the tests you write in this manner. Focus on the core workflows of your application. It is usually not worthwhile to write end to end tests for flows or even whole products that are in a state of flux.
These types of tests don’t replace the need for manual testing, but significantly reduce the effort needed to have a high degree of confidence that your product will continue allowing users to perform their needed functions.
Lastly, in writing these tests, the entire team can understand how users will interact with the system. This, in turn, will allow the team to build better solutions and think creatively about how to constantly improve the experience offered.
User Acceptance Testing
As helpful as all the above types of tests are (and we strongly encourage teams to adopt writing all of them in different scenarios) having users interacting with your application directly will always be more valuable than the tests you write yourself. A user acceptance test allows your end users to interact with functionality before they are officially released. There are many benefits to a user acceptance test that we would recommend taking advantage of.
- They allow you to get direct feedback from your users.
- You will get the opportunity to understand how a user will interact with the application, validating or challenging the assumptions made while building it.
- Bugs or failures in workflows will be discovered in a safe environment, before getting to a production environment.
Just to name a few. Be sure to give yourself plenty of time to incorporate feedback from these tests into your product! Ideally, this is not the first time your users have seen the application they will be interacting with. Make sure you have validated your development with the use of wireframes and mockups well before you start developing, so this stage of the process is validating what you already know, as opposed to discovering new needs.
Building Quality into Process
In order to realize the benefits of writing tests that verify a small section, or your entire system works correctly, your team has to understand and buy into the value of each type of test. An engineer who is forced to write unit tests just to meet certain coverage guidelines will do just that and find little to no improvement in having written them. Similarly, integration or end to end tests that are written just for the sake of appearances will still add value but fall short of delivering a massively better experience for end users.
To start building these types of tests into your own process, start by focusing on quality at every step of the product life cycle. Ensure your teams understand the need to have confidence in what they build. Push your team to deliver a better product with every iteration, and don’t settle for testing the system manually to be the only answer to ensuring quality.
For those teams that want to move faster without breaking things, having quality built into each step of the process will allow you to ship exponentially faster without the worry of breaking your system.
Building quality into your development processes will produce a better product and encourage your entire team to think differently about how to deliver value to end users. There are many ways you can build confidence that a product being built will not only fulfill a need and delight a user but will do so without several iterations of finding and fixing bugs. Take advantage of some industry accepted practices to build this into your own process!