Just Another Marble Monday – Unit Testing NGRX RxJS with Marbles - Sam Brennan & Keith Stewart

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I'm Sam I'm Keith and this is our talk just another Marvel Monday - in this talk we will discuss the benefits of marble testing what exactly marbles are and how to write a marble diagram my favorite marble syntax filter and hot and cold observables we'll also get into some examples of marble tests well take a look and learn about how rxjs schedules observables and how we can override that in our tests with the test scheduler well do a little bit of coding together and implement an 80s dance off feature into a clone of the popular tourer euros and lastly a quiz he's kidding no I'm not seriously quit hey Keith what up Sam why should we write marble tests I'm glad you asked I think Jeff said it best asynchronous developments is hard so when we write web applications we're largely writing asynchronous code there are many good reasons for this which I'm not going to get into today but there are some byproducts of this when we're testing asynchronous functionality such as observables it's it's very difficult to write these tests in a way that's easy to read and understand Sam have you ever had to write a test for an asynchronous function where you had to set up how your data was being resolved up at the top before actually executing the functionality down at the bottom didn't it feel like everything was out of order yes it's the worst well-marbled tests have you covered because they allow you to write your tests in against asynchronous functionality in asynchronous way so this allows you to write your tests in a fashion that much more closely resembles the actual flow of logic through your application and and then everything feels a little better and that is radical I know right so this and the fact that marble syntax is nicely descriptive of what and when things are happening in your of all makes it much more readable also when writing observables it's really easy to create race conditions in your application Marvel testing is a great way of exposing these types of issues awesome thanks Keith no problem so alligator IO says that marble diagrams are a way of visually representing observables so here is a marble diagram so first the lines show the flow of time as indicated by the arrows on the right side next we see the marbles which are values emitted by the observable then we see the operator which here in our face it's multiplying everything by 10 then we see the bar at the end of the sequence which represents completion the output contains the values that subscribers to the observable will receive I always forget that line okay and lastly the X represents an error if one is present in your code oh isn't that one of the the answers for the quiz later Keith how is Brad gonna win our quiz if you tell everybody the answers my bad you're welcome Brad so before we get into marble testing we are going to learn how to read them every time I told someone I was speaking on marble testing at ng-conf the response was pretty much the same they had to write a marble test at one point or another and they couldn't figure out what the symbols meant that is marble syntax and it's also my favorite don't ask why I'm a dork I know everyone knows sure to exam all right so let's briefly discuss marble syntax when I first started writing marble tests I was a little confused by it these symbols that that we put into the the tests they didn't immediately make sense I'm hoping with this baseline level of knowledge that it'll help you when you start writing a marble test yourself that it'll make make a little more sense than the easier can we get a raise of hands who has written a marble test before okay awesome well this is old news for you guys all right well let's still let's go over it real quick so dashes dashes represent a period of time usually 10 frames and a frame is equivalent to 1 millisecond a pipe represents completion so at this point the observable would stop actually emitting values the pound sign represents the dreaded error I don't like finding those in my code yeah me neither all right next characters are variables which represent values emitted by the observable you can use parentheses to group together functions that need to be executed together need to be executed synchronously and the caret represents a subscription point this is only used in hot observables I think that one's on the quiz too come on Sam you just gave me a hard time and now this what's going on all right all right hot and cold observables it's taking this a little bit so in our own words hot observables are creating values before the subscription has started with cold observables nothing happens until something or someone subscribes to it a good example of a hot observable is mouse movement because the mouse is moving or happening before somebody is listening or not a good example of a cold observable is an HTTP request nothing happens until someone subscribes to it so hot and cold observables is a concept that once you get it it makes sense it's not that hard but it's difficult to to explain and therefore there ends up being a lot of a lot of confusion on the topic so one of the things that we wanted to do in this presentation was find a an example that explains hot and cold observables that we can all relate to a little better as we were preparing for this talk we found a great blog post by Quentin Pittman where he took some real-life metaphors and and related that to hot and cold observables so he said hot observables are like watching a movie in a movie theater and cold observables are like watching a movie on Netflix the reason why watching a movie in the theater is like a hot observable is because regardless of when you get there the movie has already started and it's going to start at its scheduled time whether you're there or not the movie in these in this example is the data being emitted by the observable if you arrive late and you subscribe late too bad for you you've missed the beginning of the movie but you can continue watching on from where you got there on the other hand watching a movie on Netflix is like a cold observable because no matter whether or not someone else is already watching the movie that you want to or how many people are watching the movie that you want to your stream doesn't start until you start watching the movie aka subscribing to it so everyone who's watching movie has their own separate stream of this data and they can all be watching the same movie at different points at the same time okay now let's take a look at some marble tests some examples of them and see how hot and cold observables are used they're just a disclaimer we will be using Jasmine marbles in this presentation there are multiple different times but for us this is our favorite and thank you Mike and Brandon for writing this you're awesome that's okay they'll see it on the record applause okay so first let's take a look at what we're going to test this is the load dancers effect if you don't know what ng arts effects to they listen for actions to be dispatched and then after performing some side effect they return a new action so what this effect is doing is listening for the low dancers action to be dispatched it then executes the get dancers function on the dancer service and if successful returns the low dancer success action along with the dancers as a payload if the request fails it'll return the low dancers fail action yeah yeah we don't want that okay so this is one of the tests that we might write against that particular effect as you can see here there are three basic sections to the test first we set up our payload data then we prepare our starting and completion actions before we set up our effects and say what we expect to actually happen here you can see that we are declaring a hot observable to represent a stream of actions this is because before the test begins actions could already be getting dispatched the result and the response an expected result our cold observables because they will not emit any values before the test begins also we are using marbles syntax to describe what these observables are doing so the action that starts this process is dispatched after 10 frame delay then after another 10 frame delay we receive the response to the HTTP request with the set of dancers before the observe will stop submitting values the expected result has 210 frame delays after returning the completion action the reason for having 210 frame delays here is because the starting action and the response had 110 frame delay and result the result is delayed by both of these okay now let's take a look at a test against when this all goes wrong so if the HTTP request fails then this would be the test that we would write the structure is pretty similar except for when we're setting up the the payload data instead of actually setting up the success data in this case we are returning an error so we will have an error as the payload also one of the main differences here is that the response here is actually throwing an error so again we're not going to be returning some data from the HTTP request it throws the error we're going to deal with that the expected result here still has 210 frame delays before emitting the completion action this again is because the previous two steps in this process have 110 frame delay each and we need to account for it in our expected result okay time to dive a little deeper let's take a look at a scenario where we need to work with the scheduler that determines values that determines wind values are emitted from observables okay so what if the functionality that you are testing is doing something to change the schedule that the observable is emitting values a good example of this might be ad bounce to suppress the number of values emitted while a user is typing in some sort of a search criteria alrights j s operators internally use the async scheduler to determine when they will emit values so what we can do is use the get test scheduler method that is provided by Jasmine marbles to get a scheduler and inject it into our effects when we run tests so that we can control the flow however we need could you show us how we were able to inject the scheduler into our effects absolutely Sam okay so here's the constructor for our effects so what we're what we're able to do here is use injection tokens to optionally inject a debounce value and a new scheduler which we can then use in the tests for our effects with these in place we can go ahead and use the injected scheduler if one is provided or if one is not we'll just go ahead and use the default async scheduler from rxjs so this effect handle handles a search of the dancers the effect starts with the dispatch of a search action it then applies the debounce to the data stream suppressing any value from being emitted for a period of time this is where we can use our injected debounce value and scheduler when we test when we are not testing the ACN scheduler we'll take care of things okay so when setting up the testbed for our tests we can take advantage of the injection token so we were looking at just a moment ago and provided debounce value and use the get test scheduler function that's provided by Jasmine marbles to inject the scheduler that we can use in our tests the reason for setting the debounce value here at 30 is to make things a little easier for us as we are testing so that we don't have to write quite so many dashes in these tests if we have a longer debounce now that we have provided a test scheduler we have set the debounce value to 30 our test is pretty similar to the ones we looked at earlier one thing that I would point out is that the expected result has a delay of 50 frames this is because of the starter action and the response each have a delay of 10 frames before the 30 frame debounce is applied to the effect hey Sam why don't we show a little more interesting example of this type of a test and see how it relates to the diagrams that you were showing earlier girl thank you all right let's do it so in this example we show a little more about what debounce is actually doing for us as you can see here the search action is being dispatched a number of times notice that in the expected result that even though the search action was dispatched three times the success action is only returned twice this is because the debounce the debounce is suppressing the values and not allowing the values to make it through until the 30 frame delay that we had said earlier in the in the testbed had completed so now why don't we see how this this action stream and the expected result look in the diagram this marble diagram is showing how the data is flowing through our example and how it is and how it is influenced by the D balanced time operator sorry as you can see here in the diagram the values emitted by the observable correspond to the actions to the action stream in the example test the output from the observable shows how the values are affected by the D bounce time and how it corresponds to the expected result in our example test and now without further ado it's the time that you've all been waiting for it's our 80s dance battle this is exciting so how are we gonna do this we're gonna bring up some people maybe Brad you can come up and dance for us we'll get some other folks we'll have a battle sound good I mean we could do that or what better who better to show us the 80s dance moves then the 80s stars themselves it's time to make these eighty stars Duke it out in the most grueling but fabulous way it's a dance battle for the ages okay so again here's the URL for the project you're welcome to follow along if you'd like but it's not required so you can also just watch as we code through it after you've cloned the project if you are deciding to do this with us you can run NPM install to get all the correct dependencies you will need for this project to run the project on your machine just run NPM start from the command line and and then in a separate terminal if you want to run the test you can just run NPM test if you fall behind or miss a step you can run these commands to get caught up at the end of each task we are going to use test-driven development to write this feature so first we're going to start off with writing the test at this point the test will be failing because we haven't actually written the functionality yet so together we will make that functionality work and the test pass fingers crossed Keefe is a lot more optimistic about this than I am hey have a little faith in me 1987 anyone nice okay they'll be live coding along with you so you can follow along with him and if you have any questions do your best robot and I will help you okay if you're following along if you head over to this this URL here it'll take you to the description let's go ahead and start coding so first of all let me get oh that's not where we're going yet okay so here's the application that we're working with so like we mentioned earlier it's a simple clone of the tour of heroes but we've modified a little bit we've replaced heroes with dancers and what we are able to do if I can see my mouse here is we have a set of some of the 80 stars and we're able to choose from those stars and pit them against each other so let's see here let's choose Susanna Hoffs all right and we can click the battle button and there we go they are in there dance battle battle processing problem is right now this is not actually processing the battle and telling us who is actually going to win so that's what we're gonna implement now all right so into the code base here so I am using Walla bjs in my editor that's how we get some of this nice feedback over here letting us know what tests are passing and failing you're welcome to use that if you're following along as well the configuration file for it is at the root of the project but not needed you can just run the tests using karma by running NPM tests at the at the root as well okay so again we are using good test-driven development style here so the first thing that we're going to do is write a test that will be failing and then fill in the functionality that's going to get that passing so real quick before we get into the test I'm just going to write the shell of our effects so that we can reference that in our tests so we are if I could spell that right writing an effect called battle it is going to return an observable of an action okay I think that is good for our shell so let's go ahead and move over to the tests and start writing here so we start off by writing our describe so this is going to be used to group together all of the tests that that we're writing for this particular effect okay so we've got our describe for grouping that let's go ahead and start writing our our test so this should return a battle outcome determined action with the outcome on success okay so now what's next so first of all we want to go ahead and set up our payload data like we were talking about earlier so we're gonna need an outcome and we have a nice room that gives us all of the possible outcomes there so we'll grab one of those we need a challenger which is a dancer and a challenge II now this is difficult looking to the side while I'm doing this but it's coming together okay thank you thank you okay so we've got our oh you know what realizing I forgot we got two new these up these are new instances of these classes so there we go there's all the red lines gone okay now we set up our starting in completion actions so this is again a new battle action and it takes a payload that includes the Challenger and the challenge II all right that looks satisfied and our completion action is a battle outcome [Music] determined action which is going to receive our outcome okay so let's go ahead and start let's see here I again did not move that up look at that okay so let's go ahead and set up our effect so the the action stream is a hot observable and so here we're just simulating the the action making its way into the action stream by being dispatched then we set up our battle this is a cold observable and here we are going to return the outcome all right now our expected results okay again like we were talking about earlier the the previous two steps add to I add a 10 frame delay so we need to account for both of those here so we have our 210 frame delays and then we return our completion action okay there we go and that looks okay we'll just get rid of that little space there okay okay and lastly we just need to tell the dancer service action what it should be returning and in this case is our battle that'll include the outcome okay so now it's time to just set up what we actually expect the the result to be and in this case the the effect battle it should be an observable of our expected result okay all right so we have our tests written here and of course it is failing so let's go ahead and get it working alright so we're back at our effect so this is looking for again a type of the battle action okay there we go and once the battle action has been dispatched then the next thing that we're going to do is go ahead and execute the the function to get the the results so we'll take our action here this is the battle action and this starts dancer service okay here we go now this takes a couple of parameters so let's go ahead and fill that in so the we can get that off of the action payload we saw that earlier that that's where everything was being housed and the challenge II okay all right got that all set up now let's just go ahead and and map that over to our results so that function it returns an outcome and we're gonna map that over to the battle outcome determined and pass in the outcome and there we go green lights alright ok so so we have our happy path done next we need to take care of any errors that that might come up so let's go ahead and write the test for that this one might go a little bit faster because it's pretty similar in a lot of ways to the previous one so we can take advantage of doing a little copying and pasting but first let's write what we expect to happen so this should return this should return a battle fail action if there is an error ok so so we set up our payload payload data in this case it's going to be an error oh no it it failed ok so we have that and now I'm just going to go ahead and and since a lot of this is the same I'm gonna go ahead and copy that paste it down here the completion though is a little different so let's go ahead and write that one so in this case we are going to be doing a battle fail and its payload is the error and I again didn't do it up so there we go ok so the actions let's go ahead and set up our effect here this again is a hot observable passing in our starting action now the the battle itself is going to be a little different here because again this is our not-so-happy path so it is again a cold observable but instead of actually putting a variable in here to return a value we're instead going to have that dreaded error in it so in no value being passed passed into that variable but instead we're gonna throw the air ok and and then the rest of it is is pretty similar so let's go ahead and again do a little bit of copy and paste so alright so we have our we have our test written it's again failing let's go ahead and get it working okay so in this case really all that we need to do is catch that error and map it over to the appropriate function so we're going to do a catch error we have the error here and we're going to make that an observable of a battle fail with the error as the payload and with that back to green all right I'm gonna save that let's let's take a look at what we have in the application now and see see how far we've come so let's see here alright so we're back at our application we got Michael Jackson's still here let's see who do we want to have them battle this time we got the list here any yeah yeah what Bowie David Bowie here we go all right let's see who wins all right ah Michael Jackson isn't he awesome this is great but we didn't get to see them dancing hey that's that's really what we're after right we want to see them dance alright so let's go ahead and and change this up so that we can actually see that happen well add in a little delay and still process the battle but you know get to see the action so again let's head back over to our tests and we'll write one more test here so this should return a battle outcome determined action after a 30 frame delay okay okay so there we go I'm again I'm just gonna go ahead just to the keep us moving here I'm gonna go ahead and and copy since most of that is is pretty much the same and in in your real applications you might take some some of this's is very similar you could move some of this up to the top of the describe and share it amongst all of your tests but I'm just going to go ahead and leave that there for now the one difference here is going to be that in our starting action we're going to go ahead and pass in a value into an optional parameter here the delay so let's go ahead and add in the 30 frame delay right there okay and then we will go ahead and set up our our observable our effect again so let's get that down here and but this one of course we're planning on having a delay again right so before we were just planning on having the 210 frame delays this time we need to take it take into account the additional 30 30 frames so we'll go ahead and add our 30 frames right there we've got to tell our dancer service what it should be returning and that again is the battle and then our expectation the effects battle to to be the expected observable okay how we doing all right we got our test written it's again failing so let's go ahead and get that working and then see where we are so we just need to add in the delay again here so right before we map out the results we're just going to go ahead and add in a delay now what we want to do again the delay was optional so we're going to go ahead and grab the the payload from the action here and take the delay and use that but in optional so if it's not there we're just going to go ahead and set a zero delay the other thing that we need to do because notice that we're still in the red here we need to get this test working or we need to get the this functionality working we need to go ahead and use that injected test scheduler that we had set up earlier so we're going to go ahead and see if these the scheduler is is available for us but if it's not we'll just use the default async scheduler and there we are we were momentarily we are back in the green so awesome let's take a look at the test yeah everything's looking good this is great alright let's let's take a look back at our application and see how we're doing okay so again Michael Jackson last time it was David Bowie any other takers here who's next Mick Jagger Stevie Nicks Stevie Nicks Stevie Nicks all right here we go let's see how this goes okay we got some dancing going on battle in progress who's gonna win Michael Jackson again that guy's amazing oh all right great okay well let's let's see what's next so last but not least it's quiz time okay well Keith is getting that set up we're gonna hear from our awesome sponsors I don't want to put this the too close okay about what our prize it is short version another long version when we went on wins okay hi my name is Brian Darth ways digital and we are auctioning offering not auctioning offering one public ticket to our angular bootcamp we I am very serious online if you win the quiz you get to go to angular bootcamp yep you can also somebody else yeah great yes so angular bootcamp oh man they got it yeah okay you can go on your phone or you think on your laptop to Kahoot dot it enter the pin come up with an awesome nickname I don't know if Mike and Brandon are still in here but if you are you can't play that's cheating [Laughter] it's awesome okay a lot of people here amazing what's that where's our music where's our awful yeah I don't know the music music seems to be gone but maybe it starts when we it's you dude we still got a lot of people coming in here alright well we'll give you just another moment we're actually doing really good on time so yeah okay I'm getting about 30 more seconds Brad Green's hat okay and there are rules to this game if whoever is in the top three I'm gonna say who is this person because everybody wants to know and and you're gonna have to yell or do a dance or something that's the rule and if you don't do it sorry man okay let's start okay where is my mouse there it is there we go alright are you ready nine questions coming up here nine questions I hope you took notes what is the relationship between observables and marbles marbles represent observables observables watch for marbles to emit a value or marbles are hot observables or marbles and hot and observables are cold alright we're getting a lot of answers here times almost up 48 correct answers here that's great awesome alright let's see who do we have in the in the league in 1880 okay here we go next what are the observable types we have covered today rxjs observables but specific like be specific here hot and cold or a teaser bowls all of the above is not an option I'm sorry okay there's no music 3 2 1 73 correct answers this is awesome you guys are doing great ok so we had a change in the lead here flipster flips there and I a nice alright good job get a narrow lead at this point though keep it up alright next so what is the difference between hot and cold observables just so H o is continuously watching even when test isn't running cold observables are built in hot observables are written in a template while cold observables are in a components what say you oh good job guys nice nice ok let's see who's in the lead who's Lani Danny yeah are you an instructor at angular bootcamp okay all right next question what does the dash mean in a marble test it separates values and makes it easier to read it represents values being displayed on the screen it is a measure of time in frames and I'm sad the music is really bad well that's not supposed to make you sad if it's bad I mean it's good like it's a good it's a good like Michael Jackson bad who won all the dance battles ok ok so it's a measure of time in frames okay nice Justin T this is amazing good job who's anyways I have no idea this is crazy ok all right here we go next question here we go who had the best selling album of the 80s Madonna Prince Michael Jackson her David Bowie this should be easy come on guys huh question mark no googling it either this is good yep here we go how we doing 81 anyone answers 82 right at the last 65 right yeah Michael Jackson just wins everything this is danger dan' yeah did you Dan all right it's okay I got it no applesauce okay I got a thumbs up back there all right okay all right here we go what type of observable does the subscription care at work with I believe I got fussed at for this one hot and cold they will both subscribe eventually hot observables or colds intervals dude seventy three seventy and you guys are quick it was amazing all right two one midnight all right a little more even spread here forty three correct answers so let's see how we're doing [Applause] okay how many questions left let's see here seven of nine okay so why won't the subscription quo subscription care at work on cold observables what you talking about Willis it works on both because cold observables are not subscribing to anything since hot observables are already running they have to subscribe there we go this one's a little sweet didn't go over this this question so ah there we go okay thirty-eight correct answers let's see how we're doing hey Jones hey Jones all right good job hey Jones yo do they go okay hey good job all right I think we got two more so okay still a chance for folks to more questions how many frames are represented by 1 - 15 frames 25 30 frames 10 frames it's 15 frames wait oh I wasn't supposed to say that wait no it's not fifteen frames pretty sure no all right 71 correct answers I think sorry we apologize okay hey Jones yeah hey Joe oh nice even though he's going away good job Justin d though thank you oh we got the next man what does an X represent in a marble diagram these spots of course that's got to be at a subscription point for a hot observable a value emitted or an error it's the spot right it's a spot pretty sure it's a spot I mean what you talking about Willis what you talking about Willis okay one second good job it's nice nice most of you a couple of people actually took us up on the spot this is great I like that all right okay this is awesome good job Jared come on up here you won did yeah you are I always do that alright so I'm just gonna tell you really quick about angular bootcamp it is a three-day intensive course that we offer it's taught by the likes of Bill Odom Paul Spears Lance Finney Sanne you so if we had a lot of great instructors out there we'd do it publicly in various cities throughout the US online we also do it privately for teams so many of you in this room probably do not need angular training why are we giving this out well you can give it to your mother-in-law your neighbor's cat or you can apply it to a team purchase so we also do private like your team so feel free to apply this to either yourself your friends or anybody else and if you have any questions stop by and see us in the booth outside and thank you very much good job Jared all right awesome my storm okay so on okay it's a little bit more know what's done okay what's next here we go so we're gonna review really quick so in this talk we briefly discussed the benefits of marble testing or unit testing but marble testing in specific all right that thing stopped working so here we go we went over marbles and marble diagrams we looked at marble syntax and we talked about hot and cold observables we went through some example marble tests discussed the test scheduler and how we can use that in our tests to override the scheduler we had an 80s dance off where we exercised together or wrote a little bit of code together implementing marble tests and we had a quiz we would like to give a special thank you to the Angie conference organizers some of which are in the room and of course the ng rx organizers and the angular community you guys are great and if you don't know one of the organizers is actually my mom so we'd like to say a very very special thank you to the intercom for doing so here are the slides one more time and they will be tweeted out after our talk so no worries if you did not get them and now you know and knowing is half the battle you
Info
Channel: ng-conf
Views: 10,105
Rating: undefined out of 5
Keywords:
Id: dwDtMs4mN48
Channel Id: undefined
Length: 43min 47sec (2627 seconds)
Published: Fri Apr 20 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.