Enhancing The Processes Of Test Driven Development

05 Oct1:30 pm – 1:55 pmStage: DX StageTalk

Checking session availability…

Hang tight while we load the latest updates.

  • Understanding the processes of test driven development
  • Collaborating with teams in QA and development
  • Helping customers understand and communicate what they want
  • Testing and scaling to improve development

Enhancing The Processes Of Test Driven Development

Nix Crabtree at UXDX EMEA. Video: https://youtu.be/zp3knDUpoWo

Readable transcript: edited from the recording's captions for readability (fillers and false starts removed, punctuation and section headings added). Wording is the speaker's own. Timestamps are positions in the video. Names marked [?] could not be verified against the audio.

The TDD cycle in brief

[00:00:00] We are going to be talking about enhancing the processes of test-driven development, and if I can work out the clicker, we're going to do three things. We're going to have a quick run-through of the TDD cycle, which you may or may not know. Actually, let's have a little show of hands: who understands the TDD cycle? OK, good, a fair amount. Another quick show of hands: who here is a developer or software engineer? OK. And who is on the product management, product owner side? OK. And who is on the BA side or the quality assurance side? Cool.

[00:00:44] So we'll run through the TDD cycle quickly, then we're going to talk about why ATDD, acceptance test-driven development, and then we're going to go through the ATDD cycle and how that fits with TDD. I'm going to whip through the first bit, because I'd like to get on to the ATDD bit and I don't want to keep you any longer than necessary.

[00:01:04] TDD: red, green, refactor. Hopefully we all know this, from the show of hands. We write a test that exercises the smallest amount of visible behavior. The unit test fails; it's red. We know that our code base doesn't contain that functionality. We make the simplest code change possible to implement that new behavior, the smallest unit that we can make. We run the test; it's green. Brilliant. And then we might need to go back in and change the structure of the code to make it more maintainable, but not the behavior. So it's a refactor, not a rewrite, and the unit tests obviously should still pass, because we haven't changed anything other than the way we format the code.

Why ATDD: the problems it solves

[00:01:50] Let's talk a little bit about why ATDD. What is the benefit of moving from that well-known, trusted cycle to something more, something broader? It really comes down to the fact that TDD is not a testing practice. You end up with unit tests as a by-product of it. TDD is a developer-centric process whereby you specify what behavior you're going to write code for, and then write the code that fulfills that behavior.

[00:02:26] There are a number of problems that it can solve. Hopefully you'll recognize most of these; well, it's a shame that you'll recognize most of these, I guess. The flow of requirements is sometimes disconnected. From the business requirement down to what code is implemented is not always a continuous and joined-up process. Sometimes you're unable to take stories into a sprint. This is based largely around Scrum ways of working; obviously it applies in other models as well. If the requirements are poorly defined, you get to the point where you can't take it into the sprint because you're not entirely sure what it is that you need to do, or you take it into the sprint anyway and hope that you can work it out in two weeks or whatever and implement it.

[00:03:17] Rework is required when what you've implemented is not what was expected, because the requirements weren't quite clear. The acceptance criteria are of variable quality, in different formats. It might be that you're starting to get implementation details in the requirements, it might be that they're too vague, it might be that they contain a number of different technical implementations.

[00:03:43] Ultimately, collaboration between the three amigos is inconsistent. It's absolutely foundational to me that everyone talks and understands each other, not that the things passed between them are open to interpretation. And you end up with this mini-waterfall testing thing where you have your devs going, "Yay, I've written some code, now you go and test it," and then they forget, they go and do something else, and then maybe there's a problem and they have to get back into the context they were in. Ultimately it slows everything down, and you end up with an increasing gap between what you wanted and what you get.

[00:04:26] And quality assurance, quality engineering, are often second-class citizens, when in fact those people are absolutely vital in any team. They might be different people, and they might actually be the same people who are writing the code, but the mindset there is really important. It's a first-class citizen in any development team.

Shared ownership: a three-legged race

[00:04:48] We're going to talk about three things in ATDD: shared understanding, shared ownership, and a test-first approach. These are the three keystones of ATDD. What they mean is that we end up with high quality software and high performing teams. It sounds like it's a magic bullet, but it genuinely works. I've done it at more than one company, and these are the things that we see consistently.

