 Hi, my name is Brenda Jinn. I'm going to talk about jasmine testing for backbone models and views. If you'd like to follow along, the slides are right here, and you'll be able to get them after this presentation as well. And they should work on your phone, so you can use your 4G LTE. And if you want to tweet at me, I'm cyberneticlove on Twitter. So for this presentation, I'm going to do an overview of jasmine testing real quick, just to talk about some of the basic structure and matchers. And then I'll talk about testing models and then build on top of that to talk about testing views. And the thing that inspired this presentation was that when I started using jasmine, I had a lot of questions about how I could use jasmine with backbone and specifically to test things that were related to UI, because a lot of examples that I saw were super functional and also abstract. So this presentation shows a lot of the things that I've learned about applying jasmine to the way that backbone apps are built. And also, I'm very passionate about this topic because I co-teach for Girl Develop It with Pamela Fox, who's spoken here before. And we've redone our backbone curriculum to use jasmine for all of our exercises. And the students love it. They find that it's really helpful to be able to step through things and complete tasks one by one. And the TAs love it because it's really easy to see where mistakes are being made. So it's been really helpful in our curriculum in teaching, and I think it's really helpful for teamwork as well. So a little bit about the motivations behind testing. How about a show of hands for who uses testing? Oh, nice. And then how many of you use jasmine? Okay, awesome. So this might be super familiar to everyone here. The reason that I think we need to test is that ultimately, code is for humans and so are the bugs. A computer doesn't know when you've done something wrong or when you've done something right. The only person that thinks you've done something, well, the only thing that thinks you've done something wrong or right is a human, because what makes something wrong is that an expectation is not met. And only humans can have expectations. So jasmine helps us set expectations and then check that they're being met. But also, using this technique, we're able to think through the steps of our code before we write the code. And that helps us break things down into pieces that are discreet and testable. And that, in turn, encourages modularity and stability for our entire code base because it's easier than, because the pieces can be broken down to swap things in and out. And we also know exactly where and when things are failing. And then finally, because of all of that, it means that upgrades are easier to do and so is deprecating certain chunks of code. So brief overview of jasmine. There's describe it and expect blocks. So suites and specs are written with strings in plain English and they describe exactly what's going on to the human developer user. And this is great because it also runs in the browser. So you can do some basic DOM scaffolding and testing right where the tests are being run. So an example in your spec file might be that you want to test for a basic backbone model. So the describe block starts a suite and then here's the first spec. So the backbone model can have a custom method called calculate age. And then you might use, you might instantiate the new model right here and then you can expect a certain method to be defined. And then in your source file, you would just make sure you define your model and that the method existed. But what's really useful about jasmine is that you also have before each and after each. And the way I like to think about each spec is that it's almost like a stage. So have any of you done performing arts or theater or anything? Okay, a couple people. So before each scene, you need to set the stage. All the props need to be on the stage. All the players need to be ready at the right entrances. And then when the scene opens or when the spec runs, everything needs to execute in a certain order. And then when everything's done, the props need to be taken away. The players leave. So then we tear down the stage. An example of this is this suite with a before each block. So in the before each block, I'm actually instantiating a new model. In the entire suite, I might want to be testing the same model or I might have nested suites with different models that I want to test for different circumstances. But in this case, it's pretty straightforward for an abstract example. And I just want to create this new store with the year founded, 1858. And then in every spec after that, I have access to that model and I can just create expectations based on that model over and over. And it's fresh every time. So if you want to follow along, there's an example zip file that you can download with the spec runner and everything all set up, or you can just watch. So let's talk about testing models. When I test models, there's a couple of steps that I almost always follow. And the first one is setting up and instantiating, which I showed you a little bit of. And then next I test for defaults and methods. And then after that, I test the initialization process and make sure the models are correct when they're instantiated. After all of that, I test for control flow and the more complicated parts of the logic. Like if function A is supposed to call function X, but only if B is true. And then finally, this is the more advanced part that a lot of people have questions about and the number four and five are the ones I don't see a lot of testing around in general. And it's so control flow and then events and callbacks. Cool. So to set up and instantiate, we define a variable in a scope that makes sense for the specs. And then we can either instantiate per spec or in a before each block. So in this case, it's a very familiar setup and I'm actually gonna do a more in-depth model here. And then to test defaults, I just make sure that the newly instantiated model has the correct properties. You could also do this with an empty model. So you could define a model with defaults and then just expect Macy's to have the correct defaults if you don't pass in any properties. So let's go ahead and take a look first at the spec runner with ten failing specs. These are the exact same specs that I'm going to show you in the slide. So a test for a custom backbone model can have default properties such as brand name, website you're founded, and is department store. So if we go and look at this, here's something very familiar. And then here are the tests we just talked about. So let's go ahead and make those pass. So it looks like we're going to need a store model. And we need to give it some defaults. And let's see, it should have a brand name. And then it should have a website. And then it looks like it needs to have a year founded. And then it should also have an is department store. Cool, so if we go back, we run those. The first test is passing. Great, so then for all of our methods and our custom methods, I like to make sure that first they exist. It's troubleshooting 101, which I actually find really helpful. I've been to hackathons where people have plugged in their laptops. And then they're like, oh my god, it's not green. And then I ask, well, is it plugged in? And then we check. And sure enough, the search protector is not plugged in. So troubleshooting 101, make sure it exists. And then after that, make sure they do the right thing. So we want this method calculate age to be defined. And then at the end, we want to make sure that does the right thing. So we want to make sure that it gets the age of the store. And that should be the current date minus the current year minus the year founded. Cool, so that is showing up as this one. Test for a custom backbone model can have a custom method called calculate age. So let's go ahead and take a look. Let's start with this one. Calculate age should be defined. Let's see if that works. We got one more, and we did. And we'll set the age after calculate age is called. And let's get the year founded. And then let's set it. And let's see if that worked. And it did. Very cool. So now we've tested to make sure that our custom method exists and that the outcome is correct. Then I like to check the initialization process. So making sure that every new model that's set up is correct. And a great tool for this is the spies. So we can spy on the method, calculate age, and then force initialize to run. And then make sure that that function has been called. So here in the spy on syntax, we have what we're spying on, the object that we're spying on, and then the method. And then again, letting the players play their parts and then testing our expectations down here. So if we go here, test for a custom backbone model can make sure that calculate age is called when instantiated. Let's see. We'll have to use our, did I spell that right? Yep. And did that work? And it did. So these are pretty abstract examples, but they can be applied over and over to something much larger than this. Finally, when we talk about control flow, we can use spies again to make sure that in a certain circumstance, we will get outcome A. Or in a certain other circumstance, we could get outcome B. So in this example, maybe if is department store is true, then I want to go ahead and fetch all the department store locations. So I'm going to spy on fetch departments, and then I'm going to run initialize. And because in our, because in before each, we instantiated a model where department store, is department store is true, then we expect it to have been called. And then I'll set it to false, initialize it again, and I expect the calls count to remain at one. There's a bunch of different ways to do this. You could have done it in two different specs. You could have instantiated two different models. It's totally up to you how you want to do, how you want to check for this. But the point is that you're checking for one outcome for one situation, and a different outcome for a different situation. So in that case, we need to set some logic. Oh, actually let's look at the test. Custom method called fetch department. So let's do that first. I guess that's from the wrapping. That's fine. Make sure that passes, and it does. And then it should be called when instantiated, but only if its department store is true. Let's take a look here. It's the same exact test as we were looking at. So in initialize, if, then we will call, let's see if that worked, and it did. So we know that in this very simple example with two possible outcomes, that we're getting the correct one each time. And this becomes more and more complicated if you have three or four potential outcomes, or if your logic is chained in three or more steps. So finally, for models, I like to test events in their callbacks, and this is particularly useful for custom events that are happening. So you can make sure that they're triggered correctly using spies, and you can set event listeners right in the test. In this case, I made a fake object with a fake callback, and I'm actually gonna spy on it. And then I'm gonna listen for a certain custom event on the model, and make sure that the fake callback was called. And it looks like I'm gonna trigger the custom event through this trigger custom event function, and then I will expect my fake callback to be called. So if we take a look at the test backbone, it's can test model events using spies in the spec. We'll take a look over here, and it's that same test. So let's see, what do we have to do to make that happen? We have to create a function called trigger custom event, and then it should trigger custom event called custom events. So with your real spy in the real world, you can make sure that the correct data is being passed, that the context is correct, and these custom events would actually be much more complex than the one in the example, but you still use them nonetheless, and it's the same technique that's applied. Now I'm gonna talk a little bit about testing views. So for views, I test all the same things, custom methods, initialization, control flow, custom events, but there's a couple more things that I like to do for them. The first step is the same, and it's setting up and instantiating the view correctly. The next one is I check for DOM events in their callbacks, and I make sure that the callback exists and that the correct thing happens. I also check for interaction with the model and the collection, so interaction with the data, which I think is really important and where a lot of breakdowns can happen. And then finally I'll check for what I call functional DOM changes. So I don't test for things like CSS or styling or position. What I might check for is if this complex three-part template were rendered correctly and if the hierarchy of nodes is correct, given a certain situation, that becomes useful for things like nested list or elements with multiple containers inside. So setting up and instantiating, very similar to the model, except that you need to make sure that the model exists for your view. And then at the end, you can also tear down the view. And if you inserted anything in the DOM that you used for testing, it's also good to take them out because as I've seen on my team, people can just insert, insert, insert, insert, and then you have 200 tests running and also 200 images loading and not getting taken away. So for DOM events in their callbacks, I just check the events object and make sure that the events are defined correctly and so are their callbacks. So in this example, I'm looking for a click ULLI events and the correct callback to be click callback. If I look here on the view, test for a custom backbone view, can test for events like a ULLI click? We'll go over here to the spec. I expect that events object to be defined and so the first step would be defining the backbone view and it's supposed to be called store view and then it looks like it's supposed to get an events object with a click ULLI and click callback and then also a click callback function. Let's make sure that works and it does and now we're ready to go. So we would also test the functionality of that click callback of what happens inside and after a click callback. Then I would check for interaction with a model or a collection. I check for the views reaction to certain data changes. So maybe I'm supposed to call render every time, let's see, every time the model changes or every time a certain event happens. For that I'm gonna use a spy two on the store view and I'm also gonna use and call through in case I need to check the render functionality anywhere else in the suite so it's not going to disrupt those tests. So I'm going to after I instantiated in before each inside of this spec I'm going to change one of the properties and I expect the render function to have been called. So that is this test right here, test for reactions to model level events and this might also be a custom event on the model that the view has to react to. So we need a render function. Well, I guess we have one already so we could just do this in initialize. We can use listen to, change. Let's see if that worked and it does. So the last step would be testing for things like functional DOM changes. So checking for a change in structure to our view. This was really helpful for me when I had to make a three part nested navigation list and I had to make sure that the number of children were correct and then I was flattening the array correctly on the model but then that the view was rendering the correct number of children. So I would check the number of allies that were rendered based on the number of clicks at various levels in the list and also against the number of children in the model. So in this one I'm just gonna compare the old length and the new length. I'm going to use jQuery because this is running in the browser to append the view to the tests. I'm going to get the number of li elements. I'm going to trigger a click using jQuery and then I'm going to get the length again and I'll just expect this time that it's gonna add an li element every time that we click this view. And then at the end, this is a step you might do in after each but I'm gonna clean it all up and remove everything. So let's take a look at this last test, test for functional DOM changes. It's the same test that we talked about. Let's go ahead and make a UI append a UL to this. So that when it renders it has something to render to append to the body. And then in click callback, let's just add one more li. So in the real world, you'd probably have some template rendering happening with real data getting pulled in from the model but in this one I'm just gonna put empty nodes in here. Oh, thank you. Nope, right here, right here. Hold on, sorry. Whoops, forgot to return this. So that's why because I had chained dot L after render. Cool, it's going fast. Cool, so that's it for me. If you have any feedback or you wanna tweet at me, I'm cyberneticlove. Thank you so much for having me.