Sessions is temporarily moving to YouTube, check out all our new videos here.

How To Start Loving Acceptance Tests in Ember.JS

Thiago Massa speaking at EmberFest in October, 2017
Great talks, fired to your inbox 👌
No junk, no spam, just great talks. Unsubscribe any time.

About this talk

Starting with Acceptance Testing in Ember can be a rocky start, but once mastered it yields many great opportunities, such as letting you write a feature before the backend is ready and giving you regression tests that run blazing fast. If Mirage ever made you pull your hair out or if you ever spent more than 15 minutes trying to find the correct way to match a certain element on the page, this talk is for you! We'll go through the pains of working with QUnit and Mirage, up to figuring out how use them at their full potential.


So how to start loving Acceptance Tests, when I begin with Ember, I had a lot of issues with testing, things in Ember just have different names from my previous platform, I do a lot of backend, I do Ember, React and so on, and then I came up with the idea I've just talked, so basically I had a lot of problems and then at some point I start really like it, now I like it doing better than let's say testing applications in Ruby or even other programming languages, I think it's pretty nice the environment that Ember just gives you, out of the box, and I also introduce ways of how can you also improve it, what Ember gives you. So who am I, I am Thiago from Brazil, now I feel like already as a Berliner I've been living here for almost three years, I really also like mechanical keyboards, I have two, I'm gonna buy my third one, I really like the clicky feeling that you get when you type on them, I have one life goal, one of them is to learn Haskell, and the other is to learn German but I don't know if I'll manage to do both in a lifetime. And I also work at Kloeckneri, it's the CO company that Mr. Flourin have talked about, yeah. And I have four stack engineers so I basically do Ruby, and also Ingber and perhaps even different languages so beginning with testing Ember can be a rough start. Sometimes you're just used from the previous platform that you have been working on so let's say mobile or maybe backend or even sometimes one thing I see that the angle I call acceptance test, different things, and for then it also means something that's a bit different so they call Ancuang or something, and you might be quite lost when you just begin doing this, and so basically there is like all the types of test that actually don't have the exact name of something, like unit test, it's always unit test but for integration, integration testing in Ember can be like component integration test so you have a directory called integration but it's actually not integration test, integration test for me is like selenium testing so there is all those differences in nomenclature that can make when you are just a beginner somebody quite lost actually. We won't talk, I want talk in this talk about unit tests or integration test so like selenium testing where like this could be even done by like q engineer on your team, that's not specific to Ember so I refrain myself from talking to that, I'll just talk about acceptance test and a little bit of component testing because I think there is a lot of things that kinds of clashes between those and I think it's good to have a good distinction of what is acceptance test and also what's component test, they exist for a different purposes and I think when just beginning, writing acceptance test is like I feel that so easy that you end up just writing acceptance test when you could have just written a component test and as you grow your application that could become way slower and you get in data test that's more like a file liability to than something that actually gives you something good. So component integration testing, I always think like okay so how can I really explain this thing, so what's the difference between them? I try to always imagine as the journey of a to do list item like a simple to do list item and you usually try to think the perspective of a user, so let's say when the user just opened the list item for the first time, here is your like okay, so this to do list is empty, it has nothing and then the user goes okay, I'm going to add then a new item, and then it adds the new item and then this list has a new state, so it displays something different to the user and then he keeps interacting with the list until he finally gets a big to do list with pagination and all those things and I think component test are meant to test a slice of time so usually a state so just separate pieces of your applications, just the components themselves and what connects everything, like connects the dots of being the states is actually the acceptance test and I think it's very important to make that distinction early on because when you actually when you read like a component test or acceptance test they look somehow alike in the sense that your assertion usually assertion against some hml that equals something and they kind of look alike and I'm almost pretty sure that I could create the criteria that every component test you can actually write as an acceptance test but that's sort of not what we want, so yeah, in the end, acceptance tests test a feature and I feel like whenever you write a acceptance test you should think as a user, always. So to begin, I would just start to write acceptance test that adds a new item, I won't write a acceptance test that tests if the user can many items because I also think like sometimes I see those tests in an application I think they don't actually make sense because I guess once you make a computer do something once it should probably be able to do it twice, three times, like computers are sort of made this way, it's just that humans we know that sometimes we are not so good at replicating stuff but computers are made to do that. So just moving forward, writing a simple acceptance test, maybe everybody of you have already written it, I don't know but let's start here. So as the example I used this todomvc, but basically you can just add, it's already started with codex samples for many different languages, so in frameworks and it's basically comparing how those different frameworks, you can read the code and see what framework works best and there's also an Ember example which got no test so I'm gonna just write a acceptance test over there. So okay let's live code. So to write a acceptance test you can use the blueprint, so Ember, you can actually see now, maybe, better. Okay so acceptance test and we want to create a to do list and so just type down to do list, and Ember will generate the file for us, we can perhaps take a look at the file, what's happening, okay, I'll take out the first screen. Duh duh duh. And our test is right here. Duh duh duh. Alright, can you guys see that? Yeah, okay. So... So now we have this file inside test acceptance and Ember gives out something like this, and basically this test is composed of two big depictions that it's nice to understand like the distinction between them which is like kyunit, so kyunit gives you a test that you can pass an argument which is assert and then you can assert it against things, and also you can pass a module for a acceptance which is the module that Ember provides for yourself so you can click, you can do basically user actions with that and this test initially it comes out of the box wrong, let's just run Ember test. It can run with minus minus server, you get something in the browser now. I had to move the browser. Okay, yeah. Duh duh duh. It's building. Okay the test it starts by failing because this URL doesn't exist, this environment is provided mostly by testing, testing in sort of the runner, gives you this and also this on your terminal, I'm running with Fentyl just because the project is configured with Fentyngest but I really urge you to change your headless Chrome if you're still using Fentyngest, even the guy who created Fentyngest told people to switch from Headless, okay so now it failing and now we wanna write the test case that actually passes, so we don't wanna write anymore slash and our test that we want to write is something that let's say adds something to the list, so basically we can also just adding our to do list item. So now we make sure that we have a new to do list item, I usually work this way so I add let's say visit slash, I know that this route will work, and I do a return post test, don't actually have to return, but yeah. So at some point you get that and you see that you're doing the I frame and then what you wanna do next is perhaps show inspike here, show what's okay, what are the let's say how is the hml made, for instance I mean I didn't do the hml here so I'm gonna just do it, and I see that for example I wanna make it type something here and then press enter, right, so yeah, so I know that the input's in each of those, and I now can finally move on. So I can use this viewing and I want to viewing input should do, whoops, in each of those, and I gonna pass here in my to do what's the item, do a live coding session successfully. And then I want to press enter, those kinds are kind of hard actually to memorise and I'll show you a way to improve that later, so you need to know that pressing enter is key, the first argument of this function is input, new to do, and the second argument key down so you wanna press this key and it's 13, 13 is enter for some reason, so there you go. 13 is enter. Great. Is it missing something? Okay, thank you. Sorry, hard to see. And then I can, okay. Something weird is going on, sorry, I did not know that this was going to happen but I have my plan b, so let's actually just go to the version that is finished up, so I had this slide just in case things went wrong, I actually practise this a lot but it's impossible to code this way, but basically in the end what we have is the test so it's a simple test we just add a to do list item, we see the method that the Ember acceptance helper give us and with those we can basically write any kind of unit test we want and there you can see that it's not perfect, there is some duplication and so on and we want to actually make it nicer like for the removal of to do list item I actually just copy and pasted everything and to make sure that it's able to write a item and then I did a click to destroy it, so it removes like the list item. So yeah, that's like how you start with acceptance test, it's not perfect, but it runs, it screens, and so on. And yeah, so nice, unfortunately I didn't actually make it but yeah, let's move on. So we can see that there is a lot of duplication with this thing that I did of copy and paste and so on and you generally do this because the setup of your test like to remove something you need to setup it before and to help you do that you can use mirage to mock your backend developer, you won't need your backend anymore, you can just work separately from your backend developer, write your own app, you have now your fake backend so you can also have all the fake things and I think this is a nice library, it's very well maintained, at the beginning there is a lot of things and a lot of parts of this library and it's kind of hard to know everything what it actually does and but as soon as you actually start to learn about mirage you feel like okay now I'm in control of that monster and then it starts to work very well for you. To instal it it's pretty easy and I also recommend you start out with Ember faker, Ember faker helps you to generate fake data as you want to generate fake backend you also want to generate of course fake data. And you only need to do five steps, so the first step is you just enable mirage in your current environment and then you just specify that mocked route in mirage config.js, here I use like a shark tank so it can use resource items that we will create like I get items, post items like to create all the possible routes that could have for a resource and I think this is pretty handy, not everybody use it, sometimes, most of the people that I see they actually type down the whole thing and you don't need to do this anymore, mirage created, the guys that developed mirage created a lot of things that let's say saves you time. And to create the defector file you just need to choose Ember g, so it generate mirage factory item and then finally you get to, you can just add it to your factory as we just use the same attribute that you have in your model, and you specify whatever the data you want to mock, so in this case we use faker to create a title with lowering epson style of faking data and completed false so every item that I created for instance is false. But you can also use traits if you use I don't know Ruby or even Python so factory girl, factory boy, I guess a lot of languages have libraries like this and they're all embedded in mirage so that's one thing that I learned, like a lot of stuff that before were packages they're all just in mirage and you can just use it and if you already have used mirage, you don't need so basically mirage models anymore, if you still have that directory you can just remove it and the directory should work, it's amazing, they add it just this year, so I guess our application in my company still has it but we are gonna remove it. And so the step five is just to use it, so here is quite I actually have a video, yay, so no problems. Duh duh duh. So what I did is that I can just remove all this code and so basically it with a server that create, because that will make sure that I already have the icon. So okay now I have the marked item and that's all. You can like debugging mirage I think is the best part because you can just use server, almost anywhere on your application and you can then call the db and you see all the records that let's say your end point will return. And you can also read the docs for more, there is like a lot of resources, sometimes you want to mock your let's say you also have pagination, you use perhaps a different serializer than the one from Ember, you can use all those kinds of things and you can configure there, it's pretty easy, and my next thing which in my opinion is the main thing of my talk is page objects. I've been using it for some time and it's very helpful. So basically on this code before you can see that there is still a lot of duplication, the selectors that we have there, I use it many times and maybe like some mostly a Ruby developer I always like this code has a lot of duplication, I am very annoyed, I cannot see verbose code and so on, and I was looking for a solution and I figured out that the library, this library, so oh sorry, yeah, I messed it up. So this library's very nice, I saw actually some projects that they do their own page objects, on git hub but I figured out that perhaps using a library could be a good choice, and this library is also very easy to use and I think it makes your test acceptance test just take it to a new level because it just looks great afterwards. So yeah. So this is like how a page object looks like, so just create, you have, sorry, page object, so you have this just this object with a hash, and inside that hash you pass like the keys of this hash are the methods that before you use it for example visit slash becomes just visit which I mean is not such an improvement but I think the improvement becomes more clear to you as you start to use the select, the advantage that it gives you like when you use the selectors, so for instance when you press selector, text, et cetera, in the end, when you're using inside your test just become let's say item title and if I'm at a text and it also has this code, so a lot of your tests sometimes you're thinking, you have the hierarchy of your page and usually you try to follow this inside your selectors and sometimes your select gets kind of huge and there you can specify a scope in whatever you add inside there, will become, will be added before so you don't need to keep writing things over like yo, alai, label, first, yo, alai, and I think this is very nice, it really is like the, don't repeat yourself mantra, so and you can also even go further and add scope inside scopes, and yeah, so, basically those scopes they can also become component so you can create a separate file and basically import that sort of component inside your page object and they can be your components of, they can be sort of like a reflection of your own component and there is even more features for this package and so this is the code that we had before and what you get inside, I think is quite neat like before you had like all those selectors with a lot of duplication, and now you just have this page variable where we import like the page and you can co-visit, item title, item press enter and I think this is way more readable, like I usually when I open the acceptance test I see all those selectors and you try to imagine the page and to think what it is and I think it's quite hard to actually imagine all of that, so yeah, that for me it felt hard and I was like yeah, let's, there could be a way to make it better and I think page objects made it much better. So the next thing that I'm gonna talk about is like a random thing that I saw on our test week, and also something that I see that this happens for somehow reason, like people I feel, and me also, I just didn't understand very well how and then works. So what does and then do, so when you are beginning you try to think and then sort of the same as like a promise and you start to write your test and sometimes it doesn't find the selector that you expected and you're like maybe I should add and then here and things will magically work, sometimes it works and sometimes it doesn't, but it probably wasn't the and then that fixed it. So this makes no difference, your test will probably pass but if you actually remove all the and thens and keep one it will work and I think this mindset is because people start to actually imagine, sorry, that it's actually like some kind of promise, like dot then, so that's why I think people actually end up doing this. But the real truth is that you actually just need to use before like you do your assertion like it's a, when I've read the Ember guides I didn't see something very appropriate that explain to me very well, this basically you just need before you do the assertion and all those Ember helpers that you use such as visit viewing, key event et cetera, then you get some kind of tech that whenever call and then, it's gonna execute everything so even though your test, like this is not so much like java script if you think about it, calling visit, viewing, key event, those things they look synchronous but they are not really synchronous. So this is actually the code from then, it's a bit like yeah crazy code and stuff but I think it's actually a very nice thing that perhaps you could read and think like okay now I somehow know what is this and then so whenever you write an acceptance test you feel more confident, so I know what's happening, it feels more predictable because I had a lot of problems that I didn't know what was really doing on. And the thing is that the code can be hard to understand but if you just look at the comments instead they are a little bit easier to understand. As you can see here you can see that there is this promise, so you know that in the returns a promise that's perfect fine but it has a set interval that runs every 10 milliseconds and I mean the comments, the first comment says like every 10 milliseconds pull the sync thing for to have finishes so a sync thing very important to have finish it. If the second, if there are any ping ajax request keep pulling, so you want to make sure that let's say if your application is talking to a server that you just don't run the assert of course. And better if there are scheduled timers or we are inside the run loop, keep pulling. I think this problem can show up perhaps when you're testing some component that uses some code that's like outside of the Ember remote like I had the problem for example date keeper, and using more indents won't fix this problem so to fix this problem you need to create more like using a sync '08 and so on, so there is no l and then that you fix your problem and then it's mostly concerning like what's inside Ember what's inside the Ember framework and just to finish up it stops pulling and then it just resolves the promise, so basically that's like what indent does, I think it's very important to understand it so when you have actually a problem that you start a bug, this kind of problem that you use paint perhaps r's looking into something, you don't take the conclusion that the problem is indent, or it can be posssibly something else if you're running runs outside of Ember reloop. So I hope by this time perhaps you're a bit more like this guy, so you feel like yeah, okay, acceptance tests, yeah. Now it's like I can perhaps do something and I don't know go Monday to work and perhaps use page objects so yeah don't just do live coding, that's all, thank you.