Jeff Terrell: Okay, here we go. Today we're going to continue with testing.
Jeff Terrell: Cannot there. I'm actually I've been looking forward to this lecture, since I got to thinking about how he's going to do it. So it's going to be a little bit less slightly and a little bit more Cody.
Jeff Terrell: So hopefully should be fun.
Jeff Terrell: Okay, yes. So announcements. This is the last lecture. For me, this semester.
Jeff Terrell: I maybe I'm sorry, or maybe you're welcome, or maybe. Congratulations. I don't know how you feel about that but yeah from here on out. It'll be all presenting your tech talks so I'm looking forward to that. I hope you all are
Jeff Terrell: Should be a lot of fun. A lot of different topics. It's just one of my favorite parts of the classes to hear you all present on things and learn things from you help me stay fresh and current on upcoming technologies. So that's cool.
Jeff Terrell: We do the final quiz today. So I'm going to start a bit early going to try to get done by 340 today with this content and then we'll move to the quiz that is on Open Class.
Jeff Terrell: There's two of you who still have not signed up for Open Class. So if you don't do it by the time I published the quiz, you will get a zero.
Jeff Terrell: I i've missed a lot of times, so I feel like there's been playing morning
Jeff Terrell: Okay.
Jeff Terrell: Right. So remember that you can get technical help in the app lab. You can see app lab at UNC edu. For more info about that I typically attend
Jeff Terrell: The app lab fairly often. But some next week and a half or so I'll be out. I've got some stuff going on. THAT MEANS I CAN'T. CAN'T MAKE A D AMP lab, but other people around to help you and I'll be back in a week and a half or so to pick that up.
Jeff Terrell: So given that we're moving into tech talk season in this class. I will not necessarily have a lot of announcements or take up a lot of time in our class readings.
Jeff Terrell: From now on, and so I might not remind you about the upcoming assignments that you have to. So I wanted to just mention them all right now so that you were aware of them. If you probably were already but
Jeff Terrell: Just so that I know that I've told you during class Tech Talk. Of course you're aware of that's coming up. You have your day assigned and you should have an email about when that is
Jeff Terrell: Test coverage report is due the weekend November 1 which I guess is week after next
Jeff Terrell: Developer read me is through the week after that. And then the client oriented overview and apples reflection to our do the following week
Jeff Terrell: Finally, at the end of the semester, your personal report, which is one of the few non team based assignments that you have this semester.
Jeff Terrell: May be the only one. If you submitted your project preferences as a team.
Jeff Terrell: That is due on Wednesday, November 18 and that will help me determine. You might remember this from the first day of class what factor to wait your project grade by
Jeff Terrell: If you are a terrible teammate, you will get penalized based on the personal record that I hear from your teammates. If you were a great teammate I you might even get a little boost to your grade.
Jeff Terrell: So that's a little incentive there. But that's, that's how I find out about that is that personal report.
Jeff Terrell: And finally, the final presentation is going to be during the final exam period, which is Friday, November 20 at noon, and like I did with the midterm presentations will do those in random order questions about upcoming assignments.
Jeff Terrell: Or any of these announcements.
Jeff Terrell: Okay.
Jeff Terrell: Moving right along. Today, I'm going to start by saying a few words about tech that I'm not going to spend much time on this because I really want to get into the testing demo after that and spend most of our time there.
Jeff Terrell: After I finished that I have a few tips for how to write code that is test friendly.
Jeff Terrell: There are some things you can do to make it easier to test your code. So we mentioned just a couple of those. And then finally, we'll have quiz five on Open Class.
Jeff Terrell: So let's talk about tech debt.
Jeff Terrell: So I worked on a project recently in fact quite recently. And this project was in kind of a bad place. I wasn't, it wasn't a success story, necessarily.
Jeff Terrell: Even though it use some some technologies that I really love it didn't use them well and the code was really kind of a mess.
Jeff Terrell: There's a lot of duplicated code all over the place.
Jeff Terrell: There was kind of a mixture of different approaches that meant that
Jeff Terrell: It's kind of like the code was a jack of all trades and master of none there's kind of like we're relying on two different things to solve the same problem, which meant that
Jeff Terrell: We had to kind of understand how both of these things worked in order to actually understand the code. So it's kind of a mess.
Jeff Terrell: I'm one of my key takeaways from that project and my experience on it was how important simplicity really is and how important that that refactoring piece of the red green refactor cycle is because if you commit
Jeff Terrell: Messy code that's kind of tends to get repeated by other people as they see something that works. They'll copy it, or maybe you'll copy you think, Oh, I know. I know how to do this in the past.
Jeff Terrell: Here's how I caught the form submission event. I'll do the same way. And maybe that code wasn't as simple as it could be. To begin with, and so you're kind of compounding that complexity.
Jeff Terrell: But you might also need to like extend a feature that was implemented poorly, which means that you've kind of add another complication to already complicated code that wasn't simple to begin with. And so you're making that complexity get even even bigger.
Jeff Terrell: So, so the key takeaway here is to spend time refactoring. And that's the practice to to focus on. We talked about that a fair bit with XP and the core value of simplicity
Jeff Terrell: But
Jeff Terrell: Well, yeah, let me, let me just say one more thing about refactoring so that dry is a commonly used word if you've maybe been an internship. You might have heard this.
Jeff Terrell: But it's a fairly common thing that people say it means don't repeat yourself. In other words, if you have repeated yourself in code, you should figure out how to extract that repetition into a function that maybe that's parameters in a way that captures the variation
Jeff Terrell: But you shouldn't be repeating yourself all over the place.
Jeff Terrell: I've heard a little bit of pushback on that, by the way, it's it shouldn't be a religious rule. You should never repeat yourself. There are good reasons to do that.
Jeff Terrell: Especially if you don't really understand what the common pattern is terms, it makes sense to repeat yourself a few times until you can get a sense of the variation
Jeff Terrell: And then consolidate all that into a parameter is a function or, you know, then you can kind of see what the best approaches. So
Jeff Terrell: Again, as with a lot of engineering, the answer is it depends. But this is a helpful rule of thumb to go by. Don't repeat yourself.
Jeff Terrell: All right, but I haven't really explained what tech debt is. And so I'm doing this a little bit out of order here, but the
Jeff Terrell: The key idea here is that, just like with your finances. If you have a credit card and you don't really have the money for something, but you buy it anyway on credit. That's going to be a drag on your finances.
Jeff Terrell: That means that you're gonna, you know, in the next month you'll have a little bit less money to work with because some of your money has to go to paying the interest on that loan that you took out to pay for that thing.
Jeff Terrell: Right. Hopefully everybody understands that about credit cards, at this point, but the more that you have, the more interest, you have to pay every month and so
Jeff Terrell: It's not really a binary thing. It's, it's, like, the more you have, the worse it is but
Jeff Terrell: But if you have a little bit, it can tend to produce more because you have even less money to work with the next month. And so if you didn't have enough to start with.
Jeff Terrell: You're going to have even less than next month. And so, you know, something needs to change. Otherwise you'll kind of just get into a downward spiral. And the same is true with complexity and software projects. It's actually a pretty apt and
Jeff Terrell: What's the word analogy.
Jeff Terrell: In software. If you have a little bit of complexity, like I was saying earlier at tends to get compounded later.
Jeff Terrell: And complexity tends to be yet complexity and it kind of cascades on your so it really makes sense to spend some time.
Jeff Terrell: Before you merge your code into master and before, maybe even before you create a commit to go ahead and refactor what you
Jeff Terrell: Create it and help it be as simple as it can be, so that you're not going to cause a drag on progress, the more complex or code bases, the more of a drag, you're going to have in your code base, you know, you're trying to add some feature and
Jeff Terrell: Rather than make a simple change in one place, because your code was well factored you have duplicated code. And so you have to go search through your code base and figure out, okay,
Jeff Terrell: What we're all the different places that I referred to this function. Now I'm going to change how each of them works and maybe there's more complexity that kind of cascades beyond that have to change the colors and those functions.
Jeff Terrell: And it gets harder to search because there's more code. It's harder to know and have sort of comprehensive knowledge of the entire code base because there's just more code to deal with. So for all those reasons it's worth it.
Jeff Terrell: To to pay off your tech debt and not to go into that at all. And to keep your code simple
Jeff Terrell: That's the core idea of tech debt.
Jeff Terrell: Let me say one other thing about tech debt, and then we'll get to the testing demo. One thing is it depends. Some, some tech data is actually
Jeff Terrell: A good idea to do just like it can be really useful to have
Jeff Terrell: A credit card. In some cases, sometimes it can kind of get you out of a bind in an unusual month when you really did, didn't need a little bit of extra money, maybe to repair your car or something like that.
Jeff Terrell: So just like an engineer would say it depends a lot tech debt can be useful. And there's not a black and white here. Have you should never go into tech tech should always your factor or anything like that.
Jeff Terrell: So as one example, and probably most common example if you have a deadline coming up and you really have to get a future in for whatever reason.
Jeff Terrell: It might be worth it to leave the implementation kind of sloppy.
Jeff Terrell: Or maybe you don't write tests or, you know, these are some shortcuts that you can take just to get the feature out the door.
Jeff Terrell: But if you do that, it's really important to remember. Okay, now I'm in debt. Now I need to pay off that debt. Otherwise, that's kind of snowball on me and I'm going to be in a bad place in a few months if if I don't address that. So,
Jeff Terrell: You know, don't consider that done yet, even though it's been shipped don't consider it done until you've made it simple and added tests and whatever. And that's, that's my recommendation and that's what a lot of agile shops will do
Jeff Terrell: Now, it might be important to have a conversation with your manager or with your clients in the case of these projects.
Jeff Terrell: To say, hey, listen, I got that feature out the door, but I'm not going to be able to get as much done. Next week because I've got to go deal with a mess. I made if we leave this mass, it's going to make everything slower in time and it's a bit of an abstract value proposition, but
Jeff Terrell: You know I'm going to have to spend some time cleaning up the mess. So I can't spend as much time working on new features.
Jeff Terrell: And this can be a conversation point, it's, it's something that can be a little bit hard for people who don't understand code to understand
Jeff Terrell: Hopefully the credit card analogy. Makes sense. But anyway, that can be a worthwhile way to think about complexity and how to deal with it. That can lead to some interesting conversations among your team and with your clients as well.
Jeff Terrell: Okay, any questions about tech debt.
Jeff Terrell: I haven't seen any so far. So okay, so all right let me move on then to
Jeff Terrell: To demoing testing. All right.
Jeff Terrell: Okay, so let me see here. Well, I should have done that. Yeah.
Jeff Terrell: Hang on a sec.
Jeff Terrell: Here we go. Okay.
Jeff Terrell: So for the testing demo. What I'm gonna do is I'm just going to illustrate some of the different ways of testing. I'm going to be using JavaScript and Node JS.
Jeff Terrell: I'm going to try to illustrate the trade offs at play here. When we're doing actual testing. And why would we do an integration test. Why would we do a unit test, I'm going to be also including test doubles in that conversation so
Jeff Terrell: So we'll see what those are and how to use them a little bit and also some of the trade offs associated with them, and likewise with dependency injection. I'll introduce that topic. And we'll see kind of why those might be a good idea or a wealth anything in some contexts.
Jeff Terrell: I this is based on commits in a public get repo. So, you are welcome to follow along if you'd like to do that clone that repo here.
Jeff Terrell: HTTPS get lab com Jeff Terrell slash testing to to our testing dash tutorial. So if you clone that, then be sure to say MPM install so that you get the jest dependency Jess is what we're using for testing here.
Jeff Terrell: And I should mention that you have, you have to have Node JS installed to be able to use all this. So if you have no Jess installed that might be more trouble than it's worth. Right now, but you can always do that later if you'd like.
Jeff Terrell: I'll summarize some takeaways at the end. So don't feel like you have to take notes or anything but Sam I mean this is recorded, so you can also come back to it later.
Jeff Terrell: Right. So that's the the repo that you can clone. I've already done that, of course, I have that on my hard drive.
Jeff Terrell: I mentioned also the commit messages are actually pretty verbose and descriptive. So it's almost like a mini lecture just walking through the commit messages.
Jeff Terrell: in their own right. I kind of did that on purpose so that you could do that. So be aware that those sort of notes exist in commit messages.
Jeff Terrell: Okay, so let's do this.
Jeff Terrell: Alright, rearrange my windows here a little bit. Alright, so I have the code up. You might recall, I have this get L alias to find
Jeff Terrell: And
Jeff Terrell: That is, here we go.
Jeff Terrell: Nope, that's la sorry good L is get log dash dash graph dash dash decorate dash dash one line.
Jeff Terrell: So I cheating way to do this would be if you wanted to say something like, this isn't a bash compatible shell.
Jeff Terrell: You could say, Get out equal alias get X equals get log, etc. If you wanted to, but you can also add something to your get config. This way we that something like get config global something or other. I don't remember. You can also edit your
Jeff Terrell: config file. If you want to add that alias there. We saw that earlier. So I'm not going to spend a lot of time on that. But
Jeff Terrell: Anyway, that's the
Jeff Terrell: My quick one line view of the commits and I'm gonna basically walk through these commits, more or less, every commit in order. I'm going to skip the first couple of them, but
Jeff Terrell: Yeah, so let's let's check out
Jeff Terrell: This one here.
Jeff Terrell: Alright, so
Jeff Terrell: What I did here.
Jeff Terrell: Is I just added some code for some simple functions to compute. Let me to my face off here.
Jeff Terrell: Some simple simple functions to compute
Jeff Terrell: Now,
Jeff Terrell: Am I work okay to compute the some mean and standard deviation. It's really kind of all about the standard deviation, but the standard deviation needs the summon mean to do its job. For those of you in my 590 class last semester. This is going to be fairly familiar
Jeff Terrell: I spent a lot of time in my five minute class last semester talking about testing and ways to structure your code. So this is going to be maybe a review for those of you who I had last semester. But with this. Isn't JavaScript rather than closure.
Jeff Terrell: So that's new. Anyway, the point of all this is to create a standard deviation function. And we see that here.
Jeff Terrell: This is some just JavaScript code and then Node JS projects. So we're doing things like require and exports.
Jeff Terrell: This is just straight Node JS without verbal or anything so that we can access our iOS six module system i don't i don't think node supports that out of the box. But maybe that's changed since I last looked
Jeff Terrell: Anyway, I'm not super important that point is we have some code and this code actually
Jeff Terrell: I'm gonna, I'm going to assert that this is correct code. But how can you believe that there aren't actually any tests. And so maybe it's it's a
Jeff Terrell: Little bit hard to believe this is this is correct, unless you actually understand it yourself. I wouldn't blame you for being skeptical. I mean, what if there's a bug hiding in there. It's not a lot of code. But there's still plenty of room for a bunt hide.
Jeff Terrell: Okay, so
Jeff Terrell: I'm going to go to the next commit
Jeff Terrell: Actually, no. Yeah, sure. I'll go to
Jeff Terrell: Check out by the way my SEO is an alias for checkout. In case you were wondering what that is.
Jeff Terrell: Okay, so here I'm adding just for testing purposes.
Jeff Terrell: I'm ignoring the node modules.
Jeff Terrell: Directory, because that's where my ingests and other modules are stored
Jeff Terrell: I don't. This is package JSON. I don't need to care about that.
Jeff Terrell: But we can see that just became a deaf dependency where, depending on a recent version of just actually redid this example from last year so that we can have a recent version of jazz to not get a lot of warnings. Whenever we did install
Jeff Terrell: But, but I did change the test scripts. So in my package JSON. You don't need to care about all this for JavaScript. By the way, this is I'm just explaining what this is, but if you're not doing a JavaScript project. You don't need to care about the details here.
Jeff Terrell: The point is that I'm adding a test script that will call just and it will call it and watch mode.
Jeff Terrell: So that it will watch the file system for changes any of my source files and rerun the appropriate tests if needed.
Jeff Terrell: Okay, so I can do that with
Jeff Terrell: If you haven't already, you'd have to say MPM install to get that installed locally, but if you have installed already, you can say MPM test and run that test script.
Jeff Terrell: So it's going to call just dash dash watch. And at this point, there are no tests. Well, it says no tests found related files changed since last commit. I'm going to hit a to run all tests.
Jeff Terrell: And it says no tests found at all.
Jeff Terrell: No justice kind of cool and different testing runners will do different things that might be similar to just but I kind of like, just because I can actually interact with it as it's running. I can change filters, about which tests. It's actually run
Jeff Terrell: can filter by a file name pattern and I can filter by the test name pattern.
Jeff Terrell: I can filter to only run failed tests or only to run tests related to change files. So there's a nice little set of filters that I can use by just kind of looking at their heads up help that sound that just displays.
Jeff Terrell: Okay, so that's adding just but there's I'm not actually using just yet because I don't have any tests. So let's go to the next commit
Jeff Terrell: Alright, so now we have a test for the standard deviation function.
Jeff Terrell: All right, let's see.
Jeff Terrell: No tests found, I think the reason for this is because I created a new back commit created a new directory for the test directory
Jeff Terrell: And just isn't smart enough to notice when a director gets created.
Jeff Terrell: Or at least that top level directory. So I'm going to rerun restart the MPM Test, test process.
Jeff Terrell: When I hit a to run all of my tests.
Jeff Terrell: And there we go. Alright, so we have a past test.
Jeff Terrell: If we want to we could actually change this.
Jeff Terrell: Here, we could say this is supposed to equal three. And now we can see what it looks like if the test fails.
Jeff Terrell: And a lot of test runners are going to do this kind of thing you know the the phrase that we see an agile development ally is red, green refactor and a lot of test runners will take that literally and make the output red if your tests fail and make it green. If your tests pass
Jeff Terrell: And so notice the justice seeing my file saves and responding to them rerunning the test. Whenever I changed that file.
Jeff Terrell: If we did get a failure. Notice that justice going to tell me what was expected. But what was actually received and where the failure is so that can kind of pinpoint. Oh, okay. What is, what is it, what's going on.
Okay.
Jeff Terrell: Questions about what we're doing. So far I'm going to keep going but
Jeff Terrell: As I said, Sorry, I just saw to tie in East. He said, I think node 14 supports yes six that might be right. I have some vague memory of import not working in a note environment, unless you had Babel installed, but I didn't actually test it. So it could be that that actually works fine.
Okay.
Jeff Terrell: Let's look at next commit which check out see for be
Jeff Terrell: All right this time, just notice star changed as soon as our change as soon as we checked out the new commit
Jeff Terrell: Let's see what this command does
Jeff Terrell: We said we we broke this some function to illustrate the test error message.
Jeff Terrell: All right.
Jeff Terrell: And for some reason, I don't understand why this is actually valid Unicode just like what is presented here, but for some reason in the get log output. It doesn't display that right. I don't know why.
Jeff Terrell: Might be a problem with my terminal or something, for all I know. But anyway, um, what I changed was I just right. I just messed up the the bound on the for loop.
Jeff Terrell: So I started from index one instead of an X zero on my son function. But even though the bug is in the some function. Notice that the failure message that I get is in the standard deviation function.
Jeff Terrell: And so if I wanted to understand where this bug wise I mean obviously just looking at the death of the commit is a good way to do that. And that's not cheating. Actually, if you encounter a bug.
Jeff Terrell: And it was introduced, only in the last change or set of changes and you should totally cheat because it's not cheating and look at the diff to see what changed him a clue you into where the bug is
Jeff Terrell: But let's assume that's not the case. And let's assume that we've somehow can't do that. Maybe this bug has been around for a while. We don't know how long.
Jeff Terrell: To understand where this bug is we would have to
Jeff Terrell: Look in all of our source code to figure out where the bug is and there's not a ton of code here. But there's still enough that it is a little bit tricky to figure out what bug might be, you know, is it in mean well let's go look at me and see if that's right, I don't know. Is that right,
Jeff Terrell: Oh wait. That was some. Is it is it in mean this is this is mean well being call some some right. I don't know.
Jeff Terrell: Let's go look at this is obsessed standard deviation. Sorry, is some right it's like you're on this code. I don't know. And so you see if you're if you're actually just looking at all the code and trying to understand what it does.
Jeff Terrell: Even something as short as this can be a little bit of a challenge. So the standard deviation test that we have here.
Jeff Terrell: It's sort of covering all of the code at standard deviation or under it, which is in terms of the actual bodies of functions. It's what five lines seven lines.
Jeff Terrell: 12 lines of code.
Jeff Terrell: All captured by this one thing so
Jeff Terrell: So, yeah. All right. I think I've beat that into the ground enough. By the way, this is what an actual test looks like in just a lot of
Jeff Terrell: test runners and test frameworks are going to have a similar sort of style or language to this. I prefer this kind where you're describing some. Noun
Jeff Terrell: And your listing the behaviors that it has by saying it does this, it does at
Jeff Terrell: So,
Jeff Terrell: I sort of look out for, for that style of testing this kind of loosely called Behavior Driven Development because you're thinking about the behaviors of some function or some object or class.
Jeff Terrell: And then you actually have the assertion that you make you expect this to equal to
Jeff Terrell: Your calling the function and making an expectation stating an expectation about what the return value should be
Jeff Terrell: I don't care that you know all the details here. I just want you to get a sense of what this might look like.
Jeff Terrell: That that can vary based on language and framework.
Jeff Terrell: Questions so far.
Jeff Terrell: Alright, so now we see what if a test error message looks like. Let's go to the next commit
Jeff Terrell: Alright.
Jeff Terrell: So the thing I was going to do next was start to add some tests for the modules and functions that standard deviation depends on so that I can have separate tests for those and get a better sense of where the failure is
Jeff Terrell: And the way I did that actually
Jeff Terrell: I created a new test slash some tests JS file. By the way, just to fix if anything with a dot test JS as a test thing to run
Jeff Terrell: So,
Jeff Terrell: But what I did here was I'm I said I'm going to describe the some function. And right now I'm just going to add some pending tests. These are tests that I haven't actually implemented yet, but this is a way for me to
Jeff Terrell: Be returns zero anyway this is this is a way for me to list the behaviors of the function that I care about.
Jeff Terrell: And
Jeff Terrell: So let's actually look at that. Let's say filter by file name test. This is p filter by filename rejects pattern. I'm going to say the file name rejects patterns. Some and now I get output that looks like this. I think this is a pencil icon. It looks a little bit weird.
Jeff Terrell: But yeah, the pencil icon is just indicating these tests don't really pass or fail. We're going to say that this some test JS Test file passes, because nothing and it fails, but you can see in purple. We've got three tests to do
Jeff Terrell: I actually really like this way of going even before I've written the some function. If you're, if I'm doing a test driven development style or at the tests first. And I'll start writing code like this test out to do this test to do that I'll list out all the behaviors of the functions.
Jeff Terrell: So that I can keep my thinking at a high level, as I'm listening to behaviors and not descend into the details of how to actually implement these things.
Jeff Terrell: So I sort of brain dump all of the behaviors and get that out in a file. And that's actually my to do list to implement each of these tests and once I get that done, then I can start writing the functionality.
Jeff Terrell: Okay, I'm gonna go back and run all the tests. I still have my failed standard deviation function.
Jeff Terrell: Let's go back here get check out oh four seat to the next one. Now, we actually implemented the pending tests for some
Jeff Terrell: All right. And here we had
Jeff Terrell: To test suites failed. That's like test files and three out of four tests failed. So some of these tests that we added in.
Jeff Terrell: Our some tests themselves broke, which is a good indication that our, our error might be in some
Okay.
Jeff Terrell: Stop me if you have any questions or anything is unclear.
Jeff Terrell: But we see all of these failures here in our just window.
Okay.
Jeff Terrell: What was the next one.
Jeff Terrell: Doing that enough. I'm just gonna say real quick.
Jeff Terrell: Check out at one, one. This is a fix for the bug that we introduced with this some function. So now all of our tests pass
Jeff Terrell: That's kind of nice. Now we get everything green green is happy.
Jeff Terrell: We feel good about that.
Jeff Terrell: Alright, what is this commit. Now we're adding tests for me.
Jeff Terrell: And so the behaviors that we want to describe for me. Is it just should be returns it returns undefined given an empty array. And notice, we can kind of read this as English
Jeff Terrell: described to me in function, it returns undefined given an empty array, it returns the only item one, item array.
Jeff Terrell: Yet returns the mean if multiple numbers can just kind of read that. So it's a pretty readable bit of code there. And then we have the implementation for each of those given an empty array, array return undefined.
Jeff Terrell: You can see how this works. So this is pretty readable, both at the high level when you're thinking about the behaviors, but also at the implementation level when you're talking about how those behaviors are tested.
Jeff Terrell: Anyway, now we have two past sweets and seven past tests were green. We're good. We're happy.
Jeff Terrell: We could refactor at this point now that we're green if we wanted to, but then the code is actually fairly simple already
Jeff Terrell: Know, I see no questions so far. So I'll keep moving.
Jeff Terrell: All right, I'm going to break some again.
Jeff Terrell: In fact, I'm not going to reveal how that's broken
Jeff Terrell: Trust me, that I really broke some and nothing else. And notice that that had devastating effects on my test passing rate now six out of seven of my tests failed, only one of them actually passed.
Jeff Terrell: So I introduced one small bug in some and it affected everything that kind of rippled out from there. And that makes sense, right, because our tests our integration tests when we look at
Jeff Terrell: Mean, for example.
Jeff Terrell: We're just calling me
Jeff Terrell: And whatever mean does mean does
Jeff Terrell: What does mean do
Jeff Terrell: Well, it calls some
Jeff Terrell: So this is an example of an integration test because we are integrating both mean and some together we're letting some we're letting me and call some like it would naturally
Jeff Terrell: And we're not interrupting that with a test double. We are just letting it call it. So, therefore, our
Jeff Terrell: integration test every single one of these is an integration test integrating two units mean and some. These are not unit tests.
Jeff Terrell: Now it turns out some as a unit tests just because some doesn't depend on anything.
Jeff Terrell: Some has no require lines or import statements. It's not even using any built in functions all it's using is just basic JavaScript syntax.
Jeff Terrell: Some has no dependencies. Therefore,
Jeff Terrell: Sorry.
Jeff Terrell: Therefore, these, these test cases for some, are they have to be unit tests because there's nothing to integrate with we're just calling some and it's not depending on anything.
Jeff Terrell: Okay, so that's an example of the difference between unit tests AND integration tests, but here we see that mean you know the some of these tests for me and actually fail.
Jeff Terrell: For example, the immune function returns the only item of a one item array.
Jeff Terrell: That fails. We expected to get one. But we actually got 1.5 which is kind of weird. How did we get 1.5
Jeff Terrell: And if we just looked at this failure right here, we would not know whether that bug was in the main function or whether it was in something that the main function depends on
Jeff Terrell: Because it turns out mean depends on some
Jeff Terrell: We can see that I'm very top line. And so maybe the bugs in some, I mean we happen to know that it is because we saw the commit message, but, you know, pretend that you're just seeing this test failure and you're not sure where the bug is coming from.
Jeff Terrell: And the problem is even worse when we get to
Jeff Terrell: Standard deviation, because this depends on two things.
Jeff Terrell: And I mean, think about code that you've written some of the code that you've written and that I've written depend on like 20 things. And if there's a bug in an integrated test case for that function or that namespace.
Jeff Terrell: Where do you look to figure out where that bug actually lives. It could be in that namespace itself, or could be in any of the 20 dependencies.
Jeff Terrell: Or maybe even in like a dependency of a dependency on down the line. So that could actually be a huge amount of code to search through to figure out where this bug comes from
Jeff Terrell: And this is one of the downsides of integration tests.
Jeff Terrell: It's good. In some ways that we are testing so much code with so little code we you know we didn't need to write a lot of code to discover this bug.
Jeff Terrell: But it's bad, because the the bug that we did have is in a fairly low level function that's relied on by a lot of other things. And so that failure propagates out to everything that depends on it directly or indirectly.
Jeff Terrell: Right.
Jeff Terrell: So yeah, there's just not much isolation between these tests units were relying mainly on integration tests.
Jeff Terrell: The tests for some is sort of by default, a unit tests because some doesn't depend on anything else but the tests for me and for standard deviation, our integration tests. And so that means that that bug and some can can affect
Jeff Terrell: The integration tests for me and standard deviation
Jeff Terrell: Okay.
Jeff Terrell: So now we're using mocks. In the meantime, oops.
Jeff Terrell: Alright, so I'm going to show what this is, but basically what that means. What, what that means is that we
Jeff Terrell: Got our mean tests to pass and that bug is still present in the
Jeff Terrell: In the some function. We have not fixed that yet. But what we have done is we have isolated the mean test from the some function itself. And we did that using what's called a test double
Jeff Terrell: I'm using the word mock you're a mock is that kind of test several. There are several different ways that you can do test doubles.
Jeff Terrell: Like fakes or marks or stubs. But the point of a testable is that it's basically it's like a stunt double in a movie.
Jeff Terrell: If you've got a you know a really expensive actor. You don't want to subject that actor to harm or danger by making them do this dance. So you have a stunt double that will
Jeff Terrell: stand in for the actor and do the dangerous stuff for them. Maybe they wear a wig and there were certain clothes to make it sort of look like the actor from a distance, but
Jeff Terrell: But the stunt double is doing the thing as a stand in for the actor. And in the same way when we have a test double we are
Jeff Terrell: Basically inserting a stand in to replace the thing that we're actually depending on
Jeff Terrell: This is all about controlling dependencies in our tests.
Jeff Terrell: So let's look at how this actually works. Here's our mean test. And what we did is we added this line just mock. This is something that just supports out of the box with no additional dependencies.
Jeff Terrell: We say we want to mark the source code in this file, and then we can require that same file and get this out. And this is actually something that we can
Jeff Terrell: Play around with and
Jeff Terrell: Affect different effects in different ways. And what we're actually doing is in these cases where we're going to mock the return value of the some function. So when we call mean and we give it this array here mean of one
Jeff Terrell: We don't actually call some
Jeff Terrell: So we call me in here and mean does not actually call some because we have much the return value out. And so, Justice going to short circuit that called a some and just instead of actually calling some function. It's going to immediately return one.
Jeff Terrell: Okay. I hope that makes sense. That's a pretty important thing
Jeff Terrell: Likewise, down here we have marked the whole module. And so in this test case here, we can say we want to mock the return value of some and make it returns six
Jeff Terrell: And then we can say we expect mean when called with these arguments to return to
Jeff Terrell: OK, so now, even if some is broken, which, in fact, in this case, it is our test can still pass if the code in mean is correct, regardless of whether the code in some is correct.
Jeff Terrell: We could take it a step further, which I haven't done here. And we can actually make assertions about how some was called, we could say, for example, expect some to be called with
Jeff Terrell: This argument.
Jeff Terrell: And if some hadn't been called correctly with that argument, then that would fail.
Jeff Terrell: We're not doing that here. But that is something you can do.
Jeff Terrell: Something else about this is we can actually write our code and our tests, top down when we use this kind of test devil style, so we could have started with standard deviation before we even thought about some or mean and we could have just assumed the existence of some and mean
Jeff Terrell: And
Jeff Terrell: Used to test doubles and marks for those functions in our tests. And so in our standard deviation function even before we wrote mean or some we could have said.
Jeff Terrell: Standard deviation should compute do the right computation in itself, and we will assume the existence and mark the existence of a salmon. I mean function. And this is actually a really good way to get the perspective of the collar and keep your code simple
Jeff Terrell: Okay, so let's do the same thing.
Jeff Terrell: In our standard deviation function.
Jeff Terrell: So with our single standard deviation test.
Jeff Terrell: We are going to mock the return value of mean to be three.
Jeff Terrell: And that's given this input here. The mean is three, we're going to mock the return value of some to be eight.
Jeff Terrell: And that's actually correct because the, the sum is the sum of square differences from the mean.
Jeff Terrell: And so one is two different from the mean we square that to get 430 different from the mean that 05 is two different from the mean we square that to get for again for plus zero plus four is eight, so
Jeff Terrell: This requires some knowledge of how the standard deviation function is actually working in order to figure out what the return value from some should be
Jeff Terrell: This is a reason that some people don't like to do this kind of testing style because you have to kind of turn your implementation and sign out and have a lot of knowledge about that implementation those implementation details in your, your
Jeff Terrell: Tests that use marks and test doubles. But, but that is something that we can do.
Jeff Terrell: Alright, so we might these two things. And now check it out our standard deviation test passes.
Jeff Terrell: See that
Jeff Terrell: So now two out of three test suites pass. And in fact, the only one that fails is some
Jeff Terrell: And so now that we have is used to test doubles in both mean and standard deviation test suites.
Jeff Terrell: We can have a smoking gun pointing to some if we encountered this test output in our in a real project and we were using these isolated tests with test doubles. Then we pretty quickly be able to tell where the error actually is.
Jeff Terrell: In effect what we've done.
Jeff Terrell: Is we have changed the standard deviation test, it's no longer an integration test. Now it's a unit test. And this is one of the things that test doubles, let us do
Jeff Terrell: Is convert our integration tests where we have a function that actually depends on other things in the unit tests where we're only testing the thing under test.
Jeff Terrell: We're only testing that standard deviation in this case and we are short circuiting any dependencies and hard coding what the return value should be so that those things are not actually exercised.
Jeff Terrell: Okay, so that's kind of neat.
Jeff Terrell: One of the side benefits of that is that we have a test case a test suite that runs faster because it doesn't need to descend into the other functions and have those execute. They just short short circuits and returns value right away.
Jeff Terrell: So that's kind of cool. And the downside is that you like I said, have a lot more knowledge about the implementation of standard deviation in your test horse before we didn't need that that knowledge.
Jeff Terrell: We didn't need to care that Sarah deviation dependent on other things.
Jeff Terrell: We could just make an expectation and treat Sarah deviation as a black box.
Jeff Terrell: No questions so far. I hope I'm hoping you're able to follow along. All right.
Jeff Terrell: Tell me to go slower if I need to go slower or ask questions. If you're not sure what's going on. Or if you're confused about something.
Jeff Terrell: Okay, when we fix the some bug, we get back to a green state. Alright, so now everything's green. Again, we're happy. Turns out, this was the bunk that we introduced just added some extra stuff on to what we returned from the some function and that of course caused all kinds of problems.
Jeff Terrell: But that sort of takeaway from this is to notice that
Jeff Terrell: With test doubles, we can isolate even the test. Even the
Jeff Terrell: tests that are calling functions that have dependencies we can insert a devil
Jeff Terrell: And have that standard in place and the dependencies, so that we can convert our integration tests in the unit tests. And if we do that, we get better test output in a way because a failure is gonna point more correctly to where the error actually is.
Jeff Terrell: But at the cost of maybe knowing more about our implementation than we would have to otherwise.
Jeff Terrell: Okay, now we're adding a top level scripts to run the program.
Jeff Terrell: If we say node run JS, we can tell whether the program seems to work.
Jeff Terrell: And basically, we're kind of running the same thing. Now that we have isolated unit tests with test doubles.
Jeff Terrell: This is actually integrating everything together again. And we could put this into another test, but I just decided, in this case, we're going to have a separate function.
Jeff Terrell: Or a separate scripts that we can run that will call center deviation with the same arguments as we see in the test file. But in this case, there's no mocking active
Jeff Terrell: And we should get to and if we get that we say program seems to work. And we have a little checkmark. Otherwise, we have an x and we say program is broken.
Okay.
Jeff Terrell: So at the moment, Roundup jazz tells us the program seems to work. If we go back to
Jeff Terrell: Some and break it again. Then we can see that the program is now broken and some tests fail. Okay, so both run that jazz. And our test suite are telling us whether everything works or not.
Jeff Terrell: Now our tests week passes and run the run scripts passes as well.
Jeff Terrell: Okay.
Jeff Terrell: I added that to set up a an interesting situation here this run script is intended to be like your app actually running somewhere maybe in production, apart from what the tests say
Jeff Terrell: And it's supposed to be the final word on whether your code actually works or not.
Jeff Terrell: Independent of what the test suite says and I added that to so that I could demonstrate the failure of using the test devil approach.
Jeff Terrell: So check this out. Our, our test suite passes, but
Jeff Terrell: If we run run JS, then it reports that our program is broken.
Jeff Terrell: And there's nothing complicated here. This is just calling standard deviation 135 and it should be too, but it isn't. And so we say, you know, program is broken.
Yeah.
Jeff Terrell: So, and it turns out that the run script is correct and a test suite is is incorrect. And the reason why a test suite is incorrect is because
Jeff Terrell: Anyway,
Jeff Terrell: The issue here was
Jeff Terrell: That we changed the interface of some so check out what we did some is now a function that instead of receiving an array and iterating over the elements in that array.
Jeff Terrell: It just iterating over the arguments directly. So now we just give it a function. We have some number of arguments and we're just going to iterate through each one of those and add each one of them to the accumulator which we then return at the end of the function. So our
Jeff Terrell: You can see the difference here and how we call it by rather than passing in an empty array we just pass it zero arguments in this case.
Jeff Terrell: And rather than passing it and a re containing the number one we just pass it the number one as its own argument directly
Jeff Terrell: Or if you want to pass multiple arguments. Multiple numbers. We don't pass them in an array we just pass them directly. So that's the change that I made the sun.
Jeff Terrell: But the problem is
Jeff Terrell: Even though are some test suite. We updated them correctly to notice that the interface changed. We didn't notice.
Jeff Terrell: That anything else needed to change, but everything that called some needed to change right every, every call to some needs to strip off that surrounding array, the surrounding brackets and just pass each of those numbers as an argument itself.
Jeff Terrell: That, by the way we if you needed to do that in JavaScript, you would need to use the apply method on a function
Jeff Terrell: But anyway, the point is that some and some tests are the only thing that changed here are our standard deviation tests and our mean tests still pass. They are still green
Jeff Terrell: So this is a this is a failure.
Jeff Terrell: The problem is basically that we have test coverage for, like, for example, mean here we have test coverage for the main function.
Jeff Terrell: We have test coverage for the some function. But when mean calls the some function. The calling the way that it's called that function is where the bug actually lives.
Jeff Terrell: Hopefully that makes sense. So in other words, when we have these individual unit tests, we are not testing any of the glue between the functions. In other words, how one function calls another one.
Jeff Terrell: We've opted out of that when we tested when we inserted all the test devils and and we're not actually calling the functions themselves.
Jeff Terrell: So,
Jeff Terrell: Let me mention a couple things about this.
Jeff Terrell: There's a few things that I tried to do to trigger this kind of a like demonstrate this kind of a failure. First, I tried to rename Sunday jazz to add that jazz.
Jeff Terrell: But that didn't work because the the test was still broken. In that case, because the code couldn't actually find some digests and so it through an exception. And so we actually got a test failure in that case and the test did its job to say something's broken here.
Jeff Terrell: I also tried changing the name of the function from some to add, but that didn't work either. For the same reason. So in that case, the tests slash the compiler was enough to indicate that there really wasn't error and notify me about that.
Jeff Terrell: And depending on your language and static type system. You might even find
Jeff Terrell: The kind of bug that we introduced here when you ran your tests. So like TypeScript. For example, could have contests. If we were using types on this code.
Jeff Terrell: So,
Jeff Terrell: That's that's worth thinking about.
Jeff Terrell: I'm also in here's, here's the main thing that I think is really important if you have unit tests as the bulk of your test weight in your code base.
Jeff Terrell: But then you just have a few integration tests, maybe even high level integration tests or ED tests.
Jeff Terrell: Then that's often enough to catch these errors that live in the cracks between functions. So what I generally try to do is to make most of my tests. Unit tests and use marks and stubs and test devils when I when I need to try to use them everywhere.
Jeff Terrell: But then I'll also sprinkle a few integration tests or maybe an end to end test or two around
Jeff Terrell: I'm not trying to test all the variation in those high level tests. I'm testing the radiation, where it lives. But I do want to just have a few integration tests to notice when there's a bug that lives in the cracks.
Jeff Terrell: Alright, I think I'm not gonna have time to cover tips for testing or maybe I'll try to cover them quickly. But let me demonstrate.
Jeff Terrell: Right. So I changed that back. Now if we
Jeff Terrell: Run out and run scripts that actually works.
Jeff Terrell: And let me show you. Dependency injection
Jeff Terrell: So we could do dependency injection for are some function. For example, because some is a dependency.
Jeff Terrell: What dependency injection does is it says, instead of just depending on the some function and calling it directly.
Jeff Terrell: Make your collar pass that function in to you as an argument and then you can call whatever it is they passed it. And presumably, that's the some function, but it doesn't have to be in. So that makes things easy for testing to decouple things and to have some control over the dependency.
Jeff Terrell: At runtime.
Jeff Terrell: For something like some, it doesn't really make sense to use dependency injection for that.
Jeff Terrell: But for something like sending email. THAT'S WHERE OKAY, YEAH, MAYBE I DO WANT TO DO dependency injection for this. And it's worth the trouble that I've added to the color.
Jeff Terrell: Because we don't want to actually be sending emails in a test run. We don't want to have to send an email every single time we we run our test suite.
Jeff Terrell: But let's say we wanted to send an email, you know, this crazy person wanted to get an email every time somebody actually computed a standard deviation to know what input and output work.
Jeff Terrell: And so presume that we have some sort of Cindy malfunction that works with this, we can implement that separately. I'm not going to go into that how that would actually work, but just pretend that this is a real thing.
Jeff Terrell: We're saying if we actually got to send the email function here and not just know that we're going to call it with this, then our test can just say,
Jeff Terrell: It needs to send an email with the results. So in this case we can have a fake sentence. Send Email function that we pass in and all that does is assign whatever we get to some variable that we've declared here.
Jeff Terrell: And we pass that in and then we can just call it make expectations on what the values in sent email should be
Jeff Terrell: So all of a sudden we have this complicated you know effect where it's actually sending an email, but we can use dependency injection to
Jeff Terrell: skip all of that effect and have a really simple test, right. This test is really simple. It runs fast. There's nothing complex about it. There are no effects.
Jeff Terrell: And so this is a really nice way to handle some of the more complicated effects that you might have in your code base and still be able to test them.
Jeff Terrell: And I think that brings us to the end of that. Let me go back to
Jeff Terrell: The slides, real quick.
Jeff Terrell: And I am really running out of time. I lost track a little bit. Alright, so what we saw in that was that.
Jeff Terrell: We had a testable. Specifically, we had mocks as what we were using your
Jeff Terrell: Test devils are basically like stunt doubles in a movie they let you convert a an integration tests where you have some code that has a dependency into a unit test where you have short circuited that dependency and replace it was something that you control on your chest.
Jeff Terrell: A downside of integration tests is that they can be noisy. If there's a bug in something that's dependent upon but
Jeff Terrell: They can help you point directly to where the broken code is that sorry testicles can help you point directly to where the broken code is
Jeff Terrell: The downside of festivals, is that if the code is in the cracks between two functions just in one function calls another your test devil is not going to help you find that
Jeff Terrell: All right. Write unit tests can run faster and create tighter feedback loops, because they short circuit the dependent upon units.
Jeff Terrell: And finally dependency injection gives callers including the test suite which call the function control over the dependency at the cost of increased complexity and the color.
Jeff Terrell: I'm going to try to go through this quickly. I think it's worth it.
Jeff Terrell: By the way, this is a subject that I spent a significant amount of time. Last semester talking about. There's a lot of depth here.
Jeff Terrell: I would encourage you to if you weren't one of my students last semester, any way to think about some of these things as you're figuring out
Jeff Terrell: How to test your code and how to write code. But basically, the most important source of complexity in your code and in your tests is effects which could include network requests database rights file system rights standard. I know.
Jeff Terrell: All of these things. Anytime you have a statement that doesn't contribute to a return value. That's a good indication that you have an effect.
Jeff Terrell: And so my first tip here is to isolate your effects into single purpose specific functions. So for example, if you needed to save a post to a database.
Jeff Terrell: Don't put that in the middle of a larger function that's doing other things, pull that out into a separate function that your outer function would depend on and then you can stop that you can you can make a mock for that are testable for that.
Jeff Terrell: And then in testing, you don't have to worry about managing that effect or setting up a database and all that kind of stuff you can just use a mock
Jeff Terrell: The only alternative here is to reset the database after every single test and some code bases actually do this. It tends to make your code base run long and it's just kind of ugly, but it does work.
Jeff Terrell: second and final tip before I'll open up the quiz here is that
Jeff Terrell: These effects what they actually do is they change some shared state of the program.
Jeff Terrell: So, or at least that's one of the things they can do so they could change the file system or the database that various other parts of the code depend on. And what that means is that any function that reads from that shared state is non deterministic.
Jeff Terrell: Another other words you might get one result one time and a different result. Another time you call the function, even if you're using the same arguments, because there's this implicit dependency of the shared state that's being read.
Jeff Terrell: And so I second tip is to use dependency injection. When you must read from the shared state.
Jeff Terrell: And make the collar actually read from the shared state so that your function itself can be deterministic and that that tends to make it a lot easier to
Jeff Terrell: To task. Enter right and to reason about. So for example, if you have a function that accepts the file path or maybe a file handle
Jeff Terrell: And then does some operations and reading on that file handle instead pass the file contents make the collar read everything that it needs to read from that file handle or from that file path.
Jeff Terrell: And then pass the contents into the function itself and then the function itself can be deterministic and pure
Jeff Terrell: It does make the color more complex, but then the function can remain deterministic. And if you have callers that
Jeff Terrell: You know all they're doing is calling and doing the same kind of effect handling in the beginning.
Jeff Terrell: It turns out you might develop a trusted library of really simple effect only functions. For example, enclosure. There's a function called slurp. They just read the contents of the file into a string.
Jeff Terrell: So you can kind of learn and develop a library of those kinds of functions.
Jeff Terrell: But yeah, see testing is easier here too, because the test. I'll have to mess with the file system at all. They just pass in different strings.
Jeff Terrell: Now it might not always work. Maybe the files too big to read into memory. There are things that can work, even in those cases that are a little bit more complex like lazy sequences, but
Jeff Terrell: Anyway, those are my tips. I'm sorry. I had to rush through that really quickly. But I do want to be fair to you and give you 15 minutes for quiz five. This is actually going to be on Open Class rather than
Jeff Terrell: A sekai. This is an experiment for me to try to use something that's a
Jeff Terrell: I think is a little bit more modern okay so i just published it. I will be hanging around here in class in the zoom meeting. I'll be watching the chat.
Jeff Terrell: To see if there are any issues and I'll respond in chat. Also, keep an eye on PR during the quiz, when to stop recording and mute myself. But yeah, feel free to start taking that quiz and you can leave when you're done.