Software development is an inherently complex process. With each line of code, software grows more complex, harder to change and prone to defects. Test-driven development, or TDD, was introduced as a way to reduce these defects by writing a test first, watching it fail, writing a minimal amount of code to make it pass and then simplifying it.
Test-driven development has evolved over time to include unit tests, integration tests, user interface (UI) tests and many others. There are several strategies for writing and managing these tests, but the idea of a testing pyramid has emerged as one of the most popular concepts among Agile development teams in recent years.
Let’s take a look at testing pyramids, ice cream cone anti-patterns and how behavior-driven development (BDD) can help round out these tests to ensure that features are actually delivering their intended benefits.
Software testing is complex, but test pyramids and behavior-driven developments cut through the chaos.
Most software companies use manual testing at the start of a project. For example, a startup working on a minimum viable product (MVP) may avoid writing unit tests since there’s so much potential for change. After all, writing a test for a feature that might be removed tomorrow may seem like a waste of time.
Manual testing often evolves into automated UI tests. Using tools like TestComplete, developers can easily test desktop, mobile and web application user interfaces to confirm that there are no errors and the desired result appears. Many developers then transition to using integration testing, and ultimately, unit testing (the gold standard).
The problem with this approach to software testing is that it’s not scalable. As the number of features increase, the amount of manual testing labor exponentially grows since each feature may interact with numerous other features. Automated UI tests can also be very brittle since even minor changes to the user interface can cause a test to fail.
Writing a unit test from the start is often seen as the superior approach. They can be augmented with integration tests later on with GUI testing being used only on an as-needed basis. This approach ensures rock-solid reliability on the method level and still has higher level of integration testing to make sure everything works together.
These two approaches are often referred to as the testing pyramid (the right approach) and the testing ice cream cone (the anti-pattern), as seen in the illustration above. The idea is that starting with unit testing creates a more reliable base and more scalable testing infrastructure compared to starting with manual GUI tests.
There’s no doubt that testing pyramids are a significant improvement over testing ice cream cones, but they are often misinterpreted to mean writing all possible unit tests before moving to integration tests and then UI testing. This layer-by-layer approach may work for some teams, but for others, it’s needlessly costly and complex.
The testing pyramid is meant to suggest that there should be fewer integration tests than unit tests, fewer automated GUI tests than integration tests, and fewer manual GUI tests than automated GUI tests. The reason is that unit tests are fast and cheap whereas UI testing is slow and expensive. It’s not meant to convey the order of writing tests.
Martin Fowler, a thought leader in the world of software testing, argues that high-level tests serve as a second line of test defense. If you get a failure, you could have a bug in your functional code and/or have a missing or incorrect unit test. The unit tests then ensure that the bugs stay dead and should serve as the first line of defense.
Behavior-driven development starts with high-level tests that provide more context into the way that an application should function. By starting at a high level, business and technical teams can be sure they’re on the same page before writing any code. Lower level tests can then be written as they would in a traditional TDD process.
For example, suppose that a product manager comes up with a feature expressed as a user story with acceptance criteria. If the developer misinterprets the acceptance criteria, they could spend a week building the feature using traditional TDD practices by writing unit and integration tests. But even with those tests, the final result would need to be rewritten.
Behavior-driven development helps avoid these problems. The product manager and developer meet to discuss the user story and come up with concrete examples of the story in action. These concrete examples are converted into executable specifications that help ensure that everyone is on the same page.
HipTest simplifies BDD even further by introducing a web interface for creating scenarios and providing tools to automate the creation of step definitions (executable specifications). While these scenarios are being written and executed, HipTest automatically generates a living documentation that can help keep business team members up-to-speed.
Software development is an inherently complex process — and there are no simple one-size-fits-all solutions. While test pyramids have become the standard approach for Agile teams, it’s important for business and technical teams to understand the rationale behind pyramids and know how to use them properly.
Behavior-driven development addresses many of the shortcomings of TDD and testing pyramids, such as the need for better communication for requirements. By incorporating scenarios and executable specifications into your automated testing frameworks, you can be sure that your application is actually meeting business requirements.
Sign up for a free trial of HipTest and discover how BDD can improve your team’s communication and generate living documentation to keep everyone in the organization up-to-date. If you’re interested in learning more about BDD and how HipTest helps, you can also schedule a free demo to learn about the benefits one-on-one.