 All right, so if you're here doing test-driven development by example, when I put this talk together, I was sort of thinking, I couldn't do a talk if it wasn't a big announcement, like HTML bars or something that would blow away Tom and Yehuda. And then I thought, well, maybe there's other people at the conference who aren't working on the next rendering engine or something. And maybe they're just wanting to build web apps like I do. So hopefully this is for you. Before we get started, though, I want to show the app that we're going to build in the next 30 minutes. And it's a really simple con bond board. It has some items on the left that are unassigned. And then I can click this little blue item. And it will assign those to the assigned item on the right. And then I can drill in by clicking the name of any of these that will kind of show a details section. And those details are data bound. And the intent really is to just show how to get feedback with test-driven development. When I first started learning test-driven development, it was more about finding something that would prescribe success. And ultimately, TDD was not about that. So I kind of learned incorrectly how to do it, learned correctly, and then still sometimes learned incorrectly how to do it. I hope today just to pass along some of the information, some pro tips, maybe all of it's not that grand, but you'll pick up one small thing. And a lot of times when I was younger, I felt like when I came to these, it was the really small thing that I saw during a coding demo that was more interesting sometimes than the whole presentation or where the presentation was centered. So to get started, we're going to be using an Ember CLI app, of course, just to make this a whole lot easier. And we're going to start out just by generating an acceptance test to get started. And I'll call it Assignment. And usually I kind of start when I'm doing just line of business stuff, I'll kind of start with a high level test just to feel like I know where the client or the user, the end user is coming from. And in this particular example, we just have unassigned and assigned items. The simplest starting point really is do we have items that are unassigned or are they grouped together? And do we have assigned items that are grouped together? So we'll start out with the unassigned column. We'll basically find a table of unassigned and a TR. I have this little TR. I call it Cards. I don't know. Sometimes the Kanban boards, you call those Cards. And then also, we're just going to assert right now that there is a certain number of these in the DOM to kind of get us started. So we'll say the length of this is 3. And one little pro tip I want to throw out if you're ever doing any live coding, you might see it in the upper right corner, is the best way of success here for live coding is turn off Wi-Fi. Don't attempt to NPM install anything. You'll just instantly troll yourself. Guaranteed something's going to break. So I'm not going to NPM install anything up here today. All right. I am, however, going to be flipping. And I hope the monitors catch this. I'm going to be flipping between the live code and the test run. And if you're new to Ember, and you haven't seen this, you can actually do a test run by doing test dash dash server that will continually watch and then live update the test. So I'm going to start with our failing test. And at the very bottom there, you can see we expected three of these unassigned items. And one of my gripes with Testum is that, well, what's the other side? Well, it's technically undefined. There is just no entry in the DOM for this. So the first thing we're going to do is just jump into the router and actually define a route for these to-dos that I call them. And we're just going to work for simplicity in this demo at the root of the application. And that gives us access now to work away from the application handlebars template. So we'll just create one called to-dos. And we'll have a really simple table or something. Whoops, really a simple table. And I call this thing. I give this a class of unassigned. And then I, for some reason, decided cards was a good name for the TR. And we'll flip back. One of the things I want to point out here is, earlier in my journey for Testum development, I always thought red, green, or factor. I had to go from red to green. And then I would make some changes. But honestly, what I've found over the years is that I use the red over and over again to get feedback, even if it doesn't go directly from red to green. So in this particular instance, I'm showing that I'm on the right track because instead of three is undefined, I'm now getting three is not one. All right, so we know the answer to this. We're just going to do an each loop here and say each to-do in some kind of computed here, say unassigned. And if we're going to do that, we've got to obviously add a controller real quick. So we'll add a to-dos.js here. And we'll just basically do a very simple controller. And we'll create this unassigned property. And for right now, since I just need to pass it, kind of the simplest amount of code to pass this is to return an array with at least three items in it. And then we'll use another test to kind of drive the requirement of, what did these to-dos actually look like? So we have a passing build, no applause please, that was literally not worth it. So hold on, I'll have something better, I swear. So what we're gonna do now is we're gonna go get just the first property, which if you remember the final result, we basically have like a project name. So we'll just go get a project name from there. And I'm gonna do that just by going to the zeroed item essentially in the cards list here. And then I'll find a TD with something like to-do project in it. And then I'll say project.text should be something like first. We're just gonna, this is arbitrary data of course, we're just making this up. And we're getting of course a failing test, which is great, tells us where we need to start. Now if we looked at the handlebar side, the first thing I like to do just cause I've been burned by incorrect selectors so many times is I like to come in and just kind of put an incorrect value, so I'm putting an X there anyway. Just to make sure that I'm in the right track. Oftentimes I found myself doing multiple steps, only to find out I didn't have the right CSS selector at all. So this will help save you that painful moment hopefully. So we know we have an X, ideally we'd actually like to do something like to-do.project. And this is the failing test that we sort of wanted that last time around where if we're just returning a list of integers, there's no way we're gonna satisfy this. So let's just create some really simple sample data and we'll kind of iterate on this controller. So create a project with first, we'll just throw in some first, second, third data here. Third, and now of course we get a passing test cause we have the right data. This of course if we're looking at the controller, it's of course not the greatest controller yet, but we're gonna incrementally get there as we drive sort of the other side of this requirement. But before we go write a test, I wanna come back and look at the handlebars. Because the first thing I notice at the top of this is that we have a table and of course they're not trending on hacker news right now so our designer decides we're gonna change this to a divs or something like that and they come in here. And from the behavioral perspective with this refactor, there really shouldn't be anything that changes from the test, right? So if I change TRs and tables and change this TD to a span, the behavior of the web application is the same. Unfortunately, the tests don't reflect that. I should have had a clean refactor here. And I call this out because the tests I wrote on purpose were kind of like the early tests that I wrote when I was doing Ember testing or Selenium. And they were very structural or layout dependent. But in reality, these tests are about behavior. They don't care if I have a designer that wants to change it all back to tables or divs. So let's just come in here and remove anything specific to the layout. So I'm gonna remove anything like TD and of course remove table. And now we get a passing test. So if we wanna go change it back to tables, it'll still pass. It doesn't actually matter. And as a front end engineer, I'm just trying to get the behavior of the web application. I don't actually care. Excellent. So now we have sort of the happy path on the left side for unassigned. But we obviously need to finish the story because the Kanban board is not necessarily only unassigned. And also I noticed this terrible test name. I'm pair programming with 600 people and nobody caught that? Oh, come on. I'm just playing. Okay, so let's say assigned items are grouped together. And in this one in particular, I'll just basically replace unassigned with assigned. And the only thing I'm really gonna call out here is that you definitely want to have a different number when you're kind of just making up arbitrary things like I am. And in this case, I wanna make sure that there's only one assigned item when we get started. And then it's kind of a different number from the unassigned just to make sure I don't mix something up. And I do still get a failure that one is not undefined. So we know what needs to happen on the controller side is we need at least one item in here. So we'll create an assigned computed. And inside here, we're just gonna really return, again, fictitious data until we get to a point we can refactor it further. So we'll say last is some project with a different text again. So we'll say last. And then on the handlebar side, we need to basically copy this. And eventually you can imagine this gets extracted out as a component of some kind. All right, so I'm just gonna make one change here which is to not completely get the implementation right because I don't actually have a failing test there. And so now we have that piece of it passing. I'm not actually asserting this line yet, which should actually get us a failing test right now. So we're gonna do is switch back to our test code here and bring back kind of the final failing test for the unassigned assigned story. And that is if we go fetch a product from a sign that it should have the text of last, which should fail us right now. We have last is not x, which makes sense. So if we go back to the handlebars template here, basically just stub in to do project, now we have a passing test. Now we're at the point where on the controller side we can actually do the refactor to get us a little bit closer to a real Ember app. If you've been to an Ember, you're probably very disappointed in me right now. Like, Toren, this is not what we talked about. But honestly, we're gonna get to the point where this is just a model property. So let's incrementally move there just by actually adding this and kind of extracting out. And then we're gonna have to come up with some kind of property that I assume the backend, either I has a backend developer or someone else on the backend has decided will be the status code indicator. So in this case, we'll say we have something called status underscore code. And this status code will be the thing that determines if it's been assigned or unassigned on the backend. So now what we can do in here is actually just return this.mob or this.getModel.filter. And inside here we're just really gonna return if the model's status underscore code is a one. Cause in this case we just arbitrarily decided we'd do a one and then in this bottom case we'll just do a two. So, what's that? Sorry about that. Don't troll me. We'll get there. We'll get there. Yeah, somebody has actually already seen this talk. They just didn't want you guys to enjoy it. Sorry about that. All right. So I'm gonna rip the model out. It's obviously important. It's gonna break the world here. So the reason I ripped that model property out is we're gonna go down to routes and add a to do's.js. Just again getting us incrementally close closer to the final version of this. So I'll do ember from ember and export out a route here. And basically just paste this in verbatim right now. We're just trying to do the simplest thing to get us back to a passing build. And now we have a passing build. Stellar. And the next thing we're gonna do is just write a failing test that we can actually get the status from this. If you remember in the final app, I actually have kind of in gray and then in green here, the actual status sort of in plain English. And the challenge for us as we write this feature is right now it's just an enum or an integer value from the database. So we'll need to actually drive that out. So we'll say status is shown in plain English. That's how I got my writing test name responsibility revoked. So we'll do a slash here. And it's really gonna be sort of the start of this same test here. And the only difference is we're going to ask for the status itself. So first thing we're gonna do is write the failing test here. So we're gonna reach in to the assign the zeroed item because there only is one right now and ask for his status. And of course this is gonna give us a failing test here. Says there's nothing. And the first step really is to define the CSS selector in the handlebar side. So let's come into the assign side here. And I know this is gonna fail but I want a little bit more information. So I'm gonna put an X on the end just so you can see that it's still undefined. And now here's the challenge with purely acceptance testing. This acceptance testing is great to show the happy path flow from your route to your controller to your template maybe some model in there. What it's not necessarily the best at is some of this quicker feedback for conditionals and especially conditionals that have computed properties. So what I'm gonna do now is actually break from purely acceptance testing this and on purpose I'm gonna leave us a failing test here but I'm gonna break us out of that test run and actually go add a unit test that's just gonna drive the assigned transformation from status code to status. So we'll drop down to the test directory here and just do a to-do model test.js and I'll save a little boilerplate here by half cheating. We'll say status code of one is unassigned and we'll just do a subject here is to-do dot create adding a status code of one and we'll just do assert dot equal subject dot get status and this of course is unassigned. Now if I wanna run this test in isolation I have a module name at the top here just called to-do. One of the cool things if you didn't know about MRCLI when you're in this predicament that I'm in where I wanna run just one test. I know I have another failing test that's causing me some grief. I only run this one in particular. I can do a cool filter command here and just say filter down to just this particular modular test and I know I have an error here which says to-do is not defined which is kind of like our first failing test. So we're actually gonna import to-do from what will be in just a minute the Kanban models to-do and we need to actually go add that to the models directory here. We'll just say to-do dot js just export out a really simple model here and the only property we're gonna actually have of course is status and right now I'm just gonna return x because we didn't actually see a good failure. So now I'm seeing we expected unassigned we actually got x. Now really to make this first test pass I can just slam in assigned. I don't really have any conditional to drive here. So the next failing test will actually drive this out so we'll just kind of add another test here that says of course the other side is a status of two will be assigned. And this is kind of the test that gets us a little bit further to solving the acceptance failing acceptance test. So we get unassigned is not assigned. Imagine that we hard coded it. So we'll say status code is this I get status code and then right now if the status code is a one we will return unassigned otherwise we'll return assigned and that should get us a passing build and it does. All right so now the other reason that somebody called out computed earlier but another reason I like the unit test especially the conditional that's being driven from a computed property is so many people seem to miss the cache breaking component of computed property so what better way than to write a failing test for it? So what I'm gonna actually do is write that test that if we said subject.set status code just slam that into a two. I should get a failing test now and all we need to do is add status code and then we're back to essentially a really clean model that's tested. One of the other examples I wanna point out here from this unit test is sometimes I write tests and I get very emotionally attached. I don't know if you've had that experience and all seriousness and this bottom test I'm very emotionally attached to it because I had to live code this in front of all you guys. It was kind of a big deal. And so what I noticed on a couple lines up I'm actually testing that though right here and we have to be aware or cognizant every test is just as expensive or just as big of a burden as the production code that it maintains or it helps us maintain. So in this case I think it's acceptable for me to break that out. There's not any more than really two conditions right here anyway. So I'm gonna break that out and still feel good about myself, all right. And the next thing we need to do is I wanna go back to the non-filtered test run so I can get back to my failing test and that's in part why I leave a failing test so I don't forget about it and we can remember where we're at now. We had the acceptance test that supposedly had a status property but it doesn't seem to be hooked up for some reason. So if we go look at the route that's actually pushing down these objects you'll notice they're not the new to-do model so there's no way we'd actually have access to this status code. So I'll bring in the to-do model here and actually do a to-do dot create and now that I have all those created you'll see I'm getting assigned x should have actually pulled that out of the handlebars side but now once I pull that out of the handlebars side we should have a green test run which is what we have. So that's kind of illustrate in that example really that unit and functional or acceptance test they actually live together in harmony it's not one or the other. So hopefully that example kind of showed that off. All right so let's drop down and write another test here and this one would just say clicking the assign button will move item from unassigned to assigned and this test will really drive the computer that's lacking that it'll basically expose a bug that we have currently in the controller. All right so we're gonna visit again of course and one thing I like to show here that you might actually disagree with is I sometimes see the test as it tells a story and sometimes when I tell or I want to help tell the story better I will illustrate it a little more verbosely than I would in production code. So in this particular test I'm actually going to have like a pre-assert that when I start this test I have one assigned and then three unassigned items then if I click the unassigned cards equals zero basically the zeroed item in that list and then we'll have a new thing that we'll call the assign button here. That if I were to assign click this button and actually assign one the end result and the reason I say this is kind of like telling a story is I can see the before and after in the story here so I should go from one and three to two and two and this is actually gonna be cool because we're gonna see a couple of different ways I use the test to give me feedback. The first one is there is no assigned button found and it actually blows up on the test runner so we need to flip back into the handlebar side and just add a button here. Give it a class of assign button and then we're just rerunning the test knowing that we're not complete but we're gonna hopefully get a different error message meaning we're heading down the right direction we are so we're getting expected two and three have not been remapped. So the first thing is we're just gonna fire this action of assign off here and we're gonna pass along the to do that we have that we're currently looping over and in the controller we just need to actually add an actions block now and say assign is a function that's gonna give us it's do and of course the simplest thing to do in here is really just set that status code to a two for the moment. Now what's interesting is the tests are still failing and if I got this far and I wasn't the programmer who wrote the original computers I might be a little confused but as somebody called out earlier really what's missing here is the ability to break the cache when status code is updated here. So I just tell the computer properties when to break and now when you click that blue button it officially moves them from left to right which is cool. All right so one thing I just remembered is we have this hard coded model data and most times the very next progression here is to actually do some kind of Ajax call and get this data from the backend so let's just work towards that migration and figure out how we're gonna mock the XHR. The first thing I'll do, assuming I'm not using Ember data here for whatever reason is I'm just gonna get an array of to-dos and return getJSON say API to-dos and all we're gonna really do in the end is return these to-dos when this promise resolves or this deferred and what we're gonna do is loop over so do a for each here, get this data and all we're gonna do is say to-dos.push object to-do.create, pass in that data for now and basically simulating would probably do in a real app. Now of course I take that out, we'd expect the world would end on the test side and it does and now we need to do is just stub that. There's a whole bunch of libraries out there that let you stub this. There's Mockjax and Pretender. I'm actually just gonna use one called fojax. I won't be religious about it and I'll just say it's easy to use. So I'll say fojax.new and all I have to do is give it the API to-dos endpoint and the response text, whoops, and not create to-dos that would probably be a win. These actually need to be similar to what you'd send on your backend here which is just raw JSON and now it gets our test passing, which is awesome. So now we have more of what's a real app, our route is actually reaching out to some kind of backend and fetching this data and there's really only one test left or right and that is clicking the toggle link will show details for given item and this one we're just gonna start out by doing a visit course and this one actually is kind of interesting is I expect just to click this button so I'll go to the unassigned category again, cards equals zero and I'm gonna go to this toggle link thing that doesn't exist yet. It'll basically be a link to helper and then all we're gonna do in the very first assertion is just assert equal that the current URL has changed and we're gonna expect that to be something like to-do one because it just happens to be first in the list and the first failure we get just like earlier when we tried to do the button is the toggle link is not found at all so we'll go back to the handlebar side and just say okay, we'll add a link to here because we know it's gonna be an href and we'll probably do something like to-dos.to as the URL and pass along our friend to-do and then make sure we get our CSS selector right, we'll pass in the toggle link here and that is a link to. Now the very next error that actually confirms and lets me know that I've clicked this is that it blows up and says, hey, Toran, there's no route to-dos.to-do so which is great because the router hasn't been magically set up behind the scenes for me so we'll go actually add a nested route here and we'll just call it to-do. And give it a path of slash-to-do, slash-to-do underscore ID and now I should get a different error and notice the error here. It's actually something I wasn't expecting the first time I wrote this talk is I was expecting it just to work but of course there's no ID property so assuming on the back end we're actually gonna get an ID, we actually want to have probably some incrementing IDs so we have IDs one, two, three, four now and the test passes. Stiller. All right, that's not it. If we look at the production app here when I drill down it's great that first the URL has changed but you'll notice this entire column on the right, this kind of details column. Originally when you load the page, if I go back here, if I go back you load the page it's actually hidden so I wanted kind of a cool user interface that would just let me stay on the same URL but then show this details list so the first thing I'm wanting to actually do is say that there isn't, there is a details section but it's hidden by default so I'm gonna do that actually in a kind of prerequisite assert kind of like we talked about a little bit earlier and just say details is, we're gonna find something called details section and we're just gonna do an assert.ok that details is hidden because we expect it's hidden when we hit the page and of course we blow up the funny thing here is we're actually blowing up not because it's not hidden it's just not an element really at all so we're getting that truthy argument. So if I create a div here and give it a class of details section and then I'll just make it hidden by default just to kind of get us rolling here running out of time. The next thing I wanna do is the whole reason I set that up is in the end here I actually wanna say that once I toggle the URL, that it's not hidden. That's the whole point, right? So I can toggle this details link basically and you'll see well we haven't satisfied that because in the handlebar side the first thing we need to do is actually make this dynamic. So we'll do a bind adder and do a class where we always have details and then whether or not hidden is there is actually based off the is active route. So we'll say is this the active route and if we're gonna have this is active route we'll go back to the controller here and just add an is active route and we'll just do an ember computed equal and really what we're after here is current route name and if that is equal to to dos.index we know we're on the parent route. Unfortunately, current route name doesn't come for free. I actually need to hijack an application here and do controllers.application current route name. Once I wire that up the tests are passing. Awesome. And the very last piece that's gonna get us going here is that I also expect that there's a bound input so I'll just call this project input we'll basically find within the details section an input with the class of project and do an assert.equal that projects input.val is equal to first which just so happens to be the text is our model's first project. So now all we need to do to make this happen is go back to the handlebars add an outlet. Once I add the outlet I just need to add a parent template to dos that'll let me add to do.hbs once I have to do.hbs I can do an input class project. Oops, if I can add. And the value is simply model.project. But let's not drop the mic yet. So that's the functionality right, that's sweet. But what about how does it work? Does it really work? So we'll go out to our Embers CLI app here and here's the app. I have this little X link that when I click it changes the URL. It updates my bound property. I have these buttons that are not very pretty right now but they work. And I have the skeptical developer on my team that says, Toren, these tests are great but as soon as we have that designer come in it's over. So let's prove that guy wrong right now. So I'm gonna do Embers server here. And this is basically with only HTML changes. And I actually do work with a really great front end guy. He's not the pessimistic guy, that was fictitious but he's a really great guy. And let's just say he came in and designed this for me which I'm kind of a scumbag that way. I'm always like, hey, it's done. Would you make it look great? That'd be sweet. So you can come in and still click around. You can still click, get the details. The details are still bound. What about those tests? I think those tests still work.