a11y and TDD: A Perfect Match


Accessibility has been web development's ugly duckling for quite some time now. I often get asked, "when should you test for a11y in your apps?" My answer is simple, "right from the start!". Regardless of the framework considered - React, Svelte, Vue, YourOwn™️ - as developers we are in a privileged position to help the ugly duckling grow into a beautiful swan. How? By diving deep into the pond and harnessing the power of Javascript APIs to build the right components for your web apps. And how can do you know you are building them right? By pairing Test Driven Development with the Testing Library family. Ready to grow your web apps into swans?



Okay, so, thank you very much. Welcome to this talk, and let's get started. So my name is Rita. I'm a geek at heart. I really love comic books. I have a son named Pedro. He's three and a half years old. I recently, since we got back from the pandemic, I started cycling, and when I go to work, I usually do so by bike. In my free time, I like to game, which is nice. I work in the SDC with these amazing people, which are the ones that are kind of making noise, and the SDC is the software development centre for the Volkswagen Group in Lisbon. This is our building. It is really pretty. If you're ever in Lisbon and you want to visit, please do drop by. So, for today, accessibility. It has been talked about in the previous talks, and there's a big reason for it. So when you think accessibility, you usually think about people with disabilities, or something that can be accessed by people with disabilities, or something that is adapted for people with these set disabilities. However, that's not all of it. The first definition that you find in the dictionary for accessibility is something that is capable of being understood or appreciated and reached, which is big. Let that sink in. Test-driven development. What is it? In a nutshell, it's the ability to convert software requirements into test cases before you do any line of production code. It is quite simple, and just to get a feel of the room, how many of you have done test-driven development? Okay, cool. Then I will fast-forward. But for those of you who haven't, test-driven development is... No, sorry. So, given accessibility, given test-driven development, how exactly do these two match, and why exactly is it so important to combine the two of them? I hope that by the end of this, you will have a better idea of it. Let's come back to accessibility, and a funny thing that I discovered. So between the A and the Y, there are 11 letters, and that's why it's usually referred to as LE or A11Y. I kind of didn't know that, but once I did, it became a fun fact. For us, developers, accessibility is the power to enable as many people as possible to use the software that we use, to empower people to use the things that we built. We don't want to build things to be put in a shelf. But it's not only for people who are in a wheelchair, who have some crutches, who have a wheel cart for the baby. Accessibility is for all of us, including us, especially us. How many of you have issues with the keyboard? If we can spend all of our time just using the keyboard, that's pretty cool, and accessibility gives us this. Coming back to the test-driven development. So, my first contact with TDD, it was through this book. It is done for Java. It is written with Java examples. It's quite comprehensive. It's super, super well structured. And it gives us the basis for what is TDD. So, in a nutshell, it is composed of that merry-go-round that I showed you. The first part is the red cycle, or the red part of the cycle. You write a test, you make it fail. You implement just enough production code to make the test pass. If needed, you refactor both the test and your code. Sometimes things get done sort of right at first, and there's no need to refactor. Sometimes there is a need to refactor. Be mindful of that. And with the cycle of TDD, you will be able to have always a safety net on the changes that you do to your code. But for sure, you can say, okay, but that's very easy when you're doing back-end development. What about front-end development? I don't know exactly how things work with front-end. How do I test things? How do I need to click on things? Fine. Think about TDD not only as a methodology for software development, think about it as a way of applying things. For example, a potato. If you want to peel a potato, and if we want to create a user story that goes along in the lines of the user wants to be able to peel a potato, if you want to go straight away into peeling the potato, you're going to start thinking about things like, am I going to do it with a knife? Am I going to do it with what type of a knife? Should I use a potato peeler? Should I use a potato peeler that is one of those that you do like this, and the seesaw is like this, or is it like this? It doesn't really matter. Should you boil the potato and then peel it off? Because the outside gets off really easy like this. We should peel it from the top to the bottom or from bottom to top. It doesn't really matter. Why doesn't it matter? Because at the end of the day, what you want to do is to have peeled the potato. So you can kind of convert this into a test. So you follow the 3A pattern, you arrange, act, and assert on your object. You get a potato. You peel it. It is peeled. It's very simple. You focus your way of cooking in this case, and you will not get lost in the details. You will go straight to the point and do just what is needed to peel the potato. So this is very fine with the potato, but what if we take a real-life example? Imagine that you have a form, and imagine that within, let me find the mouse, which is here. Imagine that within this form, you are asked to fill in some personal data. You have found the string that says enter your name. You start filling it up. You do tap, tap, tap for populating the form. Will you go? Will you not go? Yeah, okay. We will go. Oops. Wrong way. This is not the way we want to go. Will we go to the hangout day? Yes, for sure. How? By boat. Okay. If we go by boat, all of the form is filled. Okay. Now, where's the button? Where do I need to click? I need to click this button that says something. Cool. The flow is done. I've just told you a story. This is the user story that we will have to fill out this form. How can we do this? We can write a test and cover this journey of this user, which will be us. So everything that I've told you, it's mapped out to this pseudocode or to these comments. So let's get started. How exactly will we go from a form that, or in this case, some designs or some visual interfaces to a full-blown project? To a project that has... It doesn't. I think it should have a... It does. How do we go from a form that has... From designs to a product, a project that has the source folder that is organized with small stuff, incremental stuff, that has tests, that will eventually have an end-to-end to back up the user journey? How can we organize things? We can do so by applying a design pattern that is atomic design. And you can be fooled by the name, where it says atomic design, so it must be targeted for designers. It is. But we can leverage the power that atomic design provides and apply the same pattern in the software that we built. So when we are building the software, we organize our components by complexity. We have the simplest elements as possible, HTML text that will go as atoms. You start combining them to form molecules. You start combining molecules together to form organisms, and then you have the last pair in the ladder, which are the templates and the pages. You can kind of think of pages as instantiated templates. I'm not going to go into detail on the atomic design part, because on its own, it's a huge topic to go on and about. But I just want to give you the seed and plant you the idea of it is possible to organize our code in a very atomic way, in a very clean and easy way for on-boarding new people. Let's get started with something. The text input molecule. From the design, the text input molecule is something very simple. It has the text, your name, and it has an input box. Okay, let's write a test for that. The test for that will be something that it has the title and it has the input. We will be using web components for the demonstration. We will populate or generate an HTML with the said web component, and within the HTML, we will expect to find a paragraph, kind of, with the text that we want, and with an input. We run the test, and when we run the test, it's on the red side, because, of course, there is no production code to back up the test that we just did. So let's extend. Let's write just the right amount of code to make our test pass, which is just this. Nothing else, nothing more. And the test passes. This is a very crude implementation. This is very hard-coded, so we want to give it some flexibility. If we want to give it some flexibility, and while we are updating the test, let's also give it some ability to control the values that we are putting there. So, the title, it's kind of useful that people can reuse the molecule, so let's have it as a custom title, and let's have the control of the input. Fine. The test fails because there is nothing there. Fine. Let's put it there. We put it there. It's there. The test passes. We're good. We can continue. So we've just added two more attributes for the text input. But as you know, there are lots and lots of stuff that can go into an input. Do we want to build them all? Do we want to start, I don't know, with each and every one of them? No. There is no need to do so. With test-driven development, you get to do just what is needed to make your use case pass. You do not more, and not less. We've got molecules. The text input. So let's start putting some stuff together and see how we can progress with the form. Let's have a form. Let's write a test for a form in which we can ask for a first name, we can ask for a last name, and, since it is a form, we will have a button to send the information. When we run the test, or when we write just enough production code to make it pass, this seems decent. We have two molecules. One says first, one says last. There's the button there. Fine. Move along. Not move along, because each of the text inputs is going to render 1p. So the test will not be able to distinguish between the first text input molecule and the second text input molecule. So this kind of sucks. But we know there are two of them, so we can make it work. We can say it's an array. The first element of the array is the first name, the second element of the array is the last name. It's cool. We got this. We totally got this. However, if by any chance in the country that you're rolling out the form for, people usually use the first name before and the last name before and the first name afterwards, you are kind of bummed. Your test is going to fail. But the contents of the form, the way the form is built, it still works. It shouldn't have failed. So how could we fix this? We can probably, if we are sleek enough, we can go to the form, we can try to find the elements. We will right click, inspect, we will look for what the hell is that one? Okay, I got the ID, so I can go and I can tweak the code and that's it. And I have the test and it's all good. Please bear with me. It's going on again. The only people who do this are us, the nerds. Users don't do this. Users will look for something. Users will look for the text that is written in the page that is being displayed in front of them. So let's make a shift in the way we do our tests. Let's have our tests resemble the way the software is being used. Because when we do so, it will give us more confidence in the way the software, in the tests that we are doing. This is not my quote, this is Ken C. Dodd's quote for the testing library. And from now on, all the test development will follow the testing library. How does that translate into the test that we had? So instead of having just the P, let's start with the label text. We will have a label for last name, we will have a label for first name. We will also give a news, the advantage of the button being a specific HTML element. And we'll click it and that's pretty much it. A small note about labels. People tend to mock me because I tend to put labels on everything that I do. Why is it? Because it is a small piece of paper that is attached to an object and it gives information about it. And it stands for computers, it stands for cables, it stands for anything. In particular, for web development, a label in an HTML element will represent a caption for an item that is on the document. So you will be able to access it. You will be able to reach it within your DOM. So let's start typing on the forms that we have. When we do so, we can increase the complexity of the test that we just built. So we will also be able to give the semantic meaning of having a form. Because I didn't have a form before. I just had loose elements spread out in the document. So let's have a form within the document. Let's have the first name. Let's click it. Let's type things on it. Let's use and mimic the interactions that the user will do with the page. But this gives us a problem, which is the input molecule, the text input molecule, it was not prepared to receive labels. It was not prepared to receive more information than the one we already gave it. So we'll need to come back, update the text input molecule. And always having in mind accessibility. So all of the queries that you're doing, all of the tests that you're writing, you're doing so thinking about how will I be able to access this? How will I be able to reach it? The testing library is very helpful when it comes to telling us what is wrong with our tests. Let's just fix it. Nothing more, nothing less. We fix it. It looks happy and content with it. And all the tests pass. So we were able to do not only some refactor, but also some extension of the code that we already had. It's good. But we already have that form with the first name and last name and then the button. But if you remember from the form, it will request more information about personal data. So maybe it is refactoring time. It should be fine. Let's create the personal details organism. And while we're doing so, let's give it even more meaning. Let's say that it is the first name, the last name, and the email. This is personal information. So all of these inputs, they can be grouped within a field set. They can be given semantic context. They can be put in the same bag so that you can reach it in an easy way. You run the tests, and, oh, sorry. You will then refactor the code. You will run the tests, and everything will still pass. So you've done a huge refactor. You've been able to improve the accessibility of your code. And nothing went wrong. So the cycle of the test-driven development, it will give you security while you are doing or while you are developing your app, thinking about accessibility first. For the end of it, you will eventually continue and develop the rest of the organisms and the molecules. You will develop the checkbox, the input, the radio buttons. You will take care of building up the drop-down, selecting it, et cetera. So once you put everything together, you can go up one layer. Instead of writing or hard-coding the HTML that you're doing, you can actually build it, bundle it, ship it, and then do some end-to-end tests on it. Always from the point of view of the user. So if you do run the thing with Cypress, you run the test. A browser will eventually open, a browser of your choosing, you get to configure it. The form, if you have more than one test, which is typically a use flow or a use case for the user, it will run. Each and every scenario that you have, you will be able to transform into a test that will get executed each time you ship your code. So you will have security in what you're doing. You will have confidence that the code that you're building, you are doing it using accessibility first, and you are doing it using just what is needed to make it work. So to conclude, you have accessibility. You have test-driven development. It is a perfect match. Because you can transform your user stories into tests that will drive the implementation of websites and also the tests themselves that are capable of being understood and appreciated by all of us. So that was it. Thank you very much. Thank you. Great job. Thanks. So let's jump into the Q&A. The first question is from Anonymous. Very good. How would you maintain the labels in the test? Doing it dynamically would kill what the user looks for, basically. Doing it dynamically. So now you hard-coded strings to the labels. Maybe you have them in a constants file and use them in the component itself and import them also in the test. That's what Anonymous means, I think. Could it be that Anonymous is called Metin? No. Okay. Who knows? Okay. That kind of looks a little bit like if you were to have translations and that you need to be able to use something that is not hard-coded. In those cases, you kind of have to abstract what you're putting in and what you're feeding to your components. So add another level of, I don't want to say complexity, but add another level of abstraction to your code and have that be the one that's responsible for taking care of the dynamically changing thing. Maybe overwrite in the test, overwrite the import. Exactly. It's hard-coded and it's mocked and eventually it's mocked. For instance, if it is a translation, you want to be sure that the translation has been called. Something simple that allows you to be sure on what you're doing. It just needs to be constant, right? So first name could just be string one, last name can be string two. For instance. Yeah. All right. Thanks, Anonymous. And what are your thoughts about using test IDs instead of labels? As a user, do you need test IDs or do you need them as a programmer? As a user, I would say you never need any. Do you develop code for programmers or do you develop code for users? Well, I actually make code for developers. Okay, fair enough. But in your scenario, you're right. You always have to think about who will be the people that will be using the software that you're developing. If that person needs a test ID, that person needs a test ID. But ask why the person needs the test ID, not just because. Yeah, and it's like you showed with Kenzie Dodds' tweet. His quote was just try to test as much as possible like the user is using your application and try to write your test as if you're a user. And if you're a user, you're looking for that label and you're clicking that so that it will focus your input. Exactly. Exactly. All right. Well, that's the time we have, Rita. So thanks a lot. Thank you. Anyone has any more questions for Rita? Rita is going to go to the speaker's booth now. And on Slido, you can also ask your questions. Everyone, a big round of applause for Rita.
24 min
16 Jun, 2022

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Workshops on related topic