React Testing with React Testing Library and Jest

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone it is time for another recitation this time on react testing so our project for today is to implement and test a react application this time it's a scoreboard for baseball game so the goal for our application is that we should have a scoreboard and some buttons and we should be able to keep track of the pitch count so there should be a button for strikes button for balls fouls and hit and it should update you know the the batter's count of how many strikes and balls they have when we hit the appropriate buttons we also want to test it we want to test any individual components that we write which as far as the project tells us we can do in whatever way we choose as well as testing the entire application that it works from the users perspective and that's what we're gonna do so the first step I already have my react server running I need to add the react testing library as a development dependency which is going to look a little bit different if you're on NPM but I'm just adding the at testing library - react as a development dependency and well that's installing I am going to fire up my tainting program and we're going to talk a little bit about architecting this app because this is a really important part of testing is trying to write code that's easy to test because code that is easy to test is generally easier to reason about and easier to compose and reuse so a good reusable component should be a good testable component and vice-versa and what do i mean by this well when we write a component we usually want it to be dumb we want it to have one purpose usually take props in and show data you know out or you know maybe it has some buttons and when we click on the buttons they do things what are the buttons do I don't know they just do something so when I'm architecting this app let's get a box so this is going to be my my app j/s and it's going to contain my entire application I know this is going to need some state the things we're going to need to keep track of is balls and strikes and I'm gonna need two more things I'm gonna need my my display right in my display display dot J yes all this is gonna do is display balls and strikes so you know I'm gonna have strikes and then you know that in a little box and same thing for balls and it can just take these in via props it doesn't need to know anything about how balls and strikes work we can just connect the state in my app to the display via props so now display is a very very dumb reusable component it just takes in balls and strikes for your props and then despise them that's gonna make it really easy to test because when I render it I can give it balls and strikes as props and check and see what get by text if they show up inside of our component boom easy testing ok so then now I'm gonna need another component and this is going to be my controls this is going to contain my four buttons one for my strikes balls fouls and hits and this is where our architecture really comes into play because I could do this directly in app j/s but what I'm going to do is I'm gonna write functions that you know count for when a strike happens of all happens foul happens and when it hit happens and I'm gonna pass them in via props to control stop J s so my controls isn't aware of the logic behind a strike a ball a foul or a hit it's just aware that it has four buttons and when they're clicked on it should execute these functions that it's being passed in so now my control is also very easy to test all I need to test is that when I click a button my my function gets called so then I'm gonna have to test the entire app Jase and this is where it gets harder this is where we we have essentially an integration test between these different components now I have to worry about State and I'm not going to write any tests that care about the implementation inside of my app J s I'm not gonna care that I did use state or if I wrote a class-based component or whatever for the purposes of my testing that's irrelevant I want to test from the perspective of a user and what to recreate the manual testing I would do to make sure that my app is working and what would that be wide open the page I would find the button that says strikes ID click on it I'd see that the strikes counter went up I'd click on it a couple more times to check and see if strikes rolled over and that it reset the other ones I'd do the same things for balls fouls hits that's what I want to do does it matter how I have that state sword from the perspective of me manually testing it no no it explicitly doesn't because the idea behind my testing is I write these tests that say what the correct what the correct behavior is not with the correct implementation is that way if down the road let's say this was a you know an old class-based component and now I want to rewrite it in a functional component with hooks well I should be able to tear out the old implementation put in my new one and from the test perspective there'd be no difference because they should behave exactly the same the user shouldn't care whether or not they're made with hooks and so that's all oh yeah I don't do my recording on zoom' I do it within outside program yes thank you I appreciate their concern no okay so our testing in this in our whole app situation in our integration test should not be aware of the implementation and that's good because when we change the implementation we shouldn't break the user experience and that's exactly what that test is there to to codify so let's check and see if my dependency finished installing okay it did and let me restart my yarn server just in case so I have just a bear create react app up nothing nothing here if I go into my source it's literally just the default - our SVG in our our Service Worker so we talked today about test-driven development and I really enjoy test-driven development I like writing the rules you know before I start writing my implementation because it helps me clarify what I want to do but speaking honestly test-driven development isn't great if you don't already know what you're going to do because I you know I talked about these decisions of architecture but from the perspective of most of us we're not going to be able to sit down and be like yes I shall do this and it shall be perfect we're going to start writing our components and try to figure these things out as we go so today I am going to write my components first and then my test second but what I'm not going to do is write my entire application and then write my tests I'm gonna do these one at a time so the first thing I want to do is I'm gonna make a components folder and then I am going to make my display component because that should be the simplest one get an import react from react I'm gonna define my components I'm going to return just an empty div for now it'll say display in here so I can visually check if it's working and then I'm gonna export my display so then I'm gonna pop into my app j/s and try to get this rendered on my screen so I'm going to import display from dot slash components slash display and then I'm going to render this okay so the idea behind my display is that I should be able to have some state in my app that keeps track of my my balls and my strikes and the display should display that I want to keep the presentation separate from my logic so what I'm gonna do with my display I'm just gonna make this very very simple I'm gonna have a div that's gonna say strikes and then it's going to display props dot strikes and then I'm gonna do the same thing for balls that's it and right now that's just gonna come up as this kind of empty thing because I haven't given it any values so an app let me test this I mean any of it two strikes and three balls full count okay so that didn't work oh it's because I misspelled strike this should be strikes so this exact thing that I did here of manually testing it this is the behavior that I want to codify into my test because we do this testing naturally right we should progressively you know as we're building our app check we should write our code we should check the browser make sure it's all good the idea is that we want to capture this behavior and automate it so that later I don't have to when I'm messing with the state implementation of my app be concerned that I'm breaking display I should already know exactly how display works and be guaranteed with my automated testing that display works the way I expect it so let's write our tests for display so I'm gonna go into my components folder I'm gonna create a file called display dot test j/s and then I'm going to do my imports I'm going to import react from react I'm going to import the render function from my react testing library which is going to be at testing library slash react and then I'm going to import my component so I'm going to import the display from dot slash display a s and now I'm ready to write my tests so I would do this inside a describe block and right now my editor is yelling at me and it's telling me that describe isn't defined it technically isn't at least in the scope but the way that we have our testing setup described and it and the other thing is that just our testing framework normally provides will all be defined inside the environment where the tests are run so I don't have to worry about that and then I'm gonna call this display then in here I can write any of the tests that I want so let's write a basic test renders without crashing so what we're going to do here is we're going to use our render function and we are going to render our display component so then now I actually have to run my tests so I'm going to open up my terminal not this one the other one then I'm gonna run yarn test which is going to start up our testing scripts so that should pick up our test and display test is yes and it passes all right so now I want to write another test you should probably name it better than me I'm just going to call it props work and what I want to do is I want to check and see if it works when I render props to it so when I render display with these strikes set to two and the balls set to three does that display visually correctly on my screen and the way I do this is I can save the output of this render into a variable and then I can call display dot get my text is that right I always forget the names of things yes get my text and then I can get an element by its text content I can also do this via destructuring because display this value here is going to be an object so if I wanted to I could extract the get by text function out of display like so and then I can just call it get by text I can also combine this into a single step I can just directly D structure get by text out of here for now here I'm going to define my display variable and then get to work on testing it like this and I'll do it the destructuring way in the next file so what I want to do is I want to find the component that has strikes : 2 because that's what it should be right if I give it to is a value of the strikes prop then I should be able to find on the page strikes : 2 same thing with balls but 3 and our testing suite is gonna rerun and there we go all our tests pass you should be suspicious of this if you write any test and it passes right away you shouldn't be like yep good to go you should always try to make it fail to make sure that it's working so let's check with the text for balls being balls : - okay now our test fails and I don't have to expect the results of get by text because if it can't find it it's gonna throw an error so my test fails and we know where it was so this is good for consistency's sake just in case the strikes and balls were hard coded as 2 & 3 we should probably check another value for this to make sure that it works with additional values and then we should probably try and check our edge cases so I'm going to check if the component is dumb so what if I render strikes as negative 2 and balls as 400 and that test is also going to pass because we know the way display is implemented it's just going to render our props to the screen and is this behavior important not really but part of the idea behind testing is to clarify this kind of edge behavior because if it changes in the future we might not realize it when we're thinking through the logic on our own but if we change it and then a test fails we know oh okay this has changed I can fix the test but maybe I should make sure some part of my application doesn't depend on the component behaving in this weird way so tests are there to help us clarify and notice things in this case it's fine if this component is done but I should probably write a test to verify that it is dumb so do we have any questions at this point for the for the messages are those error messages or what are they yes that's an error if get by text doesn't return an a result then it throws an error so let's let's trigger one let's put strikes two four here and then when our test suite reruns this this throws an error and it says it gives us the error message unable to find an element with the text strikes four this could be because ba bla bla bla bla so we could write an additional you know different string to try to find it or try to match our data up and it actually prints to us the the shape of this component and shows us the line that this happened this line through an error if I wanted to I could wrap this in an expect and I think it would handle the error a little bit cleaner but we don't need an expect yeah it does basically the same thing still throws an error which is fine also is there a difference between the libraries at testing a versus testing react library or react testing library I believe this is the most up-to-date version of this library I have there their github repo pulled up I believe Amir is asking because when we went through this we installed it a different dependency but I believe this the at testing library - react is the is the way to get the most up-to-date version of this dependency so that's what the repo service yes so just is a testing framework just is the thing that defines describe it expect those are all things from jest we can use just to test any type of JavaScript that we want react testing library gives us some tools to test react components like the ability to render a component this comes from the react testing library just doesn't have that feature because just doesn't know or really care what react is just just cares about giving us functions that we can write tests with but we use react testing libraries so we can render our our components and then search them for text inside of them or perform other actions to interact with our react application that just is really outside of its purview does that answer sorry what does the add sign in front of the important I don't know if if it actually works the way I'm about to say it in in a lot of other languages dependencies are namespaced so we wouldn't just like install react router and I'm sure you've run into this issue where you've slightly mistyped the name at a dependency and you don't get an error because someone else has a thing called like Axios which i've installed in the uncountable number of times so in a lot of other languages components that they're not just like I'm sorry dependencies aren't just named whatever I would if I published a library it would be called like you know bless / react router and this would be different than like you know the react training which is the people who make react router from their react router package so you wouldn't get into this issue where like people you know essentially domain squat on NPM package names and like no shade on react router but part of their success has been the ability that they were able to get the package name react router so they show up really highly in the in the Google searches so I believe that's what's going on here with the at testing library I do not believe that namespacing actually exists on NPM as far as I'm aware but I think that is what is trying to happen here so you may have also noticed this if you ever used unless I call the EM phone awesome they also have their packages under like a namespace as well which is called fort awesome which is incredibly confusing I was always wondering about that why is it called for it awesome yeah I have no idea but yeah it's so easy to see that ours and and and causes hours and hours worth of problems so any other questions here what's going on in crops work test is it really as is it as simple as it looks you get this is trying to pass down crops we're strengths for two and bolt or three and you just want to know if it actually displayed in the next two lines that's it yeah what I'm literally trying to do is when I did my visual testing here when I go into AB KS I gave my display two strikes and three balls and then I checked my web page that it says strikes two and balls three and so I'm literally writing that with code at this point that I want to check and see I want to render my thing with the prop I've given it and then I want to check if it contains the text this should be too if we weren't trying to trigger that error and then I do it again with these different props that's that's all I'm doing simple as that because displays should be a very dumb component props in JSX out does that answer your question yeah why do you why are you indicating strikes two balls three in both spots in display and in the test because this what I'm doing here is I am I am rendering the component with the props and then here I have to test that it contains the text that should appear given those props and then I'm doing it again with different values here and different values here so this is my setup and then this is my test whatever you're doing I can't tell which window is on top but strikes equals two in the top display that's not getting fed into no no no this is this is my app this is my app here what I'm testing is literally I am rendering the display with these arbitrary props I can remove this and all of my tests are still going to pass because in the magic testrunner land it's performing this where it renders my display and then checks it for text so why would you why would you have to import the component into the test file then if it doesn't care about the actual components well it does care about the actual component it doesn't care about app but I want to use display like here I literally write my how I would render display is JSX so I need to import display in order to use it and I can import other components in here if I wanted to but if I want to use display I have to have to import it and another question on the the the argument to get by text Dustin was mentioning you could actually wrap it in quotes yeah as opposed to this what is this regex yeah I wanted to touch on this so this is called a regular expression and you can google it and there's lots of information out there and I'll even show you a little bit of the powerful stuff you can do with it but the idea behind a regular expression is that it's basically a very tiny compact domain specific language for matching patterns within text so for example if I wanted to match like any number here I could replace this with this specific thing I'm allowed to do in regular expression which is 0 to 9 inside these square brackets and it would match in that position any number 0 to 9 so it allows me to express very powerfully patterns that I would expect it to to see within text and this will work it's not a good test because I want to know if the specific number shows up or not but I'm allowed to do that inside my regular expressions the problem is and what I wanted to touch on is that I have these hard-coded number 2 and 3 here and I have it here you know it is a good programmer I should want to put these in variables and reuse them that way they're you know easier to change and track down but the problem is so let's set balls is 3 and strikes as 2 and then easily enough I can use them in here but I can't use them inside my regular expression I cannot do something like this because this is not a template string regular expression has enough going on as far as what these special characters mean so instead what I do is this this syntax with these slashes this would be called a regular expression literal in the same way that square braces with stuff inside of them this is a literal array there's also an array class we can use and these lines of code are essentially exactly the same this is just the more convenient syntax mr. right edit we also have the ability to construct regular expressions this way so I could make a new regular expression that should be defined did I type it wrong let me check my notes oh it's reg XP and then it expects as its first argument the string that is going to form the regular expression and then is its second argument any options like I is for case insensitivity and so in here I could make a template string and do like strikes and then use my template string stuff and now I have the ability to create a regular expression with variables in it and I can do the same thing yes false yeah you know I don't have hard coded numbers and I still get to use all the powers of my regular expressions if I were choosing to do so which I am NOT that's how we create dynamic regular expressions I'll encourage you to go and read the mdn page about regular expressions or any other resource that you find there very interesting all right any more questions here how do you go if you like foiled strikes and the number into the friend oh I'm sorry could you say it again yes sorry so how how would you go about if you start with strikes and the value in two different days like if you can't really you know like if they're two different actually how would you check for the text is there a way to big access a specific if they're in two different divs you can let me break this real fast our error message gives us a pretty good hints actually so this could be because the test is broken up into multiple elements in this case you can provide a function for your text matcher that will make your masher more flexible so we can look up how to do react testing library natura function or across multiple things and there will be you know some nice documentation for how to how to do this there's also other things we can do like we don't have to get by text we can get by other types of queries you know we can get by label text and then examine properties of the the element that we've gotten but let's see wreck testing library I'm not finding the exact thing that I want right now across waters let me get back to you on this okay yeah but there are ways around it okay so any other questions all right well then we are going to move forward and do our controls because what I want is to let me actually put my display back in here I want to create a component that just displays my four buttons and when those buttons are clicked on they trigger a function do the buttons to care what that function is no they do not I'm writing a dumb component that's easy to test so I'm gonna go into my components file and I'm going to create controls j/s I'm going to import react from react going to define my components a return and empty div and export this and then I will render it inside of my app just to make sure it's working so then import controls from components controls okay and then so then what I want this to do very simple I want to make a bunch of buttons I want to make strike and ball foul and hit and what I want these to do is when they're clicked on on click I want them to call a function props dot something I'll just go down the line strike ball foul not fall and hit so now I have these buttons that when clicked on they will call functions and then what would I do if I wanted to check and see that this worked well I would do something like let's let's give this a prop called strike and let's just write a function that console logs strike all right because I'm not testing my my state or the logic yet I just want to make sure that when I click on this button it works so let's see yep works ok so now I want to codify exactly what I manually did so that I can automate it and repeat it I just want to test that these functions are fired when I click on each of the buttons so that's what we're going to do I am going to go into my components file and I'm going to create the testing file for controls so controls test j/s I am just gonna steal my imports from my other test file because I don't want to rewrite them and then yeah let's work with this so I'm gonna do my describe described as controls I'm also going to need to change this instead of display I want to import controls and then I'm going to start writing my tests so first one as always renders without crashing I am going to render controls and let me check and see if my tests did their thing yeah it passed okay so now I can actually go about testing these buttons so I'm gonna do something pretty similar to my old test I'm going to render controls I'm gonna save this into a variable this time I'm going to shortcut it and I'm just going to D structure we get by text function directly out of the object that is generated on my right-hand side because I don't really need to access you know that component I just need this get by text function and then I want to get one of my buns so let's do get my text and let's get strike okay so wait why did this not actually work oh because this needs to be a regular expression so my test sorry run why is this still crashing multiple elements found with the text that shouldn't be the case I know I'm not cleaning up between these but I wouldn't expect that to be an issue it might be I do it like this doesn't matter I would learn something interesting today if this were the case okay so this doesn't matter what it appears to be rendering everything twice okay so it must be an issue with me not cleaning up right it shouldn't because I am just rendering controls I am NOT also rendering the display see how here it's rendered everything twice I believe what the issue is is that what happens if I comment out this yes okay so the issue is with me not cleaning up between each one of my renders so as I render this - like this fake testing Dom I'm not isolating these tests between each other so this is showing up more than once so my issue is that I need to I need to kind of clear that Dom in between each one of my renders and luckily there is a way to do that and it's part of the testing library I just have to import it at testing library slash react slash clean up after each and notice that I'm not importing this into any variable name because when I import this it will run this file for me and this file sets it so that after I finish each text my testing Dom is clear and I am free to render another application so now all of my tests pass because I'm not getting that double rendering occurring so neat bug okay so now I have my button and I'm not sure exactly what to do with it did someone on meetin themselves to ask a question yeah as me inside of your describe I know that earlier today we had it as calculator Jas well you know or that's but like the component name and then okay yes and I notice that you have it mm-hm yeah that's how we're doing it earlier yeah this this does not matter this is just a string that we're labeling this test with so when it fails it will tell us this name so I'll know which test failed so if I deliberately fail this one and we watch in my test rerun err if I scroll up it will say that okay so in the controls described block the strike test failed thank you and that's what good names are important but I can call it whatever I want so I have my button and at this point I'm not really sure what to do with it because we didn't entirely cover this today I want to click on it and the way we do that is react testing library offers us this function fire event that lets us interact with Dom elephants and do things like click on them so if I do fire events it has a method called click and I can give it my strike button and this will click on my button however my button isn't currently set up to do anything when clicked on because I didn't pass any props to my controls so let me change that I'm gonna use the exact same thing I did in my app Jaso I'm just gonna give it a function that when clicked will console.log strike and we can actually see this happen it literally console.log strike because we click the button and it runs that code and it prints that log to our console which is pretty cool not that helpful for um for determining if this test passed or not but need all the same we can console.log from tests and they show up in our test runner so instead what I want to do is I want to figure out if this function that I passed is the strike prop actually completed or not so there's a homegrown solution that I don't know some of you may have liked figured this out today where I can make this let variable let's call it clicked and set it equal to false and then I can give a callback function to strike that it will set clicked to be equal to true and now after I click this the value of click should be true so I can expect clicked to be true and this test passes and let's double check what if I change the name of this to something to make this test deliberately fail yep so we expected it to be true but instead we received false so this is a neat trick to figure out if our function was run however there's a bit more of an official way to do this we also didn't talk about this but while I have you I'm gonna I'm going to show you and that is our just mock functions so jest our testing library comes with a bunch of cool tools to enable us to test things so let me I'll just write this two ways that's how you spell part deux right I know French so just gives us this ability to make fake functions and then learn things about them when they're called what arguments they were given when they're called and it lets us easily create mocked functions so I can make a function called clique and I would make that with jest dot FN and you can read all about this I in the jest Docs where we have all of this stuff about mocking functions so we make it with Jess FN and then I can pass it to my component as a prop and then instead of caring about this clicked I can expect click to be called so kind of I can expect my function to be called and this is essentially a formalization of the little trick I showed you with less but now we have a bit more of an official way to describe if our if our function has been called or not and this test passes I'm gonna rename this so I can continue and I can do this with all of my other all of my other buttons and I can copy and paste this and change this instead of strike we're just going to do this to ball and this will work and I can continue copy and pasting and it's at this point that your dev senses might be tingling that I shouldn't be doing copy and pasting this is where we should start to abstract things and you're both right and wrong so this is where the art of testing becomes very subtle and opinionated I'm sure most of you have seen the meme at this point it's you know the you know spend hours debugging your code and then realize that bug is in the test the more complicated we make our testing functions the more likely we are going to have a bug in them whereas this is incredibly straightforward is it long and is it copy and pasted a whole bunch of times yes and so there is a drawback there and then it's less maintainable because if we want to change one of these tests now we have to go and change all of them but it's very explicit dustin mentioned today that tests serve as documentation i can go into this test file and i can read this and i can see that we pass a function to this prop then we click on the button and then we should have called that function it's very clear to me what's happening here now I'm gonna share to you what I wrote when I originally did this project because I am insufferable and desire to be clever this tests all the buttons what does this do none of you are done reading it at this point and that makes it in many ways a bad test because you're going to have to decipher the logic I used to test these buttons now is it efficient as far as lines of code goes you betcha would it be easy to you know change and extend a certain things changed yeah probably but it doesn't serve as good documentation because it's complicated and so we have to walk this fine line between using our skills to write you know clean maintainable dry code with writing over complicated tests that can obscure what they're actually trying to do so I will include this as an exercise for the reader in the code but here probably a good middle solution would be to try to abstract the common parts of this out into a function and then call it and give it each button as an argument maybe that might be a good middle ground for for getting this test to work but for now I am just going to copy and paste for the remaining buttons so ball becomes foul and foul becomes hit awesome and all of my tests pass no they don't oh I forgot something when I was finding in replacing text so this should be hid and this should be foul and that's good there and now all of my tests should pass there we go all my tests pass so all of these buttons work when they're clicked on they run their Associated function nothing more nothing less I have a good testable component and if I break this later I won't be trying to stumble through my testing for my app Jas where I'm trying to decode all the logic that's happening and maybe it's this thing that I broke over here or maybe my state management broke or maybe when I refactor at this function it didn't work no I'll know that I broke this exact thing maybe I renamed a prop and forgot to update that map Jas you know it it's gonna make it obvious to me where the breakage occurs this may seem obvious to you maybe not even worth testing that these buttons do the thing when they're clicked on but it's gonna save you a lot of headache a lot of the value in testing is reading other people's code and you know who is an other person you from two weeks ago when you will walk into a code basin you need to modify things if you don't have tests you can never be sure you're not wildly breaking things so tests are so necessary when we make changes when we're implementing it most of the value we get out of tests is clarifying what we mean and thinking about our architecture but the true value in tests is when we go and we have to refactor code yeah change code and we can know exactly where and when we broke things so we're only seeing the first half of the value of testing and the second half is honestly much more gratifying when it comes up so do we have any questions here I sort of missed what I was referring to when it's a strike button equals get by text strike slash hi ah I is for case insensitivity so if I decided to capitalize these buttons later the test would still pass because again we're doing this from the perspective of a user the user does not care if strike is capital or lowercase but we do probably need it to contain that word okay cool yep all right any other questions here okay so now I'm gonna implement the actual stateful logic so part of my architecture has led me to a nice place in making controls dumb reusable easily testable whatever you want to call it I've decided that control should not contain the logic for what happens when a strike happens so this helps protect us from this behavior that we may still have where we would want to pass in you know our set strike function and then inside of controls write the logic for what happens when we already have two strikes and we have to clear the count this this logic should probably remain with an app with that state control should be dumb it should just be worried about firing a function when that button is clicked on so that's what I want to implement now within my app so let's get our state up in here so I'm going to import use state from react and I'm gonna create two variables strikes and it's going to be zero initially and then that's deep balls okay so then now I can actually hook this up I can I can display my strikes I can display my balls and with reasonable confidence I know that this is going to work because I already have my testing set up yay and now I want to write the logic for what happens when we score a strike and for now I'm gonna write it wrong and then we're gonna fix it so I'm gonna do Const let's do score strike and doesn't take any arguments what it's going to do is it's going to set the value of strikes to be strikes plus 1 and is that correct as far as baseball goes nope same thing for balls it's going to increase it by one so score ball let's do foul and here that's going to set the value of strikes to be one more that is also not logically correct for the rules of baseball but I just want to verify what I had done with my testing that when I click on these buttons they're going to do the appropriate things and when we get a hit that we then clear our count so strikes goes back to zero and balls go back to zero and let me pass these in as props so strike is going to be score strike ball is going to be score ball foul just gonna be score foul and hit just going to be score is there a reason I decided to prefix these two the score not really except I I don't it gets kind of confusing when we have like strikes plural and strike singular and they're they're vastly different things in terms of you know representing a function in terms of a variable so it can be helpful just to kind of delineate these things alright now let's do our manual testing when I click the strike goes up ball goes up foul strikes go up hit clears the count foo it's perfect I'm done obviously not if I were testing this I would want to see that when I you know get three strikes that it clears the count because I've just struck out but it hasn't and so I want to keep this in mind because this is the behavior that I want to encode in my automated tests what I'm doing manually here to check out the logic and I'm going to continue writing this to make it right so what I want to do is if strikes is equal to two then I want to do my fancy stuff if strikes isn't equal to two well then I'm just gonna give us another strike but if it is then I want to clear the count because I've just struck out so I'm going to set strikes equal to zero I'm get a set balls equal to zero so now I can test this and it correctly rolls over awesome and if I add some balls in there that also sets back to zero I want to remember that that's the behavior that I tried and I want to encode that in my test so let me correct this with a ball so if balls is three then I want to clear the count otherwise I want to increase the number of balls so let me test this behavior again I want to encode this into a test I'm gonna add some strikes I'm gonna get three balls and my score should reset so now with a foul ball it should increase strikes but if we're at two strikes the count remains the same so I'm just gonna check if strikes does not equal two if it doesn't then I increase my strikes so if I'm at two strikes out there's nothing if I met less than that then foul adds on and I could be writing some safer logical operators like maybe less than two what if strikes accidentally gets above three but for the purposes of this it's fun and then scoring a hit works so now I have my logic all hooked up and it works and now when I write my integration tap test for my entire app this is the behavior that I want to encode me checking to see if strikes clears the count me checking to see if it clears any existing balls that were on the scoreboard checking to see that foul doesn't cause our strength or our count to roll over and check them to see that hit clears the count those are the tests that I want to write for app and I'm going to do this entirely using get by text and firing click events I am NOT going to test the individual logic of any of these functions because as far as the user cares it doesn't matter that these functions exist would it be a good idea to possibly pull out these functions into their own library and like have a function that given you no account returns the updated count if a strike was stored yeah then we could unit test those functions easily outside of of the logic of the main app but I'm not going to do that here that's that's some complexity that I don't want to add but this is what we should think about when we're testing is how I can isolate the logic that makes it easy to test because that makes it more reusable right writing things that are easy to test has great ramifications in terms of the quality of our code but let's write our tests I'm gonna Jack this and then I'm gonna go into app dot test J s I'm gonna paste this and I'm gonna delete everything they already have because I don't really care I'm gonna do my fancy find replace to replace all instances of controls with app awesome so now I'm testing app J s so let's let's take a look at our test Runner okay so that has passed so we're good there so now I want to actually before I start writing these tests does anyone have any questions about the logic that I wrote to get these things to work inside of a BIS no we all feel good with this we all feel good with this pattern of writing the logic inside app J S or at least not necessarily inside after at yes but where our state lives and not letting that logic spill into our child components because that's a really powerful pattern that I want you to repeat okay so now let's write our tests I'm gonna write one big test because there's a small issue of how we handle state carrying over because when I go to another test I'm gonna clean up and I'm gonna essentially unmount my app and that I'm gonna make a new one and it's just gonna involve me doing the legwork to example get the get the strikes up to two when I want to test the roll over which I'm already gonna do when I test you know the strikes button for it to work so I'm gonna take a shortcut here I'm just gonna write this all in one big test is that proper protocol no it isn't we should probably break up our tests but it requires a little bit of extra work or perhaps some ingenuity as far as maybe not cleaning up after each maybe finding some other solution maybe we can have app take props to set the initial values of strikes and balls to make testing easier I'm not going to worry about that right now I'm just going to make one big test so first thing I want to do is I want to render app and I'm going to D structure out get by text so now I have my app rendered inside of my test I'm also going to get my buttons I want to get the strike button which I'm just going to call strike and I'll do that with get by text strike I and let's look at our test runner okay so we've instantly run into an issue and that's that when I try to get this strike it's now getting this value here and this value down here there's a lot of potential solutions to this I could use some other get function to get it buy some property that I've set on this button itself and I would encourage you all to read the documentation for the react testing library because we have a lot of get by functions that will let us do interesting things for now I'm actually going to exploit the abilities of my regular expression the up caret represents the beginning of a block of text usually a line and the dollar sign represents the end of a line of text so this will get exactly the word strike and now my test passes I heard someone on meet yourself was there a question that wasn't sorry no okay all right so this is a regular expression trick and if this looks weird to you which it should encourage you to google and read about regular expressions but the up character represents the beginning of a piece of text and the dollar sign represents the end so this will allow me to get something that contains exactly the word strike nothing more nothing less and I'm going to do this as well for ball and for fowl I apologize for running into After Hours time we're almost done here and then fowl becomes hit all right so now I have all of my buttons and what I'm gonna do is I'm going to click on them and I'm gonna test and see what happens so I'm gonna test that strike works ideally I'd write this in another test but I'm gonna run into issues with the clean up after each as I try to carry the state between these tests is it a good idea to carry state between tests usually not but I'm being lazy here so so did someone on mute themselves so this character it shifts six yeah yeah it is the up caret not the up arrow which conveniently for me and then those are the keys that I have to move to the beginning and the end of the line look at that okay so I want to test that strike works so I'm going to fire a click event on the strike button twice so I'm gonna do fire event dot click and then have it hit the strike button then I'm gonna do that again and now I'm gonna get by text and I'm gonna look for strikes two because what should happen is after I hit the strike button twice this should say strikes two so i'm encoding this into my test the thing that I did manually and it passes let's try and break it does it work with strikes three it does not we cannot find that text all right so then let's test that strike rolls over okay so then let's fire another strike event and then let's check that strikes are zero awesome maybe I also want to check if ball works so instead I am going to fire three click events on ball and that also works I should also check and see that balls is set to zero here and that are our count correctly rolls over and we can keep doing this and ideally these would be in other tests so let's check and see if ball rose over the count so we would fire let's do two strike events and then three ball events and then a fourth ball event and that should have our count be back to zero and now you know actually I'm going to stop talking about that no I'm gonna keep this in one big test don't do this in real life though and now I'm going to test test that foul doesn't reset doesn't increase strikes past two so let's fire a foul event and let's check that strikes is one then let's check that it is two after another one and let's check that it remains two and then let's also check that hit resets count so I will fire a strike I will fire a ball and I will check that balls and strikes are now back to zero I forgot to fire my hip awesome and notice how I'm not checking here that strikes in balls or one I shouldn't have to do that because if that behavior didn't work then one of my earlier tests would fail so you can be extra sure and essentially retest your tests but I can usually rely on my previous tests when I'm testing additional behavior so I don't have to test that striking ball worked because I already did that so does anyone have any questions here come on there's got to be a question I have a question but it's about something you did earlier yep that's fine okay and it you have the same issue where in one of your earlier tests you got the error found multiple events with the following tax didn't I missed the moment where you went over how you fix that bug yeah so the issue is that I will uncomment the line actually I need to do it in the other file that was in controls I'll uncover the line that fix it and we'll look at the interesting error that happens okay so let me scroll up scrolling okay so it tells us that we found multiple elements with the text strike and if we look it should print the the elements that it was searching through and you'll notice that inside this body tag is essentially our our component our controls component repeat it we see it once and then we see it twice the reason is because of how this render function for the react testing library works if I wanted to I could render multiple components into the same document if I wanted to test them like one above the other one so when I render controls here and then I render controls here it actually shows up twice if I don't want that behavior then I can import this from react testing library this clean up after each and this will set it up so that after every test is run it totally clears that Dom and we're ready to render a new singular instance of our own component to test the TK does it a little bit different and thank you so much for bringing this up because I wanted to mention this difference because the the way it's described in the training kit is that you import this function clean up from react testing library which is the function that that performs the cleanup of the DOM and then built into jest is this function after each that we can give it a function and it will run it after each test there's also a before each and these are really useful for setting up the environment that we want each test to run in let's say we want to set up you know some other state you know we might want to talk to some service get everything cleared up this is very essential when you do backend testing with a database that the database contains all the data that you want to work with but the the way I did it where we import this file directly or we import the cleanup function and then have it run after each or before each also works is also a valid way to avoid that multi rendering problem does that answer your question yes thank you great all right any other questions the the exact same way we we passed props the exact same way we do normally so on this line I'm just rendering my controls component but here I'm rendering my controls component and I'm giving it some props we can just write any JSX we want inside of here when we're totally allowed to do that so I can pass props to it and I do that here as well I passed this prop and this one and in my display testing we do the same thing when I render my display component I give it some props just like normal does that answer your question okay I would make sure because sometimes you have multiple tests failing and you'll fix one and the others will still fail and you haven't fixed it in there either and that can be confusing or I would you can we can do console logs and they'll show up inside of our test runner so maybe in our display component we can console.log props and that will show up when we run the tests for display and you can use that to try to help debug the situation that's going on yep see here's my console logs so ya can still use that same tool the much valued console.log great any other questions okay well if that's it then thank you all so much for coming after hours is happening in the after-hours Channel if you all want to jump into there and continue talking about the joys of testing which I really truly enjoy actually I'm gonna hold you here for one minute you can leave them if you want to I just want to talk about the value of testing because you won't really appreciate it until it saves your life when I did my back-end build week halfway through the week I had to make a major major change to the underlying database that I was working with I'd switched the type of database that I was using and it broke basically my entire application now luckily I had written tests for everything so I was able to know what broke in in what way and I was able to fix them one by one and it was not a big deal at all if I didn't have tests it would have been an absolute nightmare because I had just broken my whole application I would have had to manually figure out what broke where and what layer and what function in what file what was going wrong but I had this beautiful output it was blinking red and scary but it was every test that failed and every value that it expected to be one way and I got this instead and and from that moment on I have you know vowed to myself that I will always write tests I will leave the code a better place than I found it because as soon as you have to change something if you don't have tests oh it's gonna hurt so bad it's gonna hurt so bad so write tests do it until he ended it takes just like adding and subtracting I feel like and also like just trying to follow like all the syntax I guess that's kind of been like my focus like today but like in the car I guess I kind of get had to test mm-hmm like how I would use it in the context of like addition and subtraction but like in an actual and product where I was building some app I wouldn't know what I would want to test yeah so there's our I think he's some time to come up with yeah you know how to implement that correctly there's useful there's there's a lot of different kinds of tests the easiest ones to write and the most common ones are unit tests and that's where we test those pure functions like math we give it input it gives us output we write the tests you know and check that the input always equals the correct output and we're good to go what gets harder is these more advanced tests like you know testing these these display things the integration tests where we're kind of testing all this functionality bundled together my advice to you when you're in this situation is if you find yourself checking something more than once that should be a test so like as I was writing this you probably went like okay strike strike two okay now it's back to zero what if I add a ball strike strike okay now that's back to zero you're gonna manually test your application as you write it just try to be aware of like the things you keep as you're trying to figure the logic out the things you find yourself keep doing if you do it more than once should probably be a test you probably want to guarantee that that behavior works this is also one of the reasons that test-driven development can help if you write your tests first like if I click this button this should happen then you write your implementation until the test passes and and that's another way to help think about this sit down and be like alright what do I want to do I want it when I type in this form and hit enter I want it to add a to do so I'm gonna write a test that does that and then I'm gonna you know write the actual code to implement that and then you know keep fixing it until my test passes just watch out for what you do when manually testing all right any other questions okay well then thank you all so much for coming and I'll probably see you in after hours in just a bit
Info
Channel: hBlevs
Views: 12,862
Rating: 4.9868851 out of 5
Keywords:
Id: Jf5zUc3MBtM
Channel Id: undefined
Length: 80min 54sec (4854 seconds)
Published: Thu Aug 08 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.