CCTDD: Cypress Component Test Driven Design


The first part of the talk will focus on a variety of patterns when using Cypress Component Testing and TDD to create React components. The code samples will be from Angular's Tour of Heroes to make the content relatable to a wider community. Currently I am working on a GitBook rewriting it in React using Cypress Component Tests, there should be plenty of content to distill into the talk.

The second part of the talk will focus on test strategies, types of testing and where to apply them, and finally compare & contrast them to the familiar industry approaches.

We will finish with key takeaways, sample application repos to help with learning, and rollout strategies.


Hi everyone. My name is burat. I'm the staff engineer and test architect that extent and I believe Cypress component test driven design can take our front end engineer to the next level. We'll talk about a tdd example go through a component test. We'll talk further about component testing a second example. We'll cover N2 testing and in the context of tdd and finally even wrap up with becoming the best practices. The application on the test is true of years from angular. You're all familiar with it. It's Rewritten and react and the book Cypress component test driven designed to be able to find the link to the book right here and the final app with all the source code everything in this presentation over here at this link. The react application with a Json server some nice tooling we have many Cypress component test examples here. Each one of them has a react testing Library mirror. Yeah, you are integration test stubbing the network you have API at land and UI and twin test proportion of them is one to five to 15, which gives you an idea about the test architecture. Here's our first component. You can find it in Chapter 3 of the book and final code right here in this link. Start with a placeholder test first you want to make sure that you can render something important to the right file. You have the files. This is a good Baseline start. You just copy paste this to every starting component test run. That you have it that being rendered nice. Right the failing test because what did you have Paul finding tests for things that matter? So we want to link. It has an attribute that goes to react And we have our first tailor great one little test failing and we try to make that work adding link with an HF. Once I was something that failing or do the minimum to get it to work and they want to try to make it better. Want to use component test as the design tool to Aid your red green defactor Cycles. We are seeing anything being granted. So we think okay. There's an idea let's make sure that this is visible. So we had a failing test now we have a red. Incremental Vision enhancements mean you can use the visual feedback. That's not up your expectation as the red indicator. So I'm not seeing it. I want something in there. I'm going to go and add an icon. So I see a decent vendor something being visible doesn't really help. I want to see the right thing. The obvious what hard thing to do with CDD is Right. Very small incremental test at a time. Want to build the component It's relatively using tests. You want to find out more about it with the test like your ad hacking. But as you add hacking you go ahead and add a step. So we have an attribute we add a task for it. And then yeah that actually good check be write another step of test. And then we add another attribute to make that work. At the end we think okay. Maybe we need an icon right in this component. We add that data size selector to the top tag of the component, which I become it makes the component very easy to find when it's used as a child or when it's used in end point tests. So we do that and they under that component we're expecting LSG to render because that's the icon we're looking for. If you have typescript and eslint, it's kind of tooling can also Aid your red green effect or Cycles here. The property two is missing Now link is from the act after dump. So we don't really have to have a failing test for it. But and so we can just add that attribute right there. In order to make the test work on top of that we have to wrap it with browsers out there because that's how you mount your component when you're using the accruther down. We just If they always remember a component test is a small scale application. So you have to replicate your application refers you're using both router in the main app and you'll be using browser router whenever you're mounting your component. But your application may be looking like this and your test mounts will be looking like the one on the left. No problem. You can write a custom draft function. You'll be able to find the link here and you'll be writing one in the book as well. And it's just a one-time thing. If some of your components don't need to be wrapped with some of these that's perfectly fine. Extra wrappers. Do not really change how the component behaves the component just uses whatever it needs. Is the exact same thing you would do with react testing Library. You can also write multiple different kinds of. Custom wrappers right some of the rappers for these components but different rapper for another set of components. Actually just want to add a link has three spells on River some strings. So right at paying test for it. And then we get our failure. Because we don't even have that nav link data size selector. So we go ahead and add that. Yeah some classes to make the render look nice and our stance each one of them just has a hardcoded text. And we have a nice passing test at the end. Then there's the way we wanted to. The question comes up. Should we break down the tests or not? On the left and right is the same exact test in the same thing left side breaks it down right side just as one flow. What matters is the beginning state of a test if reaching this state is common and in my opinion? the right side an opportunity for a test enhancement is stronger. I prefer the example on the right so long as that's all duplicate though. They're ordered independent and stateless either side is fine. Reason unit testers May preferred one. The left is because they want to have a smaller blast radius. This is not a problem. You have a Cyprus test Runner. It's very easy to find exactly what they have failure happened. You just have to be careful not to do the thing on the left. So here we have three it's blocks. They overlap with each other. So the first most common in the second one. And third one covers the second one. So this is just one test. You can just write one test most of the third one with the visibility check from the first test. And that's good enough. An example of what not to do. And what to do? Here's the comparison to react testing Library 101 is the same thing. You can translate each line. The intent is always the same. The difference is the API style. So if I think awaits variable assignments on one side and you have a chain syntax a little more flow on the right side. But the intent your actions whatever you doing is the same just different ways of doing it. So that was I'll walk through of a tdd example with component test. Now we'll have a second example and some more pointers have a component testing especially about props rappers of components and pair child relationships. So this example from chapter 5 of the book and here's the final code. You can use hard coding initially or you can use console log to make your chest pass. Just hardcoding. Everything's fine there. Then you manipulate your component either with props or rappers. So here we have a prop instead of a hardcoding test. We're adding that prop right here the placeholder. And now we add it to the component. We can pass data with props here. We have a prop for Heroes. We're passing an MP array and then here we're passing a hero's drink. on the right side On the top, we're not passing anything just the component by itself. But if you wanted to we could have a rapper there versus context API. And thereby provide a value there the valence array and that's how we manipulate our components. So either you use a property or whatever. That's how you customize your component test and Cyprus. And yes, and anywhere else. When you're adding props, you had the property right here. We adding the name property and then you add the prop to the component types you add it to the arguments of the component and you use it in the component. Same flow will be going through it. This time we're adding the value property. A test failing with that, right? And then add prop the argument and use it in the test itself. Let's do the component and we have a nice render at some point which will be using for a form. The form we want to it to have the ability to be a read-only also, so you write a failing test for that or then at the prop. And if you have events, we can just stop them here. They have an event that's two key strokes. So we want the change event to be called twice. First the family test and then we add the prop the arguments and again we use it in the component. I see failures through tests and when you're green tests want to prefer adding more tests already factoring versus adding additional source code. This is very important. Sometimes they are passing test and then we go and add so my source code no need to do that little bit of a test. Make it work make it better. If you have something working want to prefer adding more tests. You want to have a failure first or effecting before you go to the source code? Here's a parent child example. So this is a child being used at the parents two different varieties a name and a description. Always check if you duplicating the test effort coverage elsewhere. So you want to find Opportunities to cover different functionalities? On the right. We already have a check that we're typing something and then that thing is visible but duplicating tests that test over here at the parent and that's not good. But at the parent you can do something else you can check that depends renders two variants of the child. Here's another advice with jsx and tablet literals. You can be pretty creative about your selectors. I like the other side at the top tag of the component and if there any variance I just exploit jsx and template literals. And it's pretty fun. React testing Library versus Cyprus components as I think for this one. The main difference was the how we do the unchanged event Test. On the right side is I get the change tag right here and then checking the call count and on the left side suggest FN instead of a size thumb and we check that. It's been called so many times. Doing the exact same thing every step translates slight different style. We went through tdd example component example two of two component test examples some best practices over there now, let's look at tdd. With Antoine tests. So Antoine tests are great when you're finished building your components. Now, it's time for routing State management application flows component State components talking to each other all the back end that's time for E3. But remember you want to HSS at the lowest level and then move up when you cannot test confidently. This is an example of that. I can't do anything about drafting when I'm working with components, right? I need entrance. So first you look at the child, can I do this at the child? No, you move out to the parent if that's not enough. You have a UI integration test. You stop out the network, but your components are talking to each other and maybe you need to back in maybe a component is making a back and cold or something. Maybe you can't use that one test then. Housing feature with tdd example. So this is an N1 test we're using tdd. It's from chapter 13, and there's a final code of this test. You always want to remember what you're doing at your child component. So here we're visiting the component. We visiting the application and the network test and we're making sure that that about component is this point? So whatever how do we modify our application so that the header buyer component is displaying we go ahead and take a look at the child the header bar and there we're using browsers out there and wrapping they had the bar. So our application is going to also have to do that. So you can cheat and always look at the components as that you already built. They'll make your life easier when you're writing your entry test or when you're writing other components. You'll be having the selectors already there data size selectors and you can always refer back. It's documentation for you. What do I do with this thing? The tdd flow is exactly the same with it. We want to start something failing. I want to do the minimum to get it to work. So just minimum add the Nail Bar component and then if you can make it better to refactoring Once things are working prefer to add more failures. You want to have a not found Route and that's the display are not found component right first the failing test and then we add that not found component. To the rod configuration and we're finding out more about the application through testing in this way. If we have passing tests, you want to add tests or refactor before adding more source code we have that passing test. Yeah that additional check for location. We're still green. So, okay, then we can think of another failure. I want to seek these failures failures are great when you're doing TTD want to keep adding test until there's a failure. So it's very good. Once you had that feeling test then you can add the new feature. So here we're visiting the heroes path and we want Heroes component of a display. That's not failure to make things work. We just add that feature. You say okay at the heroes out. You should be rendering that certain component and that's a tdd example using Antoine test for routing. Let's go to recommended best practices. There are a few of these so we'll go through each. With API Android test don't do naive UI in Twin testing before you have confidence that you're back and works want to use an API test client instead of using your UI client as the test client. This way you can test earlier in your deployments. You want to be aware of the test results of your backend and you want to avoid duplication? I use modest amounts of e2e and do a targeted carefully when you have UI enter. You don't want to have to repeat this UI end contest if you already have confidence in one area first, let's login works. Then try not to do the same login in every test. Maybe you can go to the back door or have another way of logging it. Well logic doesn't work only one test is going to fail and you know, it's not working not everything has to fail. Here's your API into a test strategy for credit. So equation create and delete update create update and delete and to be able to delete you have to create something but look at this update covers every single flow as you test update creation and delete already covered. So just test update. This is the backend example. You can find it in chapter 15 and the final code in this link. We are adding our hero the first request make sure that thing got added. Make an assertion then we go ahead and edit the update it. And then we make sure that whatever we updated did get updated the way we wanted it to then we make the removal and then make sure that it's removed from the database. good example of our API Antoine test and you want to have this where the back end code lists closer there if possible. UI antoin on the other side is very similar to API and one but instead you can use the API commands that you already created and isolate the UI test for instance. If you want to test update you can see the database but the API command that we already wrote and do your UI testing and then at the end you can delete We'll go through the simple creation example, but you get the idea. So we first doing UI create and then we'll clean up with the API. Here's the test. We'll go through also the UI integration test. The first ones are canceling and refreshing. And they're not really using the real Network. The final one is going ahead and adding a hero and after adding it. It's making sure that hero is on the list. You can find the full test over here. In chapter 17 we go through this in the Via query chapter. And here's the final test. In the beginning, we just navigate to that hero. We want everything to settle. We are using the real network. No stopping here. We are at the right location. We do the UI creation. So if you fill out the details hit the save button. and then we make our assertions that we're at the right path a hero that we added this displaying there. And then finally we do the API. Delete. Thank you. So before the thing to watch out for is each one of these steps can be lots of development. Right. So with API and 20 testing where you're doing tdd, you don't want to go even smaller because one line of N2 test can cover lots of source code. This is the main idea from here keep and twice even smaller. when you're doing tdd What is your integration test? So I won't always evaluate if you need your back end to gain confidence in your apps functionality. Only use real Advantage when you have to have this confidence. And you shouldn't have to repeat that cost the same and twice as everywhere. ER where Of what your backend is already doing? and that makes your UI into what concerns even less. So be careful not to duplicate try to get. It you can judge people by what they don't do by the cost of the things that they don't delve into. This is especially True For Engineers. Hope I need you. Let's convert a uin test to IUI integration test. So we have two tests here one of them testing refresh the other one testing cancel. Using the real Network. We're just waiting for the network the settled before we go ahead and check that refresh button as the refresh or the cancel button does the cancel the other have an end point test that's adding a hero and checking that the list is getting updated. So we don't really need this get request for real, right? It can be just update. So we use a Json file you say anytime you hit that route instead of the real thing use the file and there is your data to be able to test the fresh confidently or cancel. You don't need the backend the perfect use case for UI integration test. When you have errors you want to test your error cases? Any test that's covering the positive flow or side intercept is a good branching spot. I want to try to test out the component level and if you cannot test something a negative every case then are you there? You want to move up to UI integration test? So here we are. This is our positive scenario hit the save button and we expect a post request is going out. There's the perfect spot to do an error test case and not 200k. So we start with a 400 you also add a delay. And make sure that there's a spin it at first and then we get the air this form chapter 18 when we cover suspense air boundary and concurrency. So this test myself just covers use case or suspense and error boundary for this particular component. Want to avoid testing implementation details you want to lean more towards black, but testing while testing your component. And this is the same idea from react testing Library, which is using Mac service worker to do the same thing that we're doing with Cyprus intercept. So on the left side, we have implementation detail test testing that you state is being called or react query is being called or our post our post hook. Our custom Hook is being called when we click the save button. Instead you want to test the consequences of clicking the save button at the end a network request should be going out to the backend. and it's better if you check this because If you change your implementation, this test is still going to work doesn't really matter how you do the internals. Box service worker and Cyprus intercept are very comparable. Here's my comparison one-on-one. You can check the links in my opinion Cypress intercept can make things a little more succinct a little more flexible. But you do your own research and let me know what you think. You want to take advantage of the visual feedback of the component test? So here in chapter 8 navbar? I'm writing the book and I thought this is a good test. But when I start the component, I see that every single link is highlighted. Okay, I click on each I go to the right link fine. But there's a defect. Which means visually when you see the component this quality of the feedback improves your tdd inspires you to check for more tests and let you detect these effects before they may even happen you get a feedback from the CLI tool, but you have to be very careful. For me it was way easier to see the visual thing. I think. Okay, when I just click on something only that guy should be highlighted and the rest should be not highlighted. So I added a test for that. Best part is ad hoc is possible, you know components so you can just run the component test and then your component is there by itself without reading any of the rest of the application you can go and click around look at devtools to get your extensions. It's just next level. experience for a developer when they're working on a component in isolation Just mind-blowing this technology. Here's the final test for navbar RTL versus Cypress component test again different styles same intent and Cyprus, maybe shorter, sometimes let's go because of All the other packages versus jQuery that it comes with so it's easier to unselect or get the ones that are not selected. versus the other API and react testing Library Is the same way of doing the same thing? If you cannot use visual tools, then you want to do tdd. My advice is use wallaby. I'd go for this tool when I'm testing it back end, especially but if you cannot use visual tools Too hard to migrate check out wall of JS. We'll give you inline feedback as you're writing your test. And that's the Pinnacle of tdd, which you can have. Promised Eli or ID tool if you can't have visuals have that at least. You can use combined coverage to make sure your tdd was successful. So we go through that example in the appendix in the book. We get coverage from Cyprus component tests. It's coverage from Cyprus N20 tests, and we get coverage from chest and react testing Library test. This means you don't really have to migrate from the accessing library to Cypress component testing. You can already have tests. With just the old test maybe or the non-component S. Maybe you're testing your reducers or whatnot. Again, just combine the coverage and at the end getting close to 100% coverage is not a challenge anymore. It's so easy. I've been doing this in multiple projects with JavaScript or typescript doesn't matter combined coverage is the way to go it's not. Of the old and all measurement, but if you have 100% code coverage, no one is going to say they're not confident in their application. Whatever you learned of quality of the feedback is Cyprus component testing improves tdd component engineering and early defect detection. Once you move up the routing State Management on other complex things Beyond Components tdd with endpoint ass is more relevant. With tdd the key ideas to start with something failing. Want to do the minimum to get it to work and then you want to try to make it better. There are the links and references the book. The final application and the inspiration that contributed to this content. Thank you very much for listening. You. Have a nice one. Bye.
25 min
03 Nov, 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