Acceptance Test Design Principles

Jeff Langr and Tim Ottinger

Acceptance tests (ATs) are as enduring and important an artifact as your code. The proper design of each will minimize maintenance efforts. You'll recognize some familiar concepts--Kent Beck's rules for simple design and some classic OO principles apply well to the design of acceptance tests.

Abstract. A test is readable as a document describing system behavior. Uncle Bob's definition for abstraction applies equally well to tests: Amplify your test's essential elements, and bury its irrelevant details and clutter. Anyone, including non-DBA and non-programming staff, should be able to follow the steps taken in the test, and understand why it passes. Extracting duplicate behavior to a common place--the AT analog of programming's extract method--is the main workhorse that allows you to increase abstraction at the same time you remove duplication.

Bona fide. To ensure continual customer trust, a test must always truly exercise a system as close to production as possible. Passing acceptance tests tell the customer that what they asked for is complete and working. But once the customer has doubt as to what your tests exercise, you have severely damaged your ability to continue using them as contracts for completion.

Cohesive. A test expresses one goal accomplished by interacting with the system. Don't prematurely optimize by combining multiple scenarios into a single test. Keep single-goal tests simple by splitting common content into separate fixtures. Yes, this will mean your acceptance test suite runs more slowly, but it's far more important to avoid compromising clean test design. (It's also why your unit test suites should be as fast as possible.)

Decoupled. Each test stands on its own, not depending upon or being impacted by results of other tests. A failure caused by problems in another test can be difficult to decipher.

Expressive. A test is highly readable as documentation, not requiring research to understand. Name it according to the goal it achieves. As with unit tests, refactor each test to improve the ability of a third party to understand its intent. You should always eliminate magic numbers, replacing them with constants as appropriate. Improve visual accessibility by formatting your test using Arrange-Act-Assert (AAA). You should also make it clear what context is being set up in the test; one way is to incorporate additional assertions that act as preconditions.

Free of duplication. Eliminate duplication across tests (and even within the same test) before it eliminates you! Duplication increases risk and cost, particularly when changes to frequently-copied behavior ripple through dozens or more tests. Duplication also reduces the use of abstraction, making tests more dense and difficult to follow.

Green. Once a story is complete, its associated ATs must always pass. A failing AT should trigger a stop-the-line mentality. Don't allow your test suite to fall into disarray by allowing failures to be ignored!

Agile Success Factors

Jeff Langr and Tim Ottinger
Font: Brown Bag Lunch

Pop quiz, hotshot.

Q. "You're not agile if you don't ... "
A. Select one more of the following:
a.Have daily stand-up meetings
b.Pair program
c.Do TDD
d.Employ a metaphor
e.Have a ScrumMaster
f.Run iteration planning meetings
g.Use index cards

The answer is H, none of the above. Practices are just that--specific techniques a team might or might not employ to aid them in being agile--whatever agile means. Here's our definition: Agility means you are able to frequently and continually deliver high-quality software that meets the customer's needs.

The agile manifesto lays out four core values ("working software over comprehensive documentation," e.g.) and a dozen or so principles. But what factors truly make an agile team successful?

Freedom to change. Incremental change, one of the other success factors, can only occur if your teamis able to change how they work without outside interference. Meddling and micromanaging, never mind the intentions, usually divert the team from what should be everyone's goal of shipping quality software. Get the right people in place in your organization to support the team's rightful decisions, and to not try to change them. We'll be blunt: Conversely, this may mean removing the wrong people from the chain of influence.

Energized team. The successful agile team just gets it. They want to work this way. We can walk into a room and generally see whether or not a team will succeed. A good team is highly transparent and visibly enjoying what they're doing. They've built a true team spirit, and no one talks about "my code" or "my stories." They collaborate without being told; they hold their own stand-ups without a project manager having to crack the whip. They always act to protect the product and its integrity--they don't discard quality controls even when under intense pressure to deliver. They also look for ways to make life better for everyone--"How can I rework this test so that the next developer will understand my intent?" They'll step in and help anyone as needed to deliver quality product, even if it's "not their job."

While we like to think a good, energized team is all it should take, lack of freedom to change will demoralize even the best teams, to the point where your guys who "get it" choose to move on to something less oppressive.

Commo (line of communication) to customer. A product is an intricately detailed ship that must be well understood and constantly steered. The best teams we've seen have been steered by a strong, enthusiastic single-person, dedicated customer who truly understands what needs to be built. This customer has the time to ensure that everyone else can learn from them what needs to be built. While the strong customer can have a supporting cast, a large, committee-style product management team simply doesn't work. (It's always unfathomable to us that most companies are willing to staff development teams with no end of apathetic dregs, but are unwilling to pay well for strong people who know what product to build.)

Collaboration. So many teams want to be agile but can't get past cube mentality. Sometimes we think stand-ups were designed solely to compensate for cube walls, to make people feel better because they got together for a few minutes in the morning. Collaboration isn't just meetings. It's working together, and more importantly, figuring how to change workflow so that you have to work together. Due to the heavy intricacy of hundreds of thousands of lines of code interacting together as a single unit, software projects cannot be individual efforts ("you do your code, I'll do mine"). We must learn how to collaborate in the code. Really--collaborate. Work together. We mean it. Those who treat coding as an individual activity don't get it.

Attention to quality. The code is your product, and, unlike most other products, one you will continually build on and shape to meet continual new customer demand. If you fail to pay attention to quality, you will eventually slow down in your ability to meet demand, sometimes to the point of stagnation. The team must ensure that the code is clean enough to accommodate new incremental customer needs. Attention to quality is never a separate task on a plan; it must be embodied in everything you do to build your product, including coding, design, documentation, testing, automation, tracking, communication, and so on. Quality must be incrementally and continually addressed.

Incrementalism Most of the practices you employ in agile have something to do with ever-smaller steps. Instead of a massive requirements document, you allow the customer to provide features just in time, in small chunks described on little index cards. Instead of a comprehensive up-front design document, you learn how to design on a task-by-task and test-by-test basis. And so on. You must learn to think incrementally.

You must also look to correct course continually and incrementally. For every few lines of code added or changed, take time to ensure the design is as good as you can get it. (Which you can only do if you have enough controls in place to allow frequent code improvement. Best way we know how to get there: TDD.) Not only do you need to correct course in the product, you need to always correct course in your team. You should always be introspecting about your team, probing for ways it's not performing optimally, and working to correct these problems. Retrospectives are a good start.

A successful agile project is not a bunch of hare-like sprints to the finish line. It is a cool-headed, tortoise-like, slow 'n' steady approach of small, well-reasoned steps. Each step is an opportunity to look up and see where it got you--closer to the finish line or further away? It's easy to correct a single misstep. In contrast, a single mad sprint in the wrong direction can take you pretty far off course from the finish line.

Automation. There are numerous ways to waste people's time on a software development effort--running automatable regression tests manually, for example, or suffering a build process that unnecessarily requires multiple manual steps or manual verification. Agile cannot work unless you automate as many menial, tedious, and error-prone tasks as possible. There's simply not enough time across a two-week period to get any real work done if you have to slow down for numerous manual gating processes.

Information Radiators

Tim Ottinger and Jeff Langr
Font: Brown Bag Lunch

In keeping with the agile value of Communication, agile teams often place large charts and graphs in their workspaces to radiate important information such as defect rates, rate of completion, and measures of code goodness (CRC, bugs, test counts). Much is made of Informative Workspaces or Big Visible Charts (BVCs).

You'll find numerous graph types in the agile literature. Some types, such as burn-up, burn-down, and cumulative flow are commonly used to graph progress against a goal such as an epic story. Defects and velocity are also useful things to track, but don't limit yourself. Let your team determine--and let these information radiator principles guide--what you track and publicize.
  • Simple
    A chart or graph should not require minutes or hours of study. Ensure it is brief and concise. Anything dense or complicated will fail to communicate. Also, don't use highly derivative, biased, weighted information. Simple facts speak more profoundly than clever algorithms.
  • Stark
    BVCs don't exist to convince the public of your team's excellence. Nor should you mask errors or problems with them. The purpose of the graphs is to display progress and expose problems. Don't subvert the honesty of the graphs, otherwise your team will stop trusting them, and therefore stop using them to improve their work.
  • Current
    Any information that is not kept current is quickly ignored. You may want to have the CI build graphs based on automatically-generated stats. Information more than a few days old is too stale to have evocative powers.
  • Transient
    Radiators that only expose problems shouldn't be up there long, otherwise it's clear that you aren't solving your problems. Choose a BVC to highlight a current challenge for which you can demonstrably show improvement over the next several days or iterations. Once it's clear the team has gotten the message, and things are back on track, take down the BVC.
  • Influential
    A good information radiator influences the team's daily work. It may also influence managers, customers, developers, or other stakeholders. Ideally it will empower the whole team to make better decisions, otherwise it is not worth preparing and presenting.
  • Highly visible
    An effective information radiator needs to not only have information on it, but must transfer it to team members, stakeholders, and passers-by.
  • Minimal in number
    Having too many graphs or charts will cause all of the charts to lose evocative power. Also, exposing too many problems on the wall at one time can demoralize any team, so choose either the most important, timely, or fixable problem to highlight, and defer the rest. Sometimes less truly is more.

Development Iteration

Jeff Langr and Tim Ottinger
Font: Brown Bag Lunch

These half-dozen items provide the core set of principles around the iteration workflow for delivering stories to an eager customer. (Most of the principles would also apply to a kanban environment, with a bit of tweaking.)

Team commits to stories they can complete. Each iteration begins with a short planning session. The primary goal of this session is to ensure that the development team (hereafter "the team" in this blog post) and customer are in sync on what the team will deliver by iteration end. The customer and team jointly produce acceptance criteria that act as the contract for completion. The team commits to only the set of stories they can confidently deliver by iteration end. The workload is thus fixed for the duration of the iteration, although the customer can negotiate a change to the workload with the team. Hopefully this occurs only rarely, and hopefully the team has the sense to insist on work being taken off the iteration's plate.

Team works stories from prioritized card wall. The customer is responsible for managing the flow of work. Their job is to ensure that available stories are presented in priority order. Available developers grab the card with highest priority and move it into an "in progress" bucket. The card wall, whether real or virtual, is visible to everyone involved in the project.

Team minimizes stories in process. Applying non-collaborative approaches to agile asks for the same, lame results you got from waterfall. A typical anti-pattern we've seen (we'll call it "Indivigile"): Every story is close to a full iteration in size, and each developer grabs their "own" story to work on. So much for any of the significant benefits that you might have gained from collaboration (primarily better solutions, increased mindshare, and thus minimized risk). Worse, you dramatically increase the probability that stories will not be delivered by iteration end. A better approach: Team members work on the smallest sensible number of stories at one time, maximizing collaboration and ensuring that each story is truly done before moving on to the next.
Following this approach, you should see almost half of the committed stories 100% complete by midway through the iteration. Overall, the number of stories fully complete by iteration end should increase. Also, many activities that would have otherwise been blocked until iteration end can start earlier (additional testing, clarification/correction, documentation, etc.).

Team collaborates daily. A daily stand-up meeting is a good start for getting the team on the same page, but never sufficient. If the stories-in-process are few, the team must find ways to collaborate frequently throughout each day, to avoid wasting additional time.

Customer accepts only completed stories. Stories must pass all programmer and acceptance tests before the customer looks at the the software. The customer needs to play hard-ball at this point: Any story shy of 100% passing gets the team zero credit: Incomplete work provides no business value. The lesson for the team to learn (and apply in subsequent iterations) is to not over-commit.

Team reflects on iteration and commits to improvements. Agile is built around frequent incremental delivery, in order to maximize feedback, which in turn provides opportunities to improve. Iterations are not only opportunity points to improve the product, but also for the team to reflect on what the team needs to change, whether to improve quality, throughput (which improving quality will do), team morale, or any other execution concerns that the team recognizes. See our Retrospectives card for guidance on this critical agile practice.

Everything else that happens with respect to iteration execution is "implementation details," and thus up to your team to determine. We don't care if you use software tools or physical card walls. We don't care if your team is distributed, whether you run a formal stand-up meeting, whether you use velocity, planning poker, load factors, yesterday's weather, Scrum Master whip cracking, or any other specific practices for planning and organization. All is generally good as long as you adhere to the above principles for flowing work.