[00:05:17] Let's race through this a little bit so we can get on to the juicy bits. Shared ownership: it's a three-legged race, it's not a relay. We all know what a three-legged race is, and did it at school. You tie one leg to someone else and you have to do it in sync. You do it as a pair; otherwise you both fall flat on your face.

[00:05:39] I'm going to talk a little bit about Scrum and agile development practices again, because it fits very well with this model. Your mileage may vary, you may have other models, but the foundation of ATDD still works. The Scrum Guide tells us that there are no titles for development team members other than developer. It tells us that there are no sub-teams. You have a test team in your development team, and you end up with that handoff and the loss of context. Individual development team members will obviously have specialized skills, things that they'd like to focus on, but the accountability for delivering a piece of high quality software is the team's. There should be no point at which someone can point at someone and say, "That was him," or her.

[00:06:33] And there are no heroes. I'm sure we've all seen the hero developer: "Stand back, I can do this, you go do something else. Hold my pint, I'm going in." Nobody likes those people. The development team is responsible. I'm going to labor this point a little bit: the development team is responsible for delivering this stuff, not the individuals in the team. And this is really important. They should feel a collective sense of pride and achievement in getting stuff done, the whole team, not just the guy who worked late and told everyone else that they don't need to worry. Everyone should feel they've contributed something to the organization.

[00:07:16] There is no dev complete. A PBI, or a piece of functionality, a feature, is done when it's proven to meet all of the acceptance criteria, not when someone says, "Yeah, I've done the code." There are no handoffs in a high-performing team. Every time you hand off you lose context; you take yourself out of the process of completing that feature. And there are no second-class citizens in a high-trust team. In a high-trust team everyone is trusted to deliver high quality software, wherever they might be, and everyone should feel that they are empowered to do that and be proud of it.

Shared understanding: one set of criteria

[00:08:03] Shared understanding. PCQ: anyone know what this is? It's a Hoare triple. You have a precondition assertion, a command, and a postcondition assertion. If the precondition is met, then executing the command establishes the postcondition. Let's put it another way, which maybe you'll all start to recognize: given the precondition is met, when we execute the command, then we have a postcondition that we are expecting.

[00:08:41] So a Hoare triple is a logical function. We provide some input, we invoke an action, and we examine the output. What does it mean? Well, isn't that what a unit test is? We specify something that we're going to pass in, we do something, and we expect a known result every time. Cool. But then isn't that also what an acceptance test is? I realize it's an overloaded term, but an acceptance test is exactly that. And if an acceptance test is that, surely that's also a functional requirement. We have a system, we're going to pass something in, we're going to do something in our customer journey, and then we're going to establish a particular condition. It's exactly the same thing.

[00:09:25] What it means is that turning functional requirements into acceptance tests, into unit tests, into working software, can and should be done from exactly the same set of criteria. Exactly the same. Everyone involved is singing from the same hymn sheet. No one gets the words wrong, no one sings out of tune. That's the plan; this is the glorious utopia.

Test-first development and acceptance criteria

[00:09:47] Test-first development. There are a few things that it's not. It's not a waste of valuable delivery time; I've heard that so many times. It's not all about code coverage at all. It's not an opportunity to start a holy war, which still to this day is the case. It's not just for the software engineers. And it's definitely not boring. I love it. I love writing software that way, because it means I know that what I'm writing is exactly what was required.

[00:10:21] What it is, is a lean approach to development. We avoid the gold plating: "I'm going to need to store something, so what I need is a repository pattern, and I need to abstract everything away." Well, maybe you will one day, but not right now. It's a way of being confident that your software meets the requirements from the very first line of code that you write, and the reason for that is that you've already written the test that says so. And it's all about understanding what you're doing and why. It's not diving into the code and trying to figure it out as you go and then going down rabbit holes. It's about knowing exactly what you need to do, being able to do it, and then moving on to something else, adding more value as you go.

[00:11:00] Acceptance criteria are foundational to all of this. They're defined and agreed with the development team and the product owner, or whatever terminology you use for that. They're written in a business-readable, domain-specific language, so everyone should be able to understand them by reading the words, not having to read the code. Gherkin is a particularly good example of this. They're written from an outside-in perspective: as a user of the system, or as a consumer of the system, I'm going to do a thing and something's going to happen. And they are definitely expectations of behavior, not implementation. Implementation comes later. You can change the whole implementation, the behavior remains the same, and you're still meeting that acceptance criterion.

