The Next Generation of Testing
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 27 | |
Author | ||
License | CC Attribution - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/35702 (DOI) | |
Publisher | ||
Release Date | ||
Language | ||
Producer | ||
Production Year | 2018 |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
EmberConf 201812 / 27
3
4
6
8
12
13
15
16
18
21
22
23
25
26
00:00
VideoconferencingLinker (computing)Statistical hypothesis testingOcean currentElectric generatorScaling (geometry)Set (mathematics)WordXMLComputer animation
00:54
Information technology consultingContent (media)Greatest elementMultiplication signInformation technology consultingMobile appComputer animation
01:27
Common Language InfrastructureComputer fileRevision controlDefault (computer science)EmailKey (cryptography)Open sourcePoint (geometry)
01:52
Function (mathematics)Functional (mathematics)SynchronizationCodeBoilerplate (text)Wave packetChainSlide ruleVariable (mathematics)CodeGroup actionDensity of statesFunctional (mathematics)Process (computing)Statistical hypothesis testingSubsetStatistical hypothesis testingComputer simulationState of matterMereologyControl flowSystem callWeightRoutingFocus (optics)Standard deviation
04:26
Query languageStatistical hypothesis testingService (economics)NP-hardFunctional (mathematics)SubsetElement (mathematics)Point (geometry)Object (grammar)Module (mathematics)QuicksortINTEGRALConnectivity (graph theory)Price indexGreatest elementCASE <Informatik>Different (Kate Ryan album)Cellular automatonStatistical hypothesis testingOnline helpStudent's t-testWeb pageXMLComputer animation
07:01
Function (mathematics)Modul <Datentyp>Module (mathematics)Execution unitHand fanPoint (geometry)Multiplication signGodModule (mathematics)Statistical hypothesis testingSuite (music)Revision controlCodeObject (grammar)Statistical hypothesis testingSubsetOcean currentElectronic mailing listEndliche ModelltheorieGraph coloringMathematicsBuildingConnectivity (graph theory)XMLComputer animation
08:42
Statistical hypothesis testingStatistical hypothesis testingWeb browserPhysical systemMaxima and minimaRevision controlMetreDemonMobile appContent (media)Statistical hypothesis testingComputer animation
09:17
Statistical hypothesis testingHuman migrationStatistical hypothesis testingCodeStatistical hypothesis testingFunctional (mathematics)QuicksortSet (mathematics)SubsetUtility softwareStatistical hypothesis testingCodeModule (mathematics)Execution unitCartesian coordinate systemDistribution (mathematics)Connectivity (graph theory)Service (economics)Computer animation
10:19
Statistical hypothesis testingRegular graphModule (mathematics)Module (mathematics)Statistical hypothesis testingCodeFunctional (mathematics)Distribution (mathematics)Home pageLatent heatFunction (mathematics)CASE <Informatik>Statistical hypothesis testingPlanningTheory of relativityZustandsgrößeNeuroinformatikRegular graphSlide ruleComputer animation
10:48
Game controllerService (economics)Statistical hypothesis testingStatistical hypothesis testingINTEGRALExecution unitGame controllerService (economics)Unit testingData storage deviceLecture/Conference
11:16
Statistical hypothesis testingSet (mathematics)Module (mathematics)Directed setMessage passingBuffer solutionDistribution (mathematics)Statistical hypothesis testingObject (grammar)Slide ruleMessage passingConnectivity (graph theory)Service (economics)Selectivity (electronic)Statistical hypothesis testingModule (mathematics)MultilaterationRegular graphFormal languageCartesian coordinate systemPoint (geometry)HookingMereologyCodeFlash memory1 (number)Suite (music)Set (mathematics)Dimensional analysisCASE <Informatik>Instance (computer science)Category of beingSolid geometryPattern languageQuicksort
13:28
Artistic renderingStatistical hypothesis testingComponent-based software engineeringModule (mathematics)VolumenvisualisierungFunction (mathematics)SynchronizationElement (mathematics)Content (media)Statistical hypothesis testingConnectivity (graph theory)Rule of inferenceLinear regressionStatistical hypothesis testingINTEGRALVolumenvisualisierungFunctional (mathematics)Distribution (mathematics)Software frameworkRight angleDefault (computer science)ResultantMultiplication signArtistic renderingChainSuite (music)Template (C++)Latent heatLogicSet (mathematics)CASE <Informatik>BlogQuicksortComputer fileMathematicsExecution unitProgram slicing4 (number)Online helpMultiplicationWordGodXML
15:59
Formal verificationStatistical hypothesis testingSimilarity (geometry)SynchronizationElement (mathematics)SubsetStatistical hypothesis testingCodeFormal verificationCartesian coordinate systemSet (mathematics)CASE <Informatik>Point (geometry)INTEGRALQuicksortDifferent (Kate Ryan album)Statistical hypothesis testingRoutingVolumenvisualisierungNormal (geometry)
17:07
Execution unitRevision controlComputer animation
17:38
Statistical hypothesis testingHuman migrationSynchronizationCodeFunction (mathematics)Human migrationPlanningStatistical hypothesis testingRight angleDistribution (mathematics)Single-precision floating-point formatOnline helpWeightSheaf (mathematics)Goodness of fitWordSet (mathematics)Projective planeBit rateSimilarity (geometry)Group actionKälteerzeugungCASE <Informatik>Execution unitCase moddingCodePerfect groupComputer animation
19:39
Computer clusterInjektivitätMobile WebComputer animation
20:14
Object (grammar)Service (economics)CASE <Informatik>Service (economics)CodeHTTP cookieInjektivitätCategory of beingState of matter
21:17
State of matterReplication (computing)State of matterStatistical hypothesis testingDefault (computer science)2 (number)Functional (mathematics)Statistical hypothesis testingField (computer science)Slide rulePredicate (grammar)Multiplication signForest1 (number)CASE <Informatik>RoutingMultilaterationXML
23:05
Statistical hypothesis testingFunctional (mathematics)Default (computer science)SynchronizationFunction (mathematics)MultilaterationFunctional (mathematics)Statistical hypothesis testingSinc functionQuicksortPolarization (waves)Computer animation
24:02
Statistical hypothesis testingCross-site scriptingElement (mathematics)Task (computing)outputQuery languageInstallation artStatistical hypothesis testingoutputPoisson-KlammerBuildingElement (mathematics)Social classProduct (business)Statistical hypothesis testingAttribute grammarGreatest elementField (computer science)Client (computing)Computer animation
25:38
CodeElement (mathematics)Execution unitClient (computing)Multiplication signContent (media)Statistical hypothesis testingProjective planeFunctional (mathematics)XMLComputer animation
26:45
ExistenceInstallation artSet (mathematics)ResultantAttribute grammarSocial classDefault (computer science)CodeStatistical hypothesis testingComputer animation
27:18
Statistical hypothesis testingLinker (computing)CodeStatistical hypothesis testingPhysical systemComputer animation
27:40
Square numberAverageRSA (algorithm)Coma BerenicesBlock (periodic table)Data typeComputer animationXML
Transcript: English(auto-generated)
00:17
So you might be wondering why I'm here, and well, we
00:21
swapped the talks. So the scale you see outside is a little bit off. So the next talk would have been, or actually this talk would have been about writing custom test helpers with the new APIs. But I'm going to introduce those new APIs now. So it makes a lot more sense. So this talk, as I said, is about the next generation
00:41
of testing in Ember, although right now we already have that generation. So it should actually be the current generation of testing. But I'm still going to introduce you to it. So this doesn't work. No, it does. OK, so this is me. I'm actually glad I parted in my spare time. So if you have any questions about that, ask me after the talk.
01:01
But also if you have questions about the actual content of the talk, you can ask me about that too. You can see the handles at the bottom if you want to contact me otherwise. I work in Germany for simple apps consultancy in Munich. We do consulting all over Europe, basically, just like Jessica, who just presented to. I do have some Ember Jazz Munich stickers with me.
01:23
So if you want some, find me after the talk. But you may also know me from my work on Ember CLI. So you can see I have a problem, open source style problem. I maintain quite a lot of packages at this point. So the first thing I want to talk to you about is the and then helper.
01:42
And it was already mentioned in a keynote this morning. And then is pretty much no longer a thing. If you don't know what and then is, it's this thing. So when you write acceptance tests, you use the visit helper to visit a route. You can use fill in and click to interact with that route.
02:03
You can basically simulate UI actions from a user. And at the end, you use the and then helper to run assertions on that state. So what the and then helper does is basically wait for any asynchronous actions to finish. So imagine when you do the click, the submit button
02:20
click, that that would fire off an AJAX request. Now, the and then helper would wait for that AJAX request to finish until it goes on and then runs the assertions for you. But the and then helper is not exactly standard JavaScript. And it was invented actually five years ago.
02:41
If you look at the code as it was in 2013 when testing was first introduced into Ember, you can see promise chains. And if you look at them, they're not exactly readable. It's quite noisy and boilerplatey. And it takes your focus away from the actual code that you test.
03:02
So the and then helper was basically invented to work around that problem that promise chains are essentially very verbose. But as it is on the slides, that was 2013. We have 2018 now, so five years later. And TC39, I think last year or two years ago,
03:21
introduced async functions. That makes it a lot easier to write asynchronous code. And if you compare the promise chains to async await, it looks like this. So a lot more obvious what's going on. The async parts of the tests are clearly marked with the await keyword.
03:42
So basically, all you have to do in front of the function, you write the async keyword. And in front of all the asynchronous helpers, you write await to await until that promise has finished. And then the control flow will just continue. So in summary, and then almost five years old,
04:01
it mixes synchronous and asynchronous code in a way that is kind of hard to follow and understand. And it has a somewhat unintuitive API. While async await, it is standardized ECMAScript now. It is very explicit about what calls are actually asynchronous. And it's based on promises, so another standardized feature
04:22
of JavaScript. Onto the next topic. jQuery, you've already heard it in the keynote. Ember doesn't use it anymore. So why do we have it? Well, there is one caveat to the announcement in the keynote because, well, the test servers, they still use it internally.
04:41
So it's kind of hard. If we look at example snippets, you can see an acceptance test snippet at the top and a component integration test at the bottom. And just look out where jQuery could be used in those snippets. In some cases, it's somewhat obvious. In other cases, it's not.
05:01
You can see at the bottom, there is a this.dollar sign, which is usually a good indicator that jQuery is used. There's another one. But did you know that the click and find helpers are also using jQuery underneath? So that's basically the reason why jQuery is still somewhat needed. But I'm going to show you how you can work around that.
05:21
So if we focus on the top snippet, you can see the click and the find helper. And basically, all we need to do would be to write a function that has the same API as the current helpers, but uses native DOM elements and native DOM APIs underneath.
05:41
So thanks to Miguel Camber, CyberKnox, thank you. We have Ember native DOM helpers, which does exactly that. So instead of using the global click and find functions that we include for you, you can just import click and find as examples from Ember native DOM helpers and use those instead. And they will just use native DOM APIs,
06:02
so you no longer rely on jQuery at all. There is one minor caveat. The find helper, the global find helper actually returns a jQuery object. Since we don't use jQuery with the native DOM helpers, we can't actually do that, which means we actually return a native DOM element.
06:22
So there is a slight problem there, but it's actually easy to work around, because the native DOM APIs at this point are quite all right. So at the end of last year, we started extracting those helpers into an official add-on, and that is Ember test helpers, as you can see here.
06:41
So basically, those are more or less the same helpers. There is one minor difference, though. The Ember test helpers only work with the new testing APIs. The native DOM helpers work with both, which makes it great as a sort of intermediate step towards migrating to the new APIs.
07:01
Next topic, nested modules. I'm actually a big fan of Mocha. I've been an opponent of QUnit for quite some time. I had quite strong feelings about that. At this point, I must say, QUnit is OK. I can work with it with the new APIs. Anyways, let's get into nested modules. If you have never used Mocha, this
07:20
is basically what they look like. So what it means is you can basically nest test suites in one another. And the reason for doing this is that each of those test suites can have setup and teardown hooks that got invoked for every test in those test suites. It's a very simplified example, but you
07:42
can see how that would scale, basically, if you have a lot of tests in those. So one thing that annoyed me always about QUnit was you could not do that, at least not for Ember. Luckily, last year or so, a new QUnit version came out, and it actually allows us to do those things.
08:01
So if you compare the current Mocha code with the QUnit code, it looks like this. It's basically the same. There are a few naming changes, and the hooks object is more explicit than in Mocha, but it's pretty much the same other than that. There is another caveat, though. This does not work with our current APIs.
08:22
So we usually use module 4, module 4 component, module 4 model, module 4 acceptance in Ember tests, but those don't allow us to nest that, which I'm not a fan of. So we took a stab at solving that. And Rob came up with an RFC about, I think,
08:41
two years ago called the grams testing unification. It's been around sometime, and it was RFC 119. And if you look at the RFC, it is long. It's actually very long. So it had quite a lot of content,
09:00
and nobody actually dared to implement it, because the scope was just so big. So two years later, nobody had still worked on this, basically. So we started extracting out several smaller scope RFCs out of that. And the first one is RFC 232, add new QUnit testing API.
09:22
And the TLDR of this is that we replace the module 4 functions with the native module function that QUnit exports, plus a somewhat custom setup test function that basically integrates the module with the Ember testing setup.
09:40
There is a sibling RFC which is 268, and that basically does the same for acceptance tests. So instead of writing module for acceptance, you now use the native module API of QUnit, and you use setup application tests inside of that. I will go into all of this. So we basically now have four different ways
10:02
of writing tests. And I will dive into that now. So the first kind of test you can write is a plain QUnit test. That's basically for code that is entirely unrelated to Ember, so say a utility function that doesn't actually know about services, routes, components, whatever. Example code is like this.
10:22
You have a module and a test function that you import from QUnit. Notice there is no Ember at all on this slide. It's just a regular, plain QUnit test. In this case, we're testing the relative date function. We're passing something in, asserting on the output. And it's just you could basically find this example on the QUnit home page
10:42
because it's nothing special. There's no Ember specific stuff in it. The next thing you can do, and more regular thing you can do, is write container tests. I've put that in quotes because Rob told me to not name it that way. The thing is, though, that we both couldn't come up with a better name so far, so I still continue using it.
11:03
You can use container tests for controllers, routes, services, stuff like that. Basically, everything where you previously used integration or unit tests. And I'm going to go through a quick example. By the way, don't be scared about all of the code. I will publish the slides in the select channel afterwards.
11:21
So you can just go through it at your own pace later. So the first thing we do in this example is we import stuff. So you've already seen before we import the module and the test functions. All these are just the regular QUnit APIs. And the second thing we import is the setup test function that I mentioned. Next, we set up a test suite, and we
11:42
run setup test as the first thing inside the test suite. Notice this is not running as part of a hook, but this instead is setting up the hooks. So that is why we pass in the hooks object into the setup test function. This will be a very common pattern, so get used to that from now on.
12:00
It's basically the same in Mocha, by the way, where we have pretty similar APIs at this point. This setup test function makes the testing container available inside of tests. And what that means is inside of the test, you have access to this.owner. This.owner has a method called lookup,
12:21
and you can use that to look up basically everything that's on the application container. So stuff like services, controllers, routes, technically even components, although components should be tested in another way. And finally, once you have an instance of, say, the flash messages service in this case,
12:40
you can use that to assert stuff like properties, method calls, and whatever you want, basically. So to recap, import from QUnit, import from Ember QUnit the specific stuff, open a test suite, run setup test, and after that just write test, and you use this owner lookup
13:01
to look up whatever you want to test, basically. You can also look up other things. So in this case, we're writing a test for the flash messages service, but no one's keeping you from looking up other things. So if you imagine you're using Ember Intel, for example, and you want to change the language that Ember Intel is using, you can basically
13:20
look up the Intel service from the container, use set locale, and then that will just work. So next type of test, rendering tests. That is basically what was previously the component integration tests. And you would use that for testing components and helpers. And it looks like this. Roughly similar, except in this case,
13:43
we're not importing setup test, but setup rendering test from Ember QUnit. And we're importing render and click from the Ember test helpers add-on. Now you might be wondering, why are we not just importing that from Ember QUnit? And you're right. But the thing is, that also works with Mocha now.
14:02
So the only test framework specific stuff that we do is actually the setup test functions. Everything else works, whether you use Mocha, QUnit, maybe some other test framework in the future, we'll see. So you import these things. The HPS import is just the same like we've used before. And then you open a test suite.
14:22
And in this example, you're not calling setup test, but setup rendering test instead. But everything else is exactly the same, essentially. And setup rendering test is actually using setup test underneath. So that means you can still use this owner.lookup, for example, in the tests.
14:40
And everything will just work the same as in setup test, basically. If we look at the test, it uses an async function. So no and then anymore. I don't think and then is even supported in these kinds of tests anymore. You can use promise chains if you want to hurt yourself. But I really would recommend to you just use async functions.
15:02
And it is the default in MLCL3 right now. So as you can see, we are calling the render function. We're passing in a template, basically rendering an x counter component, for example, yet. And we are awaiting the render function results.
15:22
The reason why we're doing this in the keynote today, you heard about async rendering. And that is basically incrementally rendering over multiple time slices, more or less. And if we would just continue synchronously, the template might not actually have finished rendering. So that is why in this case, in the new APIs,
15:41
we are explicitly, asynchronously awaiting the result of the render function. After that, we can just run assertions like we would normally do. We can use the click helper in this example to click on the counter, run assertions again. And it just works, basically. The fourth kind of test we can write
16:00
is an application test. It's basically the same as an acceptance test before. And you can just use those to verify user stories, basically. The testing code looks almost the same. In this case, set up an application test, not set up rendering test. We import mostly the same test helpers,
16:20
which is another big advantage of those new testing APIs. Because before, in integration tests and acceptance tests, used entirely different code, basically. Now it's almost the same. The only big difference here is we are not importing the render helper. We are importing the visit helper. And if you look at the testing code, well, I mean, we use set up application tests.
16:42
That's obvious by now, I hope. If you look at the actual test code, it almost looks the same. We are visiting a route of your application. After that, we're filling in some stuff. We're clicking a Submit button. We're waiting for that to return. And then we just run our assertions as normal. So actually, pretty straightforward, at least for me.
17:02
Sorry. So at this point, you might be wondering, how can I get this? This looks nice. I want to have that. What do I have to Ember install to get this? And the good news is you might already have it, because it doesn't actually depend on your Ember version at all. The only thing that matters is your Ember C, like Huion,
17:22
needs to be on version 4.2 or above. And if you have that, you can just use the new APIs already. Whether you're on Ember, I don't know, 2.0, I think we test all the way down to 2.4 or something. So if you're above that, you should be fine. Also, you might be wondering, well, I already have 5,000 tests or so.
17:42
What about those? How can I migrate those? Because that seems like a lot of work. And you're right. So I got a high-level migration plan for you. The first thing is migrate to using async await instead of and then. Async await works within the old APIs, too. So that's a good first step, basically.
18:03
The next thing is use Ember Native DOM Helpers. As I mentioned before, it does work with the old APIs and the new APIs. So that's a really good intermediate step. But that's still a lot of work. Great news. We have a robot, or a code mod.
18:20
It's called Ember Native DOM Helpers Code Mod. If you run that, it will automatically transition you to the new APIs, well, to async await and the native DOM helpers. Disclaimer, code mods are never perfect. But we really do our best. And I've had great success with this code mod. It basically converts like 95% correctly.
18:43
And the 5% is usually something you can do manually. The next step is using the setup test functions, or the new testing APIs. And while that's still a lot of work, we also have a code mod for that. It's called Ember Q Unit Code Mod. There is a similar thing for Mocha,
19:02
although it's not entirely finished yet. So give me a week or something. We're hopefully going to be finished with that, too. That also has a quite high success rate over all the projects that have migrated so far. So pretty good. And then the fourth step is basically just cleanup. In most cases, you can just rename the Ember Native DOM
19:22
Helpers import to just use Ember Test Helpers. You might have to adjust some things here and there. But also for that, there's a code mod. So technically, you don't have to do anything. You just have to run those three code mods. And hopefully, you're just done after that. So that's good. I got another section which is called Tricks.
19:43
We have about 11 minutes left, apparently. So I'm just going to go through that now. Mocking. There seem to be quite a few misconceptions about how to mock properly in Ember. And Ember is using this thing called a dependency injection model, more or less.
20:02
And that is actually meant to be mocked, more or less. So you can just register different things on those dependency injection containers, and that will automatically mock stuff for you. So I have an example of this. In this case, we're registering a mocked cookies service. And we're just saying, if someone reads cookies,
20:24
whatever key it is, we just return ABC delimited by semicolon. Next, we're just going to look up, say, a feature service that uses the cookie service. And we don't have to overwrite the cookies property in the feature service because it just injects
20:41
the mocked service for you. The only caveat here is you need to register that thing before you look up anything that uses it. Because if you look up before, the injection will already have happened. And if you register the mocked service after that, it will not matter because it won't be used.
21:01
So keep that in mind. If you mock something, mock it before you look up stuff. That's important. After that, you can just use the feature service as you would and do assertions on it, do whatever you will. All in all, not much code, and it works quite well, actually. Another thing is loading states and how you can test them.
21:22
I mentioned earlier that, say, if you use a wait click, it will wait for any Ajax request, for example, to finish until it continues. But what if you actually want to test the loading state of your application, say, a loading spinner? This is one example. So we visit the comments new route.
21:40
We put in a bunch of stuff into the comments field. We click the submit button. But in this case, we're not actually awaiting it. But we're saving the promise that comes out of the click function for later. Next, we're using the wait for helper from Ember test helpers. And we're waiting until the loading spinner appears on the DOM. So what this does underneath is it automatically,
22:03
every some milliseconds, pulls the DOM and checks is that selector available yet or not. And if it isn't after a timeout, I think by default it's two seconds, it will fail the test. If it's available, it will just continue. I forgot something in this slide
22:20
which is doing assertions actually after that. But you can imagine that after wait for has returned, you can run assertions on those loading states. Once that is finished, you can await the original promise from the click helper and then run assertions on the finished state. What wait for does underneath is essentially this.
22:42
We also have a helper called wait until. And that basically is the thing that's polling. So it will just run the predicate function that you passed to it. And once that function returns true, it will resolve. If that function doesn't return true after two seconds,
23:01
it will reject the promise. Another thing is custom test helpers. And that is actually the thing that Jamie will talk about in the next talk. If you've used register async helper or register helper before, that's no longer a thing. So register async helper used to look like this. You're basically, you're not actually exporting anything.
23:23
You're actually calling the register async helper function, passing in the name of the function that you're defining next. It all looks a bit weird. Also, it's using the old helpers which isn't great. What we do now is this. We just export an async function from basically anywhere you want.
23:43
I would recommend the test helpers folder, but you can basically do it wherever you want. And then inside of that, we're just using the new helpers. And we're using an async function to be able to await stuff inside of there. So that's actually pretty straightforward. As I said, Jamie will mention that in a lot more detail later.
24:02
Another thing you can do to write better tests in general is use test selectors. Test selectors is the thing you use if you look up stuff on the DOM. This is one example. You can see here we have a header, we have an input field, and we use the CSS class and the tag name to look up those elements
24:22
from the DOM for our tests. But imagine someone would like to make the H1 and H2. That doesn't really work because you now have to change all of your tests. The same for the CSS class. If you change the CSS class, things will break. That's not great. So those are the test selectors.
24:42
What you can do instead is you can use data attributes. By convention, what we usually do is we use data-test-something to indicate that this is the element that we want to run assertions on or that we want to click. And then at the bottom, you can see we have these square brackets.
25:01
Inside of that, we put the data attributes, and we just use those as selectors. It has one downside. The DOM that you will use looks kind of noisy because it will send all of that to your clients. Fortunately, Ember allows us to do AST transforms,
25:20
which means we can actually strip those selectors out if you run production builds. And as we use Ember, that's obviously an add-on, and you can just Ember install and the test selectors, and it will automatically remove all those data-test attributes from your production builds. So that's pretty good. Next thing I have is readable assertions.
25:41
That's one thing that really annoyed me about QUnit because its assertions are a lot more verbose than, say, Chai Jazz. So let's say we want to run this assertion. It's not exactly obvious what we're testing there. I mean, if you take a second, you can see we're asserting a text content.
26:02
We're trimming that. We're running the test that is this hello world or not, but it's not that readable. So last year, I thought, how can we make this better? I've been working on a client project, and we use QUnit, so I couldn't switch over to Mocha. Well, so I had to use QUnit, and I came up with this.
26:23
So this was the API I wanted to use. Because it's very expressive, you just pass the selector to the DOM function, and then you run, for example, a has text assertion on it, and you have to admit it's much more readable. So around the time of Ember Fest last year,
26:41
I sat down and spent about a week implementing this, and you can now install it as Ember install QUnit DOM, and you can see it's not just has text, but you can also assert that something exists in the DOM, that it has a certain CSS class, that something is focused, has a value, and there are several other assertions that you can use.
27:02
And I'm happy to announce, two weeks ago we merged an RFC that this is actually going to become the default in Ember CLI apps from now on. You don't have to use it, but I would strongly recommend it because it's really great, and it makes your test code a lot more readable. To finish off, have a look at the rest of the Ember
27:20
testing ecosystem. There are lots of great add-ons. I've been using quite a few of them, and they make your testing code a lot easier. Thanks to everyone that worked on this, and thanks to you for listening for me. Thank you.