To mock or not to mock, that is the question. Whether 'tis nobler for the code of the programmers to engage with spies and stubs in outrageous tests, or to take the real components against a sea of timeouts, and enduring, to validate their code: to commit, to push.
To Mock or Not to Mock - That's the Question
AI Generated Video Summary
This Talk discusses the SDC's approach to software development using agile methodologies and extreme programming. It highlights the benefits of pair programming and the use of atomic design in React components. The importance of test-driven development and the React testing library is emphasized, along with the implementation of code, navigation, and form validation using Formik and Yup. The talk also touches on the abstraction layers in software development and the testing of user journeys and accessibility in the BookKeeper app.
1. Introduction to SDC and Agile Development
In this talk, we will try to answer the question of whether to mock or not to mock. I am Rita, a geek at heart, living in Lisbon and working at the SDC. The SDC is a software development center established in 2018. We develop products using extreme programming, an agile framework based on communication, simplicity, feedback, courage, and respect. We work in balanced teams, including designers, product managers, and developers. We do test-driven development.
♪ Hi, welcome to this talk. In it, we will try to answer a question, which is, to mock or not to mock? Let's get started.
My name is Rita. I am a geek at heart. I live in Lisbon with my beautiful family. My kid is almost three years old, so he's a bit of a fool plate. But whenever I'm not busying around my family, I'll probably be playing, or I will be at the SDC, the place where I work. I work with these amazing people. And at the moment, I'm part of Team Falcon, and things are slowly starting to get back to normal here at the office.
Where is the office? So, it's a software development center here in Lisbon, and it is right in the middle of the city. It was established in 2018. It was the first software development center that Volkswagen opened outside of Germany. And so far, things have been going very, very smoothly.
What do we do here? So, we develop products using extreme programming. In a nutshell, it is an agile framework that allows us to produce and to deliver high-quality software at a very fast pace with a good quality of life for us, the developers, or for the whole development team. It was created by Kent Beck in 1996. So, it's somewhat old, but the core values that it is based on, they still stand. So, communication, simplicity, feedback, courage, and respect. These five pillars, they are also engraved within the SDC spirit. So, it is a perfect match.
What else can I tell you about the SDC? So, we work in balanced teams, which means that a team is composed of designers, product managers, and us, developers. A team, let me put also some stress in this, a product team, because the team is bigger than just the product teams. The designers, what do they do? So, they keep tabs with the users. They explore, they do the research, they investigate possible avenues to solve the users' problems. The product managers, they will translate these user needs into stories, and they will also keep tabs with the business and see if a product is or isn't viable to be developed by the business point of view. And us, the developers, we will take care of the feasibility in terms of technology. We will be the ones responsible for seeing if something can be integrated with legacy systems, if something can be done using cutting edge technology. If it is technology, that's our realm. In the middle of these three very different roles, that's actually where the magic happens and where good products come to be.
So, what do we do and how do we do it? So, us developers, we do test-driven development.
2. Pair Programming Benefits
In pair programming, two developers work together on the same code simultaneously. This practice allows for real-time code review and immediate problem-solving, eliminating roadblocks and fostering efficient collaboration.
Which means first we write some tests, we run the tests, we're expecting to see the tests fail. But the tests pass, the tests pass. No, they shouldn't be passing. Maybe there was a bug in the test, most likely. So, to minimize this type of situations, we also engage in pair programming. Which means that you always get two sets of eyes looking at the same code. We got one computer, two keyboards, two mice, two screens, and that's how we develop. There is no need for code reviews because the code is being reviewed live as you go. You don't get roadblocked because if you do get roadblocked there will be someone there with you. You will be able to express your thoughts, engage in a conversation very quick and efficiently, you'll be able to unblock the train of thought that you have. So it works really well, I have to admit. And I couldn't see myself doing it any other way around.
3. Books and Technological Solutions
I'm going to talk to you a bit about books. Books can take you anywhere and tell you stories that you'll never be able to see in real life. We have a library at the SDC, and it's time to bring it to the 21st century. Let's get a technological solution for the bookkeeper and do a couple of sketches to visualize our goals.
So, coming to the core of the presentation. I'm going to talk to you a bit about books. Books, why? Well, books can take you anywhere. They can take you to places and tell you stories that you'll never be able to see in real life. So, we also have a little bit of a library here at the SDC. It is actually two bookshelves with books, but it's a library. So we can get books out of it. Therefore, it has management system. What is it composed of? It's a scratch pad. It's a scratch pad in which we write our name, the book we took, and when we return it, we scratch it back. So, yeah, a little bit. So let's bring this to the 21st century, shall we? What if we dug food on our own methodologies? So instead of having the scratch pad, let's try to get a technological solution for the bookkeeper. So let's talk to the peeps that request and develop the books and deliver back the books to the peeps that have to maintain the library. And let's do a couple of sketches. So this could be what visually we would try to achieve and accomplish. It was a starting point.
4. MVP, Atomic Design, and Coding
So let's get an MVP, a proper MVP. We need to add books, borrow books, and return books to the library. We'll use atomic design to organize our React components, starting with atoms, molecules, organisms, and templates. We'll deviate from the original methodology by allowing organisms to have templates. We'll start coding with a blank canvas and aim to achieve test-driven development by developing the page to add a new book and going through the user flow of adding a book to the library.
So let's get an MVP, a proper MVP. And out of the research that we did, the valuable things and the things that posed the most relevant problems to the users were to be able to add books to the library, to be able to borrow books from the library, and to return books naturally. So we can add books. There will be a page for them for such. However, since people tend to be a little bit itchy-trigger on the keyboard, let's also have some validation just to make sure that everything is correct before submitting. Let's have a separate page in which we can see what are the books that are available in the library. Let's have the possibility to actually borrow a book. Um, let's be able to see which books are out there in the world. And let's have the possibility of returning them back to the library.
Okay, these in a very quick overview, these are the sketches that we have. So, now what? How do we go from these sketches to React components that will... Obviously, we will develop this using React. So, how do we go from the sketches to react to our React components? Well, enter atomic design. Atomic design is a way of organizing your components and your visual elements as a design system, most likely. And it is structured in a very straightforward and growing way or increasing in complexity way. You got atoms, you got molecules, when atoms are the simplest ones, molecules are groups of atoms bound together, organisms are groups of molecules and atoms bound together, templates will be canvas where you say, okay, I wanna have this here, this there, this right down here, and then pages when you instantiate the templates. This was how Brad Frost defined the atomic design back in 2013. However, we did some tweaks and some adjustments for our own use. So, we stick to atoms, yes, fine. From our designs, atoms will also be buttons, something very simple. Molecules, a group of atoms bound together or more than just one HTML tag put together. Organisms, such as a book card or a template for the pages for the bookshelves for example. You do get a template and when you instantiate such template, when you do instantiate the template, you will end up with the pages for the bookshelves. However, this is the moment where we broke rank with the original methodology because we said, okay, but organisms can also have templates, which is pretty cool, and as such, we have our deviation.
Okay, so let's get things started and start coding. Where? Okay, you will be able to have the repo that I've used for this presentation available here, and it starts off with something very simple, a blank canvas with a header, a main content, and then the footer. What do we wanna achieve out of this? Okay, we will be doing test-driven development. We will be developing the page to add a new book, and we will be going through the user flow of adding a new book to the library. All good. Let's get started.
5. Test-driven Development and UI Improvement
Test-driven development is crucial, and we choose to use the React testing library to write tests that resemble the way our software is used. Starting with the new book page, we render the components and aim to have the form with all the elements, including a disabled save button. Initially, the tests fail due to missing production code. By correcting spelling errors and using regex, we ensure the tests pass. However, the UI still needs improvement. To address this, we write additional tests and tweak the React testing library to mock and call the necessary components.
Test-driven development, which means we'll have to write tests. To write tests and having the user at the center of it all, we have been choosing to use the React testing library because, as Kenti Dodds said in a very elegant way, the more your tests resemble the way your software is used, the more confidence they give you. And this is in fact something that we resonate with and that we think is a good way of going through the test-driven development.
So we'll be using the React testing library for this. Let's get started with the new book page. What do we have here? Okay, we have the form that will contain the save button disabled. We will render just the components, or in this case, the page, to have the better off. It will be the component. The component to have the form for writing a new book with all of the elements, the header, the different input sections, the buttons, and ideally, to have the button disabled. Simple test.
Of course, it fails because there is nothing to be, there is nothing. The component is clean. We haven't done any of the production code. So let's get to it. When you add some of the production code, you rerun the tests and your tests are still failing. Why are they failing? Because we were a little bit permissive of the test and then the person who implemented the code was a little bit late with the typing. So instead of having author properly spelled, we were somewhat creative. Why? Because the test was using regex, so it allowed us to be a little loose with the definition of the string. If you fix it for the correct spelling, your test gets to pass. Okay, cool. But we're front-end developers, so we always like to see how things are.
And this is how things look, which isn't brilliant, is it? So let's try and fix it. How can we fix it? We can write another test. And instead of using the React testing library as it is meant to be used, let's tweak it a little bit. You remember the input molecule? Let's say that we wanna call it. Let's mock it and let's say that we wanna call it with the parameters that we're expecting to see. Let's do the same for the OK and Cancel buttons as a molecule and let's go for it. Yeah, we know. Yeah, obviously it's not called. So let's call it.
6. Implementing Code, Navigation, and Formic with YUP
When you do implement the code, your test gets to pass. Easy. It's mission accomplished. And as a bonus, when you get to see how it looks, ta-da. Yes, this is what we want. We're getting somewhere. Furthermore, when we click on the Cancel button, research showed we would like to be taken back to the homepage. So for that, let's use the React Router DOM so that we can do navigation between our pages. I've showed you two different ways of going on and about for this simple component, this simple form. Pros and cons. When you have Forms, my strong advice is for you to go with Formic with the combination of Formic and YUP for its validation. It is better tested, it is amazing, I have to say, and it is somewhat easy to get acquaintance with. So let's get down to business with Formic and YUP.
When you do implement the code, your test gets to pass. Easy. It's mission accomplished. And as a bonus, when you get to see how it looks, ta-da. Yes, this is what we want. We're getting somewhere.
Furthermore, when we click on the Cancel button, research showed we would like to be taken back to the homepage. So for that, let's use the React Router DOM so that we can do navigation between our pages. So let's expect the history push to have been called. It really hasn't has it, so, yeah, we know it hasn't been called, so let's call it. When we do call it, both know when we call it on the callback for the Uncancel, our test gets green, and all good. Super, easy. Very easy.
I've showed you two different ways of going on and about for this simple component, this simple form. Pros and cons. When you do mock the components, you get an easy going from the designs to the components that you're using, which is cool. You don't get to cheat on the components that you use, because if you wanna use our button, you're gonna use our button, you're not gonna use the HTML Tag for Button. When you render your component, you get to do so in a controlled environment, so you get to mimic the navigation back and forward or wherever you wanna go with a lot of... Or the other way around, with not that much trouble. However, the downside of it is that you do have some shallow rendering going on, which I know, it's totally against what the React Testing Library stands for, but then again, pros and cons. Eventually, you might end up with some duplicate tests here and there. Which is okay, and you have a tight coupling with the implementation details. We said we wanna use the React Router DOM. That's what we're gonna use to do navigation. Pros and cons. There's no right or wrong here.
Moving on. When you have Forms, if this is new to you, when you have Forms, my strong advice is for you to go with Formic with the combination of Formic and YUP for its validation. It is better tested, it is amazing, I have to say, and it is somewhat easy to get acquaintance with. So let's get down to business with Formic and YUP.
7. Formic Testing and Service Documentation
On the tests, we mock Formic and expect it to be called. However, we decide to use Formic as it is, without mocking. We focus on the part when we're going to submit and test the service to add a book, which calls the slash-API slash books endpoint with the given payload.
On the tests, how do you make sure that you're using Formic? So you mock it, and you expect it to have been called. Easy. It's not called, yeah, obviously it's not. Of course it's not because we just introduced it. We do call it like this, and the test passes. But if only it was this simple. So maybe we should go and read a little bit more about Formic. So, on Formic you get the basic example, and out of it you can pretty much spot that we need to give more information to Formic, so the initial values and what we wanna do when we submit, and speaking of submit, we will want to do so. But the button is disabled. We wanna click it. How do we click it, how do we enable the buttons? Okay, furthermore digging, and we discovered that there is this valid property that goes along with the dirty, and then we'll have to see how we can use it in the forms, but pause. What do we wanna do? Do we wanna spend time learning how to mock this third party library? Or do you wanna spend time using it? The answer is fairly simple, which means let's talk about it. We need to decide what we wanna do. And as a team, we have decided that we wanna use Formic as it is, so no mocking. We go for it fair and square, and we do so. So, instead of having all of the forms, we just focus on the part when we're going to submit. So, when we're going to submit, we are going to have a service to add the book, and we will be expecting it to be called, but it's not, obviously. So, if we go to the form that we have, and that is already populated with more of the Formic stuff, we get to call the add book. And we've got a green test passing. So, easy peasy. No biggie. But what is this? What is this service? This service is documented through the tests. So, it's another advantage of test-driven development. The tests are your documentation. They will be your reference for the things and for the information that you need to extract. So, the service to add book is merely a service that calls the slash-API slash books endpoint with the given payload. Just that, not much. So, there you go. It calls the fetch that is from a fetch service that in turn is something that we have in the bookkeeper that is encapsulating the fetch API. So, it's a couple of layers going on here.
8. Layers and Abstraction in Software Development
But Trek told us that layers are good. Layers are cool. Why do we have layers? When you have layers in this case, you get a lot of abstraction. You're able to ignore the specific libraries or APIs you're using and focus on the fetch service. Mocking the service makes testing easier, but you may need to dig to understand the services and their locations.
But Trek told us that layers are good. Layers are cool. Why do we have layers? Procon. When you do have layers in this case, you get a lot of abstraction here. So, you're able to ignore if you're using the fetch API, axios, node fetch, a library that you've built on your own. We don't really care. It's hidden away, tucked under the fetch service that is used throughout the entire application. And you're good for it. And you're also good for mocking it. So instead of trying to mock all of the places where you use the library, you can just mock the call to the service. It makes it a lot easier to test. However, you will end up with some digging that you'll need to do if you wanna understand what are the services and where they are.
9. Testing User Journey and Accessibility
Now we can focus on an entire journey for Bob using the BookKeeper app. Render the entire app without mocking, except for Global Fetch. By focusing on the user journey, you can test the React components and navigation. This approach allows you to simulate an end-to-end test without the need for a running backend. You can also prioritize accessibility and test the application from the user's perspective.
So to wrap up, now we can focus on an entire journey for Bob. So Bob wants to use the BookKeeper. So he wants to use the BookKeeper, which is the name of our app, if you haven't noticed. So you render the entire app, all of it. No mocking whatsoever, except Global Fetch, which is the uttermost part of your app, when you do mock this uttermost part. You get to be very, very focused on the user journey. Take it with far and square through the React testing library flows, and of course, the test will fail because test-driven development. Remember, we started with a blank canvas on the BookKeeper. So, let's add stuff. Let's add the navigation with the React RouterDOM, switching between the homepage, the page to add new books, and eventually the page to borrow books. Things would pass. Pros and cons in this approach. You can probably say that, yeah, that looks a lot like an end-to-end test, and we could be doing this using Cypress instead of the React testing library within the front-end development. Yeah, you could. But if you do it like this, you actually get to focus on the user journey, which is something that you could also do in the end-to-end. Fair. But in this case, you don't have to worry about having a backend up and running. You just need to be mindful of what are the endpoints, and what is the interface that your backend is providing you. Then you can simply go ahead and mock the responses that you want to have. You can also focus on accessibility. You can test your whole application through the eyes of the user, how he perceives your application, and that is something that is really valuable. The cons of this part... I really didn't find any, so kind of, sorry.