The ATDD cycle: discuss, distill, develop, demo

[00:11:50] On to the juicy bits: the ATDD cycle. We had red, green, refactor in TDD. In ATDD we have discuss, distill, develop and demo. Let's go through those one by one.

[00:12:04] Discuss: the team asks questions. This is typically when your product owner, your BA, whoever is looking after that role, says, "We need to do a thing," and they say it to the development team. The development team sits with them and they have a chat. It might not be the whole development team; it might just be representatives from the software engineering specialization in the team, the quality engineering specialization, and the data engineering specialization. It might result in breaking down that feature. It might be that considering how you might do it means that there are actually two different sets of features in there, and maybe one just goes on to the backlog and you focus on the one that you're going to do right now, with the highest business priority and business value. They might add other new features to the backlog and spark ideas, and they could be things to develop in the future, or things for this particular feature. And ideally they establish a shared understanding, so everyone knows what is required at the time you're discussing this. Everyone knows it's coming down the line and what needs to be done.

[00:13:16] Then we distill that. You formalize the acceptance criteria. We're still not writing any code, we're still not writing any tests, and we're not writing any automated tests at this point. Use a format like Gherkin; again, there are other formats which you may prefer, but I like Gherkin. Use ubiquitous language, something that everybody understands but that has a specific meaning. You might use personas. You might say Jenny is a particular customer and she has a particular order history, and when you say Jenny, that ubiquitous term is something everyone understands as a more concrete object, if you like. They don't include implementation details. The implementation can change and the behavior would remain the same, in theory. And at this point it's ready for the sprint.

[00:14:08] Then we develop that by turning it into automated tests. We transform those ubiquitous terms into domain objects, so we take Jenny and we turn her into an actual customer in our tests, a customer object with a related history, or whatever it might be. We might add technical scenarios in the development team; you might add NFRs, you might add various other things, and you can do that at this stage when you start the implementation. What you end up with is a set of executable requirements. The requirements that you took from the business, in the same language, in the same terms, become executable and repeatable, so that with every change you make you are confident that it still does what you set out to do.

[00:14:52] And then you demo it, and that might not be an actual demo. It might be that you start to gamify it. You already know it meets the requirements, because your executable requirements say so. So you might do some exploratory testing. You might gamify it by handing it off to someone else in the team: "I've developed this feature, so you can break it." Or you might have two teams working side by side, and you go, "OK, well, we've done all this stuff, you go and play with it," and everyone gets involved and starts trying to break it, and passes in two billion characters into your string field, whatever else it is. And then there's your classic sprint review, where you demo it back to your product owner or your stakeholders, whatever it might be.

Where TDD fits inside ATDD

[00:15:34] The TDD cycle sits in the middle of this. When you get to your develop stage and you have a set of automated tests, or executable requirements, at that point you can start using TDD, from an outside-in perspective to start with, and then classic TDD once you've identified your collaborators, to implement the code. But you're still using exactly the same criteria. You can take those given, when, then, and turn them into your arrange, act, assert, or however you choose to do it. You can even use a BDD-style framework. At that point your automated acceptance tests are green, your unit tests are green, and it's ready to go.

[00:16:20] Everything in the TDD box there is white box testing, so your tests understand the code that you've written. But everything outside the box, the ATDD stuff, is black box testing. That comes back to the point I made earlier, which is that you can change the entire implementation, but your acceptance tests, your executable requirements, should still pass. You can take the whole thing and rewrite it from .NET into Java, or you can turn it into Go, and ultimately, if you pass in the same thing and perform an action and you get the same thing back out, it makes no difference.

[00:17:01] And then everyone understands, right from specifying the business requirements through your BAs, quality engineers and software engineers. Everyone understands what they're implementing, and everyone has the confidence that any change they make to that software will result in a high quality, bug-free release. In theory. OK, whistle-stop tour, so we have time for questions. We're going to do maybe three or four minutes of questions, but I'm going to be around for the whole afternoon, so come and grab me and have a chat. I'm always happy to talk about this stuff at great length.

Speaker

Nix Crabtree

Nix Crabtree

Lead Principal Software Engineer

ASOS