What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
Node.js Testing Strategies
by Rob Conery
Tips and Techniques for creating simple, elegant tests with NodeJS and Mocha.
Start CourseBookmarkAdd to Channel
Table of contents
Description
Transcript
Exercise files
Discussion
Recommended
Introduction
Introduction
Welcome to NodeJS Testing Strategies here at Pluralsight. My name's Rob Conery and I will be your host for this course. If you need to get a hold of me, you can reach me on Twitter @robconery, or at email, rob@conery.io. Those addresses are in the bottom left-hand side of your screen. If you want to leave a comment, you can just drop a comment in the discussions list down below and I will get back to you as soon as I can. In this course we have been retained by The Rocket Shop. They're going to send people to Mars and they're going to crowd source it and they need to take signups and subscribers and they're going to put you on a rocket and send you to Mars so we need to build their site, and for that, we're going to use Stripe, a mobile payment solution that I love and is loved by many developers. We're going to set up a subscription system using their API, fully documented up here at stripe.com. Go have a look, set up an account if you haven't. Stripe is an amazing payment system. I've used it for years. Their API as well as their service is fully documented. Go take a minute and go to stripe.com\docs and have a look and I think you'll be as impressed as I was. So, what are we doing in this course? We are going to dive into building an application using the testing tools provided in NodeJS. This includes using things like Sinon to stub out API calls and database calls. We're also going to work directly with the Stripe API to work with recurring billing and see how it works with credit cards and their token system. When testing your application, it's always nice to have good tools at your disposal. You don't have to, but if you do, sometimes those tools help you to resolve patterns, good software design patterns and those good software design patterns will help the design of your application and if all of that stuff works together really well, that's the goal, then you'll have better tests. It's kind of a recursive pattern that we're going after here, running tests and using tools to help us clean up our tests, which drive us to use different patterns, and then, well, you get the idea. Speaking of you, you should be familiar with NodeJS and the basics of how it works as well as JavaScript. You should also be familiar with testing, both unit testing, behavior driven design, whatever, just testing in general. You should've written some software before. This is not a beginner's course, so ideally you understand the ins and outs of writing software. Finally, you should be willing to be patient and take your time letting things roll out and willing to change them as we move along and discover various problems. As for me, well, I've been working full-time for the last two and a half years with Node. It is my go-to platform for working on the web. I have been a web developer for well over 20 years, nonstop. That's all I do and I've grudgingly used JavaScript for the last 15; I'm not exactly happy about it, but I've come to know it and use it every day. I like exploring new things, including various patterns and ways to make things hurt less, and ideally I will be able to show you a few of those things in this course. As mentioned, our client is The Rocket Shop. They need a subscription application and we are going to build the signup system for them. We're going to take our time and go a little bit slowly, but we will get into the weeds as our application becomes more complex. Specifically, we are going to get to know the basic testing tools that you can use with NodeJS. Then we'll have some basic testing strategies, structuring our test, using dates, and so on. They're going to be working with data in a database. That's always a problem when you're doing testing. And finally, we'll get into advanced topics like mocking and stubbing and what about that asynchronous JavaScript stuff? This isn't easy, especially when things get really complex and we're going to tackle it all today.
Installations
Let's start off talking about the tools that we're going to use today and it is impossible to do any kind of testing without wondering about your IDE and for that I'm going to start off talking about WebStorm. I should say if you're a .NET developer and you use Visual Studio, then you might be less interested in this part, but if you're a regular Node developer, I want to show you what's out there, as far as what can help you write tests and execute those tests and you have to start with WebStorm. It is amazing. I really like it. It's cheap. Look at that, 49 Euro, which translates to just about $49 dollars right now to U.S. prices. It's got a ton of features including the ever wanted IntelliSense or our code completion. It's got little helpers that will help you write better JavaScript. It's got inspectors and linters and you can use snippets, you can use so many things. I really recommend WebStorm. It's a great tool. That said, I won't be using WebStorm today, I'll be using Sublime Text. This is one of my go-to editors, especially for doing screen casts. It's beautiful to look at and it's really fast, so that's what I'll be using. It works great with Node. It's got lovely code completion and if you're curious, you can buy it for $70, which oddly is more than WebStorm and I'm not sure what the deal is with that. There is also Atom from the GitHub folks. If you want to try something other than a big IDE and you want to use just a regular text editor, Sublime or Atom are great choices. Atom is free and it's from the folks at GitHub. It works on top of Google's Chrome engine and the only down side to using Atom is it's just a little bit slow on startup, but the editing process is rather fast. It also has excellent package and theming support. I really can't recommend Atom enough. Any of these text editors that you want to use are just amazing, so pick one, if you're not using a big IDE like Visual Studio, and install it. Next up, of course, is Node. Head over to NodeJS.org and there you'll see the current install. As of the recording of this screen cast, it is version 0.12.0. I have Node version 0.12.0 installed and I know this because I opened up my command line and typed in Node-v. Also, after you install Node, please be sure you have NPM installed and you can do that just by typing npm. Our testing framework today is going to be Mocha Js and you could read more about it at mochajs.org written by none other than Mr. T.J. Holowaychuck, a name you're going to get to know when you start working in the Node universe. Mocha is fun and we're going to see more about it in just a bit. For our mocking and spy framework we're going to use Sinon.JS and for working with dates, we're going to be using Moment.js. We are going to install all of this in due course; I just wanted to introduce them up front to you. Speaking of installations, let's take care of installing Mocha now that we have npm and Node installed and to do this we just say npm install mocha and I'm going to use the -g flag to make sure that it is global. In other words, I want to be able to access the Mocha runtime from everywhere. Just like that, I have Mocha installed and you can see when I just type in mocha, it's looking for a test directory. It can't find one. Believe or not, that error means that we did things right; it is installed. Well, now let's just jump right to it. I'm going to go into my demo directory here and I'm going to take a look at all the files I have, which are none, except for that annoying .DS_Store, the MAC nugget. Thank you very much. I'm going to make a directory for our project, call it rocket-shop, and let's change directories into rocket-shop. Inside of here I have nothing. It is completely blank because I just created it. What I'm going to want to do now is to make a test directory so that Mocha can run. There it is. All it needs is a test directory with some tests in it, or not, as you can see, and we can run it to make sure that it works. It's working! That's awesome. So let's see another way that you can run Mocha. You don't have to run it globally. If you want to run it locally, you sure can. Here, instead of using the -g flag to install things globally, I just hit install Mocha and save that reference in our package.json using save dev. So what does this buy me? Well, let's take a look at our directory structure now. Inside I now have a Node modules directory as well as a test directory and inside that Node modules directory, if we take a look, you will see we have a single directory called Mocha. Inside of here we have bin, images, lib, and so on, but if we take a look inside of bin, which is a special executable directory, you can see we have Mocha as well as a semi-hidden _Mocha directory. Those things are not terribly important, but what you should know is if we take our way all the way back up into our rocket-shop project directory, I can execute the Mocha runtime straightaway. So if I just say, hey, command line, execute node_modules/mocha/bin/mocha. And there it is. It ran Mocha for me. Now why would I even want to do this? Well, if we take a look at the npm commands at my disposal, we have a bunch of them in here that I can override and one of them is test. Well, why do we care about this? That's a nice little bit of abstraction on top of executing the tests in our project. In other words, when we want to run tests now we'd have to type in mocha, but what if we change things later on and we want to add some flags and so on. We would have to communicate that to the rest of the team and to everybody else who runs our project and wants to run our tests. However, if we just script out npm test and put our commands inside of that, it makes things a little bit easier. Let's take a look. So to do this, I first want to make sure I initialize an npm package inside of rocket-shop and I do that using npm init. It's going to ask me a few questions here, specifically what version, what's the name of the module, and what's a friendly description that others can reference? And then we get to the test command. Right now it senses that Mocha is installed and so it's defaulted that for me and if that wasn't there, I would just put mocha, but what I can also do is I can say, hey, execute this instead; node_modules/mocha/bin/mocha. Run Mocha locally, not globally. Now that I've done that I can just add in my author and license information and we'll say yes. So what did we just do here? Well, basically this created a package.json for our Node project and hopefully you know what that means. If you don't, then you might want to go review some Node courses here at Pluralsight. Well, that's interesting. Here I have a package.json. What did this buy me? Well now I can just do npm test and as you can see, it executes Mocha locally. If I want to pass in some special flags, I sure can, and we'll take a look at that in the next clip. For now, let's charge ahead and install the things that we're going to need. Next up is going to be Sinon. We're going to use this for mocking and stubbing later on. Here I want to make sure that I use the save dev flag. This is going to save Sinon in our package.json as a developer dependency. Next up, I'm going to be working with dates so let's install that straightaway, and here I'm going to use --save. This is going to be a dependency for the entire project, not just for development. Alright, well when I run Mocha, everything is working really well and when I run npm test, same thing. It's running really well, except now it's running with a local binary instead of the global one. And by the way, I want to point out if you're following along at home and you're wondering why I can type l and show the contents of a directory instead of ls -la, well that's because I have l aliased to that command. It makes things a little bit easier on my fingers in typing. So let's make sure that everything is wired together really well and for this I'll use vim and I'll just crack out a throwaway spec and I will mention that this is the only time I'll be using vim. If you don't like vim, well, then don't worry. It's the only time you're going to see it. So what I want to do is I want to throw together some basic Mocha functionality just to make sure everything is going off the way I expect it to. Here I'm using some basic Mocha syntax. This is two keywords that you're probably going to use 95% of the time, describe and it. Describe is basically a block. Inside of that block you're going to have bits of functionality. How you put that together is sort of up to you. Here I am doing typical behavior-driven design. I have a feature as my outer block and I want to describe that feature in terms of scenarios. In other words, when I do this, what happens? And then the it blocks in between, those describe the behavior of the feature for a given scenario. Now it's quite a mouthful, a little bit of jargon. Don't worry, we are going to see this in-depth. Alright, I'm going to use the built-in Node assert library and here I'm just going to assert true. I just want to make sure that everything goes off the way I expect. Now let's take a look and make sure that Mocha is going to execute as we expect. I've quit out of vim after saving the file and I run it. Here we have one of the best things about Mocha, nicely formatted output that corresponds almost exactly to the blocks that I put together inside of here. In other words, we have a feature and we have a scenario, and then we have the executable specifications. Very typical BDD stuff. Now using Mocha, as you're going to see, doesn't mean that you have to do BDD. You can do straight-up unit testing, but I just wanted to show you the BDD stuff first because that's what I like the most about Mocha. Alright, well before I go any further, let's be good developers here and create a repository, source code repository and for this I'll be using git. I find that git helps me to think, in testing terms, quite effectively. In other words, whenever I do a new feature I'm thinking about different specifications or scenarios. I will often create a branch. I usually will just do a branch per feature, but it's really interesting when your tests reflect your git history and you go back and you want to know what you did and where it was. Well, it becomes really, really simple to look over your history and figure out how long it took you to make a certain feature. I like it, at least. Anyway, taking a look here we have nothing so we need to add everything. I've created a git ignore file and package.json. Notice I am not including Node modules. You'll want to make sure that that is in your git ignore because you do not want Node modules included in your git repository. They tend to be rather large. Moreover, when people download your module, your application or what not, all they have to do is say npm install. Npm is smart enough to look at package.json and look at the dependencies that you have when you used npm install, --save, or save dev, it'll go and grab those things and it'll drop it in your project for you.
Optional Installations
Every developer has their favorite set of tools, so let's take a look at some optional installs that you might want to take a look at; however, I will start this off by saying that when it comes to testing and working in any platform, simplicity wins in the long run, almost always. What does that even mean? Well, it means the fewer dependencies you can take on an external tool, the better, but for some people, some things are just not negotiable. They want their coffee script, or maybe they want to use underscore or some other assertion library that they find to be a little bit more understandable. Specifically there is one called Chai JS that a lot of people like to use because it allows you to write your assertions in a little bit different vernacular. Here it uses the should syntax, which I absolutely dislike. If you've watched any of my videos before and I've done any testing, you've heard me say that either something does something or it does not. Should is a wiggle word and I do not like it in terms of testing, but that's me. There are also expectations you can write with expects and also just plain old assert. However, if you think I'm full of hot air and you like your shoulds, that's fine too. There is a library called Should. I've used this before actually; it's pretty good when it comes to assertions. I just wish they could get rid of that word should. I don't like it, but anyway, should is a great assertion library if you want to do something different than just plain old vanilla assert. However, for me personally, I can get along without any of these extra syntax niceties, although they are pretty nice. Take a look at this should have property, should not be okay, should not exist, should exist. All of these are abstractions on top of programming concepts, but they do drive clarity in your tests, which is important because if you want to use just a plain old assertion library like I do, you don't have many choices. You have assert.fail, assert.ok or just plain old assert, and you have equal, not equal, you have deep equals for deep objects, and so on. You can see all the assertions here. Personally, I like this better. I like it better because it's part of the core node framework. I use the assertion library all over my project and if I don't have to take a dependency on another library then by golly, I'm not going to do it. But that's just me. That is my opinion, I want to make that clear. But if you like your should and so on then you can use Chai or Should. I just want to point out that they are there. Another tool that a lot of developers really like is CoffeeScript. It helps you avoid a lot of common pitfalls with JavaScript and the syntax is nice to look at. It's nice and clean and clear and it draws a lot of inspiration from Ruby. I like to use it for tests sometimes because clarity with tests is really important. So let's install it and you can take a look at how to wire this up to Mocha. Here I'm installing it globally using npm install with a -g flag and it goes in rather quick and all you got to do is say, hey, coffee are you there, and it is. So I told you how you weren't going to be seeing vim again; I lied. Let's create a mocha.ops file. These are options that Mocha will read in and they're basically just flags that you can send in at runtime. I like putting these in an ops file because people can have a central location where all the different options go in. Here I'm specifying that I want the Coffee Script compiler to be pulled in whenever Mocha runs and I'll just do that using the syntax you see here with the -- compiler's flag and I also specified that I wanted the BDD reporter. Alright, well let's create another spec and this time I'm going to use the .coffee extension. That's going to tell Mocha that hey, do I have a compiler for this file, and it's going to look at our compiler setting and say, ah-hah, I do. So this is why a lot of people like to use Coffee Script for testing. It is really clear and the syntax is simple to read. Look at this. So the JavaScript that I just wrote versus this Coffee Script. I don't know, you take your pick. I'm not a big Coffee Script fan, I won't say. I just like writing plain old JavaScript, however. I find this syntax so clear that you can show these tests to any developer if they know Coffee Script or not, and they should be able to understand it. Alright, I have written out our second spec here using Coffee Script. Now let's run Mocha. There it is. It was pulled in and there was really no visible change in terms of speed and it's just really nice to see that Mocha works directly with Coffee Script. What do you think? Let's take a look at these tests side by side using the Coffee Script syntax here and then let's flip back over to the JavaScript spec. It looks a little bit different. Now admittedly, I have syntax highlighting enabled here for JavaScript and I don't use Coffee Script normally, but if I did, you could see syntax highlighting for this as well. Either way, just focus on the text itself. Which one do you think would be easier to communicate to other developers? Honestly, in my personal opinion, I don't use Coffee Script so it doesn't make sense to me to have it simply for tests. However, I can see the merits of it, so to each their own.
Setting up Webstorm
Well you heard me gush about WebStorm earlier in terms of IDEs. Let's see how it handles testing. I do want to show this to you when working with a framework like Mocha. I'm going to start out with a problem. This is WebStorm, it's beautiful; however, it requires Java and if you're running on a MAC, oh boy! That is a big can of worms and if you have problems with Java and the security of Java, then may WebStorm is not your ticket. However, a lot of people don't care. I'm one of them, and the SE6 runtime is recommended by WebStorm. It seems to work well. I haven't had any problems. Okay, well, let's open up our project inside of WebStorm; you can see I have it open here and straight away you can see we have a bit of a weirdness. It's not a huge weirdness, but those gray little squiggles under describe and it and require, what's going on here? This ID is supposed to recognize this stuff what's happening. Well, you know, the problem is if we go down and we'll take a look at which JavaScript libraries we're using for this page, well you can see we're missing a few. We want to specifically have NodeJS Global checked here as well as Mocha definitely typed, which I had to go and download it installed so I had syntax recognition. Well, for some people, this is worth it. You can see the difference right here. They are now italicized, which means they are keywords. That's exciting for people. Well, it's not a big deal for me. I don't like wrestling with IDEs if I'm being honest, but once you get it in there, then I guess it's done. Well, now that our syntax looks right, let's go to that little dropdown up there and we're going to edit our configurations for our project and this is going to help us run Mocha. So once I click this button, I'm going to go and click the plus and there is Mocha. I have a new runtime configuration I can create and right now it's unnamed. Let's change that and I think I'm going to call it all tests. Inside of here we have our checkboxes we need to check and our text boxes we need to fill out, but basically what we're trying to do is tell the configuration where things are. Specifically, it needs to know where the test directory is, so we'll do that and we'll tell it to include sub directories as well and that's that. So, well it's not all that hard, I suppose, and now that we've done this, we have a configuration, we now have a run button and boom! We have a test window that runs these things and it breaks things out in a really nice way. Look at that! We have green because all of our tests pass and it puts it in a hierarchy which is easy to see. Low and behold, we also have a debugger, which is amazing and this is where having an IDE really pays off. Look at this! I can inspect all of these things and I can take a look at the assertion library if I'd like. I can take a look at locals. I can set watches. I can do all the things that you would expect from a debugger. This right here is where all my grumbling about IDEs tends to go away because having a debugger like this and also having a test window that works nicely now, that's really worth a lot, and this thing is less money than Sublime Text. I think that's pretty darn funny. Alright, let's go and take a look at one of the key features that I really, really like about working with WebStorm's test window. So if I run all tests again, you can see that boom! We have our output here, but we have this special little button over here in the corner and that is the auto test toggle. So if I turn that on, that's going to run our test for us whenever the file changes. Now if I go over here to set auto test delay and I set that to one second, that makes a really interesting thing happen. Watch when I change this word and I hit save. Boom! It reruns the test. That is great in terms of a feedback loop. Now I think that's really, really handy. So if I go up here and I change this to a scenarion, which I think, I don't know, is that proper? I don't know. Anyway, it changes and it runs. That's neat. Alright, one last thing I want to show you is if I put this window into floating mode, I can just pop this over into, let's say a second monitor if I want, and I can have all my tests go off as I write code. Here I have kind of limited real estate on my laptop so it's a little bit difficult to see, but if I change this to, let's say false and I give a reason, oh-oh, my test will fail, and what I can do here is to try and figure out what's going on. I can click and I can see a diff. At first I can see the error itself, but then I can see a diff of what was expected and what I got and that's pretty neat if you have a big, fat output like an object output and you want to see specifically what's changed. If I want to go to where the error happened, I just click on that first link there and boom! It takes me right to the line where the assertion happened. This is great if you have a ton of tests and you want to know, well, hey, what's failing where? Let's say you could just click on it and you go straight to the test. Now if something does crash or break and somebody committed a change that you really don't like, well, look at this. You have a git window down below. You can take a look at the log and the graphical display right in your IDE and that is pretty nifty, I have to say. Alright, well, the last thing I'll show you here is that you can use TODOs if you want, and the IDD will register that for you. It's a little bit wonky. It takes a bit to figure out how to do this. It's reading all the TODOs that are inside of my Node module so you see a ton, but if you look in current file, you can see the one I just created and it tells you the line and position of it, and it's pretty, pretty useful when you have a big code base and you know you have things to do and leave yourself a little TODO note. So that's setting up testing with Mocha and WebStorm. It's a fascinating IDE worth all of the money, if you ask me. Even if you're a Visual Studio fan, working with WebStorm in addition to it, well, that's a pretty good idea especially if you're working on a Node project. As you can see, there's lots of bells and whistles in WebStorm that, well, frankly don't exist in Visual Studio.
A First Test
Well now that we have our tools installed and we have our IDE picked, let's structure things up and write our very first real test. So I'm going to be using sublime text. Let's open that up from the command line, and here it is. If you're curious, I am using the programmer theme here and I am using the flat land dark syntax color scheme. In addition, I'm using Inconsolata as my font. Now I say all this because a lot of people have asked me, what do you use for your color scheme and theme and so on? Alright, well, as you can see here, I've got the rocket-shop directory open. Inside of it we have Node modules, we have tests, we have git ignore, package.json, and .idea. That is from WebStorm and geez, wasn't I just saying how much I liked WebStorm? Yeah, we wanna get that thing right out of there. It is only settings and so on, but yeah, I don't really want it. Okay, I'm gonna delete the entire text directory because I am not going to be working here inside of the main rocket-shop directory, because all we are doing is we are building the subscription system. So for this I'm going to create a lib directory and I'm going to make a subscriptions directory inside of lib. Node works with modules and that's what I wanna do. I wanna have a module dedicated specifically to subscriptions. In fact, I am even going to use npm to help me create a package.json because all the dependencies of our subscriptions module I want to be isolated. In other words, rocket-shop itself doesn't need to have reference to Stripe or any of the other things I might need inside of subscriptions. So I'm gonna have this be as isolated as I can. There we are. So we have a subscription module for rocket-shop. We have a package.json. Very good! Okay, well let's keep going and I'll structure up our subscriptions module in a fairly standard way. Inside of here I want to be able to have dedicated models and processes that I can use in an isolated fashion, so I'll create a directory for each thing, models and processes. Now there are different ways of doing this. However, you will find that you'll come up with a way that you like to separate your code. I've kinda followed along with what I've seen other Node projects do and what feels good to me as a developer. Okay, well there we are. There is the structure of our project, lib. Inside of lib we have subscriptions and then we have models and processes and a dedicated package.json. Now for the best part. A dedicated test directory specifically for subscriptions. I don't like monolithic projects. This is one reason I love Node. You can isolate everything into tight little packages including the tests. That to me makes all the difference and it's gonna keep things really light weight for us. What does that even mean? Well, let's open up our module now in Sublime Text and take a look at its structure. It's pretty simple to look at. Models, processes, and tests. Nothing else. If we had the entire rocket-shop project open here in front of us, we would have a lot of files to think about and here we just have our subscriptions module, which is going to keep us focused. Speaking of, let's get off the ground here with a very typical happy path, kind of spec. Here I'm saying that I want to describe what happens when a new user signs up. Here I'm just gonna call it news or sign up as a feature and my scenario is gonna be, what happens when everything works? In other words, what happens when the user gives us a valid email, first name, and last name? Well, I expect them to be let in somehow. I don't know, but the point is I got some expectations down here on screen and I don't have any assertions. What's Mocha gonna do? Well, here you can see it's in blue. It's pending. In other words, it didn't see any assertions so it said, well, if you don't have an assertion, this is the test you're gonna write in the furuter and that is correct. Getting off the ground is always the hard part, so let's think about this a bit more. New user signup? Well, it's not really a feature is it? How about applying for a mission? Keep in mind here that you can change your mind however you like, at any time. The important thing is that you use clear naming and things make sense. So instead of sign-up spec, I'm gonna call this mission application spec, because I'm trying to describe how people are going to actually apply to get in and go to Mars. Naming things is hard and you're gonna see me change up a number of times, but the important thing is that you start somewhere. This is a good start for us and in the next module, I'm going to start writing out tests and we'll get rocket-shop off the ground, so to speak. Sorry. In this module we got things set up for ourselves. We took a quick look at IDEs and tools and then we installed the things that we are going to need like Mocha, Sinon, Moment, and maybe CoffeeScript too, if you're a person that likes CoffeeScript. Then we set up WebStorm to use Mocha and took at look at some of WebStorm's interesting features. Finally, we structured our project as a module and ran our very first test.
Simple Tests with Node.js and Mocha
The Membership Application
In this module we're gonna do simple testing with Node and Mocha and we'll start out pretty simply, just doing the basic stuff, but don't worry. It's going to get complex as time goes on here. So we'll start out doing very simple unit tests and then we're gonna move on to testing with dates using Moment JS. Then we'll get into behavioral testing using a process workflow. Then we're gonna get into things that are a little bit more complex. Specifically we're gonna wanna know if certain callbacks are being called and also if certain methods are firing in our processes. Then we're gonna handle some Asynchronous code. We're gonna start with an event emitter and then I'm gonna move on to the Async library. But for now, let's focus on a simple unit test, nothing tricky. We have a membership application that we need to create, so let's just focus on the basics. Let's revisit the very first scenario that I wanna work with here, which is applying for a mission, using all valid information. Now I wrote originally isn't terribly clear. I wrote email first and last, but we also have height, age, and weight that we need to consider and it lets them in, well, what does that even mean. Well, let's rewrite this. When working with features and scenarios, our assertions should hearken back to what we expect the application or the code under test to do, given a certain scenario. So in this case, when we submit an application with a valid email, first, last, height, etc., etc., what do we expect to happen? We would expect that application to be valid and we expect it to report that the email is valid and that the height, age, weight, and name are valid as well. And this is an important thing. Right from get go here, what is it? Well, it should be obvious when we run our test and see the output, so in this case, it is our feature under test. In other words, applying for a mission, and the scenario that we're trying to test here is what happens when we give all valid information? Email, first, last, height. Well, what happens with that application? Well it is valid and it reports a valid email, height, age, weight, name, etc. Well, alright. Well all these tests are blue, which means they are pending so let's change that and here I'll create a brand new file for our membership application. This is gonna be a simple Node module and I'm gonna use the prototype here and just export that prototype directly. So I'll save this as membershipapplication.js and at this point I've got a number of patterns I can use, but let's go with this simple constructor pattern and I will define some methods on our membership application prototype that will allow me to validate certain things because that's what I said would do in my test. Here I wanna test if the email, height, age, and weight are valid and then I will have an all-in-one routine, if you will. This is valid, and it'll just return the results of email, height, age, and so on, the individual tests. Now you might be wondering two things at this point. Why do I have all these individual methods, and why am I putting the validations here inside of the application itself? Well the first answer is actually pretty easy. There's a lot of functionality that's gonna go into each one of these validations. I wanna keep things clean. I don't wanna have one massive validate method. It doesn't make much sense. I'd rather have things done individually. Moreover, this is gonna help us later on using the Sinon JS library and our spies and I'll show you that in just a bit. Well, the other question is, why am I putting this all on the application? And the simple answer for that is, I like to follow tell, don't ask. This is more of a guideline than a rule, but the idea here is that the logic should stay as close to the data as possible and you want to basically tell an object to do something with its methods. Instead of asking, hey, what's your email, first, last, etc., you basically tell it to validate itself and it does. In the Smalltalk in Ruby world they think of this as receiving a message and they think of methods on an object as, well message receivers. This differs slightly from a classical understanding of methods on objects that are kind of treated like verbs. In other words, if you had a class or an object named dog, you see the classic example of, say hi, and then you see bark, and that is a typical classic example of a method on an object. In the Ruby and Smalltalk world, you could also use a method to send a message to that object, like walk with me or heel or sit. Anyway, that's the idea here behind tell, don't ask. You wanna tell an object to do something and you do so through its methods. Okay, so back to the code. At this point I am going to be passing in some information through args and I'll need to set properties on the application itself and I've got a lot of work to do here because, well I might have undefined things and so on and I'm gonna have required stuff that I need on my object. For this, let's use Node's assert library. This isn't just for testing. You can use it all over your code. When you expect something to come in, then you can assert that it should be there, in this case first and last. If it's not there, then an exception will be thrown and in this case, I have to scratch my head a little bit. Is that what I want? Do I wanna really stop execution if I don't have a first and last name? I don't think so. Let's take this out and instead I'm going to use Underscore. I'd rather not be that strict, at least not yet until I know what I'm doing. So I'm gonna install Underscore using npm install underscore. Now if you don't know what this is, it's a utility library that allows you to do all kinds of things. In this case, I'm using the extend method on Underscore and that's gonna take all of the properties and methods on args and graft them on to my membership application. That'll be nice for all the properties that I send in. So then let's fill out the validations here for email. I'm simply checking that email exists and there's an at signal in the email string. Alright, the next thing we need to do is fill out height, age, and weight validations. You gotta remember, we are taking rockets into space and there are certain height limitations as well as age limitations. I know, please don't send me email. These are just rules that were handed to me from the Rocket Shop, what can I say? We also have weight restrictions because, well, as you know, weight and fuel, they go hand in hand. Alright, looking good. Not classic TDD or BDD for that matter. We wrote our expectations here as we should've done, but I didn't exactly watch everything fail and do the red green refactor. That's okay. As you'll see, I'm not dogmatic about this stuff. And again, I wanna state that this is not about theory and testing theory, this whole course. This is more about the tools and how you use them in Node. How you put them together and write your tests is up to you. This is the way I do it and hopefully you can see that there will be differences, probably, between the way you do it and I do it.
The Membership Application, Part 2
We've written the skeleton of our simple unit test for our membership application. Now let's get in there and make these tests pass. The first thing I wanna do is I want to have a variable that represents the state of the thing under test I want to have that created in a before block. The before block in our test here in Mocha, goes off before everything else does in this block. So this is going to arrange the object that I'm going to test. I don't wanna have to keep firing all the logic down below. I'd rather one variable here represent the result of our scenario. If you find yourself changing that variable or working with a new one down below, consider creating a brand new block for it, because that usually means you have a whole different scenario. Alright, well here we go. We have a valid application that we're setting up here in our before block. We have initialized the variable above so we can test it down below. So this is how you do this in Mocha. You just use a callback and that callback should contain some kind of assertion. Mocha is going to look at this and see if it passes and if it does, it'll give it a nice green checkmark. Speaking of, let's see if we can get one of those green checkmarks. Here I'll just assert that our app is valid and if it's not, then I'm gonna give the assertion library here a message and it's that easy. Assert or assert.ok, those are synonymous. Before I run this I want you to take a look at the way the code is laid out here. It's very simple. It's easy to read. I don't have a lot of blocks. I have a number of assertions down below. Your eyes can easily scan this page. This is important. When you start building out your spec files, things are going to get complex. You're gonna have a lot of tests to wade through. What you really want is the simplest possible thing that you can do. This is why I don't like to take a lot of dependencies on other libraries. I don't wanna have a lot of strange syntax. Here I'm keeping this bleedingly simple. Of course, that's gonna change, but that is going to be the goal because what I'm gonna drive for here is clarity. I want my test to look the same, pretty much, on the code page here as they do when I run them, and speaking of, let's do that now, and I'll flip over to the console and run Mocha and first, yay! Second. Read this from top to bottom. Applying for a mission using valid email, first, last, height, age, weight is valid! It kind of reads okay, but the idea here is I'm going for as much clarity as I can so I'm gonna be doing this back and forth where I read things to myself, go back to the code and tweak it. I'm gonna change a lot of the wording here to be as clear as I possibly can. So for now, let's focus back on our validations. I'm going to pop out an assertion here for email and for height and weight and name, and I'll go and I'll just fill these in. Height is valid, age is valid, weight is valid, and name is valid. Now if any one of these things failed at some point in the future, well then we'd know our is valid function failed for a particular reason. Oh boy, and I run into a problem, and it looks like our final test here, valid name, looks like it doesn't exist. So heading back over to the code, sure enough it doesn't. I had thought that I put that in there, and I didn't. Oh boy. Well, that's okay. It's only gonna take just a second. I wanna be sure that our first and our last names exist for the application, so let's go back and we can try this again and re-running it. Mocha, boom! All green checkmarks. That's exciting! Functionally, I'm happy about this, however, not terribly happy about the way this reads. Let's read it together. Applying for a mission using valid email, first, last, height, age, weight is valid. Reports a valid email. Hmmm. There's a lot of repetition. Reports A, reports valid, what does that even mean? I could dig this thing to pieces. I think I'm gonna rethink this a bit. Let's go back in here, applying for a mission. Let's just say membership application requirements because that's what we're dealing with and here we're dealing with validations and here let's be specific. Application is valid if all the validators return true. Much clearer. Let's go on down the list here and see if we can tweak this to reflect exactly what our tests are doing and what it is we are asserting. (typing) Oh, much better. Two things I really like about this. The first, it reads well. It reads as if you're reading top to bottom or sort of like a check list. The second thing I like about it is age assertion is one line. It's easy to read and that makes me feel as if I'm not trying to do too much. Alright, well going over this again, there's still some tweaks we can do here. A little redundancy. All validators successful. I think that makes a little bit better sense. Let's tweak this here. I want this to be very specific. So let's have it say application valid if, all validators successful, that makes good sense. Okay, now let's run this. I like it. Application requirements. Its valid if, all validators successful, email is 4 or more characters and contains an @. Okay, you might thing I'm being a little bit nutty about this, but remember, you're going to be reading this an awful lot. It should make sense to you. If it doesn't, then you might be doing the wrong thing. Now we can output this, if we want, in HTML format. All you have to do is pass in --reporter doc to Mocha and then it'll output HTML as you see here. I could output this into a file and if I wanna be a complete nerd, I can take that file, print it out, and take it to the meeting. Here it is. I can show my boss. Look at! This is how everything is going. Ah, geez, I did that once and I got laughed out of the meeting; I did, really, I am not kidding. I took my little check list here, I printed it out, took it into a meeting, and said, look at what I've been doing! And they just, they laughed at me and they said, whatever Rob, so I suppose I have to pass that along to you. It might sound like fun, but you don't wanna print this out and take it to a meeting; you will get laughed at.
The Sad Path
In the last clip we focused on what happens when everything goes right. Well, that is not exactly reality, is it? Well, we can make sure that that is valid by now working on the sad path. What happens when things go wrong? So this first block you can consider to be, well acceptance test. It should always work. When everything goes right, you wanna make sure your app always behaves exactly the way it's supposed to. Well, on this second block here, what we're gonna do is throw stones at that, so what happens if, in other words our application is invalid if email is 4 characters or less, and so this, of course, should not be valid. So we are going to set our validapp.email to d@d and that is going to violate our length. For those of you familiar with Mocha and Node and you're looking at this saying, ah, what are you doing? Yes, this is not exactly a good thing to do. I will talk about why in just a second. For now, I just wanna see if this will work. So here I've just reset the email on our valid app and I want to assert that the email is not valid. So let's do that and our test passes. It might not look like there's a problem here, but I violated one of the things and I said not to do above and that specifically is I am changing a test variable from a different block and that could have disastrous consequences later on. In fact, you will see me back myself into the woods in a later module. For now, what I want to do is I want to not do this. I want to make sure that I have variable specific to each block here. In this case I'm just going to drop it right inside of my spec. I don't need a before block for this because this is a simple test all in and of itself. So here I'm just gonna create a brand new application with a bad email and make sure that it's not valid, and in fact, it is not. Our validators expect the email to be greater than 3 characters. Okay, the next thing I wanna do is I want to make sure it has an at sigil in it and for this I'm going to once again create a new application and there are a number of ways I could format these tests, but it's simplest inside of here to just create a brand new application for each expectation. But didn't I just say you should have a variable specific to each describe block that's under test? Well, I did, but in this case my golden rule of simplicity trumps everything. Well, I'm gonna let that have free reign. It's just easier to create a brand new membership application each time. That way I can read it clearly and I can be certain that it is going to have its own instance that is under test. So that's what I've done and as you can see, I've got a lot of tests here and I'm testing each one of the validators. Let's take a look at how it runs, and it runs really well. So look at this. The application's valid if, and you can read it really well. It's invalid if, and it basically shows the opposite of what's above it, which brings something to mind. Why do I need both test cases? It's almost like saying black is not white and white is not black. Hey, both those things are true. Well, really, that's kind of ridiculous. It's also a bit redundant so let's just go up here and have our blanket validator tested and make sure that the application's valid in that case and, you can see, it is valid, and by definition if the email or height or age or weight are anything other than valid, it's gonna be invalid. So in this case we've covered everything nicely. No code is the best code and that includes tests.
Testing Dates with Moment
No matter what framework you work in, testing with dates is a pain in the butt and that includes Java and C# and just about every one of them out there, including JavaScript and Node. What do we do when we have to work with dates and test with dates? That's just what we have to do now. Each application needs to have some form of expiration. So for that I'm gonna have a valid until date, oh, but what I am gonna do? Well, when you have to think about something you can use the skip keyword. That tells Mocha, hey, no matter what I put here, skip this test. You can use this for when you wanna think about things or when you'd normally just comment it out, as I am prone to do quite often. And before I run this, I wanna point out that I'm working in my membership application get branch. So anyway, I run Mocha and I got one pending test. There it is. I have no failure and it just is gonna sit there pending because I put skip on it, that's good. Skip has a counterpart and it is called only. This is great for debugging. So if you have a problem test and you wanna just focus on it and not have everything else run, you can use only and that will isolate that test and only run that test. So in our case we don't have an assertion in there so oddly it's passing, anyway. I'm going to tell this to skip because we need to solve our date issue and for this I'm gonna use Moment.js. Moment is one of those modules where if you have an application that has any kinds of dates, then you expect it to work, and you're gonna wanna install it. This is just pretty much nonnegotiable. You gotta have something like Moment to work with dates in JavaScript. Here is why. It handles formatting, it handles date math, it handles sugary, fun syntax stuff where you can see if something starts at a certain time or it's at the end of a day you can have, as I mentioned, subtract and add for date math, and you can also specify certain points in time and you can see if something is between two points in time, etc., etc. Moment is absolutely critical for every JavaScript project. For us in particular, it's going to help us with our exploration. So let's head back over to our membership application and right at the top here, after we do our extend, I am going to interrogate valid until. I wanna make sure it is always present. Here I am setting the valid until variable equal to a Moment object either way. I'm checking to see if valid until is sent in on the args. That would be a date or a string or something that Moment can understand. If it is sent in, I'm gonna have Moment parse it and that's the first argument there. I just call Moment directly with args.validuntil. Otherwise I'm going to say Moment add 10 days to today. And that's that last statement right there and that's basically saying that this membership application will be valid until 10 days from now. That's how Moment works. So this means that Moment is going to be deep in the guts of my project and I am okay with that because it's easier to work within JavaScript dates. Alright, well let's test this thing out. So I'm gonna create a brand new membership application and I'm gonna create a brand new membership application and I'm gonna pass in valid until and this is going to pass in just a straight up string with the date that's in the past and down below I'm gonna assert that our app is expired. And that brings to mind that I don't have an expired function. So let's go back and write that and what I'll do is I'll just use Moment and say this .validuntil is before today, and in case you're wondering, if you just invoke Moment as I've done, that defaults to today's date. This is the power of Moment. I'm not using JavaScript dates. I'm using Moment directly here and it's very much a part of my application and it helps me throughout my app so I don't need to worry about weird date situations. I can just invoke the Moment methods as I need to. Hooray for me! Okay, well let's see if Moment's going to work the way I want it to. Heading back over to our console and running Mocha, oh boy! Well, I guess not. Okay, well valid until, I can't read that property. It looks like something is failing somewhere, so let's use our Mocha chops and I'm gonna isolate this test by using only. You saw me use only before, but this is one good case as to why you'd wanna use it and let's run it and hmmmm, deprecation warning. Moment construction falls back to js Date. I see. So it looks like if you go chase down that issue, #1407 on Moment.js, it's basically saying, well we don't know what time zone you're in and we don't know if this is a UTC date or what we're gonna do with the string, so don't use a string, okay? Use a new date object. So for this I'll just use date.parse. That should solve that problem. Let's go back and run our test and it's passing, which is sort of surprising because I got an error before. Hmmmm, and that error seems to have gone away. Well, did I solve it? Well, no. Let's go back and check all of our tests now. I'll go back to the code here and remove only and that'll allow me to run the entire test suite. And wait a minute. There's that same error. What's going on here? Let's head back up and see what test this error is happening on. If I go all the way up to the top here, hmmm. You see a pattern? Email is omitted. Height is omitted. First is omitted. All of the things where I said that a given property was omitted or failing, but the other ones seem to be succeeding. Hmmm. Seems to be a problem that is not necessarily related to Moment.js. Let's go back to our code here and see what I might've done. This is a typical way that I'm passing in arguments, but for the arguments where I'm saying something is omitted, I just don't pass anything at all. Ah-hah! I think I know what the problem is. Moment is expecting to interrogate the args and if there's no args, there's no valid until and well, that will cause an error. So what I need to do here is I need to check if there's args if they're not undefined. If args are undefined, it'll return false and so that or statement will trip over to the right side there and it'll set args to an object that should cause our tests to pass, and they do. Now reading the results over, passed the valid until date. Hmmm. Otherwise known as expired. In fact, I even used the word expired down below for my function so let's change that and inside of here as well, I wanna make sure that the is valid function reflects the new expired validator and hooray!
Testing Process Behaviors
As I keep saying, I'm not necessarily dogmatic about how I do things when it comes to testing, but I do really like BDD or behavior driven design, especially when you're putting together process oriented code. So in this case, I need to create the review process for when an application comes in and somebody or in this case, our application reviews it to make sure that everything is okay. This is typically a stepwise process with lots of steps involved and that can lead to some really ugly Christmas tree code. So what I like to do is use Node's event emitter in a structure that you see here. I require the emitter and then I inherit the emitter down below with Node's util library. The next thing I do is I write myself some comments inside of the class itself and what I expect to happen. Here I need to make sure the app is valid. I need to find the next mission for the person who's applying. I need to make sure that the role that they've selected is available, and height and weight is right for that role, and I need to accept the app with a nice message or deny the app with a nice message. I know that each step in this process could change and I might be adding steps, which is likely, and I will probably be taking them away. So while I'm here, I'm just gonna code it. Why not? And this is kind of breaking the dogmatic TDD or BDD the way I'm doing things, but I'm here and it's easy to write. So for this I'm just gonna emit that the application's validated. This is gonna fire Node's event emitter and pass along the app variable. If it's not valid, it's gonna emit the invalid event and it's gonna just pass along a message saying there's a problem with this application. Simple enough. However, it would be nice to know what's going wrong here with the application so, while I'm thinking about it, might as well do it. So this.validation message, this is just going to interrogate the application itself and it's going to figure out what's going wrong and if it's valid, then I'm going to say it's valid. Otherwise I'm going to ask each one of the validation methods, hey are you the one that's false, and well, you get the idea. I'll just return an individualized message. This is going to allow me to use this in my tests. Hooray! Look at that! Nice and clear. Alright, well heading back to review.js, instead of just saying, there's something wrong, I can now call this. Validation message. And moving along, so for find the next mission, I don't really know what I'm gonna be doing here, so let's just stub this thing out and what I will do is I will just create an object that represents what I think a mission will be and we'll have a commander and pilot, an MAV pilot and some passengers, and so for now let's just emit that the mission has been selected and again, I'll pass along the app. Alright, well moving along here, we need to make sure that the role is available and again, why not just stub it out? That's what I'm gonna do and I have no idea what is going to happen about a role or, I don't know, how to select one, so I need more info from the client. So for this case I am simply going to emit that the role is available. And as far as height, weight, age, and being right for the role, well, can you guess what I'm gonna do here? I have no idea and you might be thinking, dude, what are you doing? You're writing a bunch of functions that aren't really doing anything and yes, you are correct. That's okay. To me this is part of the design process, the thinking process. Not only is it important to get the real code out there, but it's also important to think about how it's all gonna go together. In this case I know I have to do these things above and I know that eventually I'm either gonna accept or deny the application, so I might as well just get it on down and make sure that what I'm thinking here in terms of how this is all gonna go together, will actually work. Alright, well we have the mid points and the endpoints of our process. We also need the place where it's gonna start and that will be process application and once this happens, I'm gonna emit an event, basically saying the application was received. Here I'll take a callback as well, next, and that is something I'm going to need to remember, because I am writing things in an event-based way inside of here, but I'm going to need to make sure that I have a way to call back code later on. So to do this, what I'm gonna do is inside this review process instance, I'm gonna create a callback variable and then down below here, I'm going to set that callback variable equal to next. There you go and I should point out here that that should be one line above. Line 53 should actually be on line 52. That'll cause an error. I'll fix this later on. So what I'm gonna do with that callback is I'm gonna actually use it if the application's accepted. If it is accepted, I am gonna return an object that has success true in the message that you see here, and again with deny application, I'll do the same thing. I'm gonna call back. I'm not gonna pass along an error and I'm gonna call back a data object that says success is false and I'm gonna give a message. Where does that message come from? Well, it is passed into the deny application function. Okay. What am I doing here? You might thinking, Rob, have you lost your mind? You haven't really written any code. Well, what you might be sensing here is that I have a bit of a harness going on. Since I'm using Node's event emitter, it's really important to me that I wire it together correctly. In other words, this is kind of structure and glue that I'm putting together right now and I have found that it is much easier to make sure that you get this event chain done correctly before you have any code in here. Okay, so what does that even mean? Well, let's take a look at this. This is our event path or our event chain. When the process application method is called, it's going to emit an event called application received. When that event goes off, that is going to fire a method called insure app valid. When that happens successfully, the validated event is gonna fire and then that's gonna call find next mission and if the next mission is found, well, you get the idea. That is our event path. Well, what if things go wrong? If any of these methods have a problem, it's going to emit the invalid event and it's going to pass along a message and that is our sad path down below. This code looks good to me and when I run my first test, it should all pass, which is exactly what I want. If it doesn't pass or I have trouble, it's going to be specifically because my event chain here isn't put together the right way and I'll need to know that up front. Speaking of, let's write some specs here and see if we can get this thing to work as we intend. So in our review spec here, I will do what I have done for every one of our test files and I will use Node's assert library. I'll pull in the review process module that I just created and also our membership application. Here I'm gonna describe the review process. That is our application feature. Simple enough. The next thing I wanna describe is something basic and simple. What happens when we receive a valid application? And the next thing I need to do is create my before block, as you've seen me do, and inside the before block I am going to create a valid app and for that I'll borrow some code from our membership application tests. Here I'm gonna do something a little bit different. Here I am passing in done to the callback for our before function. That is gonna mark this as an Asynchronous test. This test needs to be Asynchronous because it's going to be working with Asynchronous code. Specifically, when I call process application. It expects a callback. When that callback comes back, I'm gonna wanna fire done so I can tell Mocha that I am done with this before process. Let's just see how it works. Here I'm gonna create a new instance of the review process and then I'm gonna call process application, sending in our valid app. Then I'm gonna expect back some error or a result, hopefully a result, and then I'm gonna set my decision variable to that result and finally, I'm gonna call done. Once done is called, Mocha will continue with execution of our tests. Speaking of, what tests are we even gonna run? Well, let's start with the most basic one. I mentioned before, I just wanna test to make sure that we have a successful return. This is gonna tell me that I've put together the event emitter stuff in the right way, so I'm just gonna assert, decision.success, and if it doesn't happen right, then I'll have a look at the returned message. Okay, well I'm almost done. I've just gotta be sure that review process, there we go, instantiation happens inside the before block, not outside of it. That make everything crash, that would not be good. So let's do a quick review here. I have a feature above, the review process. This is a feature of my application. Underneath that I am describing a scenario, which is receiving a valid application. I am constructing that scenario in the before block and the representation of this scenario is kept in my decision variable right there, and then down below, I am specifying what the behavior of the application should be using assertions. Look at that! I did it right the first time. Oh, that's no fun. I was hoping for something to go wrong so I could troubleshoot it. Alright, well our event chain is working nicely and we have our scenario put together. Let's move on and see if we can break things.
SinonJS Spy
There are a lot of steps in our review process and we're gonna wanna make sure each one of those steps is carried out. For that we can use the spy functionality from Sinon JS. So to install it I'm gonna use npm, just npm install sinon, and then I'm gonna save this as a developer dependency. Trying to explain how Sinon works can be a little difficult so I've found that the easiest thing is to just show it. So let's do that. Here I'll just require Sinon straight away, right at the very top. I wanna use the spy functionality. In other words, I want to know if something is fired on a particular object. For this, all I've got to do is to create a spy and for that I'll just create a variable called spy and then I'll tell Sinon to spy on my valid app object and then in, as a string here, I'm gonna say spy on email is valid. So let's put all of that information up here at the very, very top. These are just variables that we'll wanna use below in our before function. So here I am spying on our email validator and everything is working and that's good. We didn't get any exceptions. Now let's see if the spy is actually gonna work. Here I'll just assert that the spy was called. This means the email is valid method was called on valid app. There it is. So I declare the spy above saying sinon.spy on our valid app object, and make sure that email is valid is called. Down below in the review process it is in fact called, so everything works. Great! Things are working so let's go and add another assertion here and this way I can be sure that our review process indeed validates email, just by making sure that that spy was called. Let's rerun our test. Hooray! We have a nice new assertion. Well, I don't need to use spy.called here in my assertion. I can use a little bit of syntax goodness given to us by Sinon. When you tell Sinon to spy on something, it will actually wrap that object and its functions so you can interrogate those functions. As you see here, I could just write validApp.emailIsValid.called and it will tell me if that function was called. Spying on our valid app object doesn't make all that much sense. What I'd rather do is I'd rather spy on the review process itself and as I mentioned, there are lots of steps in this process and I wanna make sure each one of those steps goes off. So let's change things around. Here I'll put the instance of the review process in the spy and then, having a look at our event path, let's just start with the very first one, ensureAppValid. Let's make sure that that is called. So as you just saw, this should just work because it is being called we know that, we're getting success back. So down below I'll just say review.ensureAppValid is called. And boom! Hmmm. Well what is happening here? Well, this can cause a lot of problems. We are getting back false. In other words, Sinon spy is telling us, nope, it is not being called and the simple reason is, because I'm using events. For some reason, it doesn't work that way with Sinon. You have to have a process that is calling a function directly. It can't be the result of an event trigger. Now I don't know why that is, but if you're using events like I am, you have to do things a bit differently, so here all I gotta do is I have to wire up something that happens when validated is called on my review object. In this case I'm gonna call spy directly and I'm gonna get in trouble for it. Hmmm. This is just something you have to know about responding events. You don't wanna actually invoke the method you wanna use or the function. You simply want to specify this as the function to be called and you leave that to the event emitter to do it. Agggh. Alright, well let's try it again, and ha-ha! Still getting problems. Well now down below here I have to change this to spy.called because our object isn't wrapped. Oh boy! There we go. This works and that's how you work with events in Sinon spy. It doesn't feel very good, but let's continue on here. I wanna spy on each one of these events, so what I will do is I will just wire up a bunch of spies, and there you are. So every time the review fires each one of those events, I'm gonna call spy. So how do I make sure that all of these are called? Well, one thing I can do is I can change spy.called to call count and do an assert.equals. So I wanna make sure that that spy was called, oh, let's say four times. That's it and above it's called, should be called four times because four events go off. And that works, but as you're probably sensing, that's not the ideal situation here. While it's nice that it ensures that it's valid, it doesn't tell us which one is firing and if one of them is not, then we wouldn't really know. So let's create individual spies for each one. We have a validation spy, mission spy, role available spy, and so on. Now I just create new assertions for each one of those spies to make sure that they are called and that's all I need to know and if they are called and everything returns successfully above and my first assertion returns success, well then I know that each one of my validators is being fired and everything is working as I expect. Let's try it. It works.
Using Async
Having introduced Sinon into the equation here, we're starting to feel a little bit of pain in terms of using an event emitter. Let's change that around using the Async library. As I mentioned, tests are gonna lead us to using tools, in this case Sinon, and that's gonna lead us to patterns in considering our design. In this case, using Sinon to spy on an event chain, and that's not the easiest thing. There's probably a better way and that's why tools and tests push us into different patterns and ways of doing things, so let's see what that would look like. In here, I'm gonna remove all of this event stuff. It really isn't using Sinon or spying to its best advantage. It actually made me write a lot more code and I don't really feel good about it and when you're writing more code to support testing, that's a bit of a pain. So what I'd rather do is I'd rather do something a little bit more direct, using the Async library. So here I'm going to just commit my changes and here I'm just adding a little thing about spying and I am going to make sure that I merge in everything I just did because I'm going to check out a brand new branch using the Async library in my review process. This is important. When you make a big change like this, please be sure that you do it inside of a branch. Alright, well let's plug in Async and I've already installed it using npm and a --save. If you don't know what Async is, let's head over to the repo and take a look at it. Basically, it allows you to work with a bunch of asynchronous functions in an organized way. There are two methods that are core. There's parallel and there's also series. There's another one called map, but I'm not gonna deal with that right now. If you wanna know more, head over to the GitHub address that you see above. So, parallel will execute a bunch of functions all at the same time and then give you a single callback to respond to. Series will fire things one at a time. I have to change the way I'm working with the application or the membership application. I'm gonna work on an instance level variable here, so I wanna make sure that I have a dedicated review process instance for every application that needs to be processed so that means I'm gonna use a constructor. Here I just wanna assert that an application was given to us and if it was, then everything will be okay. Well, since I'm working with an instance level variable called app here, this is a membership application, I need to change the way that I'm actually calling each one of the steps in the process. So instead of passing the app along, I'm just gonna pass a callback and that's a traditional Node kind of callback with an error and any data pass along. I can also remove the process endpoints here, accept application and deny application. I don't need them anymore, but I do need process application. I can remove the event chaining. This is great. I am removing code. I think Async might just have to stay. Wow. That's always good man, when you can remove code because you're using a certain tool. Hurrah! Well here I'm not emitting, I just need to call next. In this case if the app is valid, I'm just gonna call next. There's no error, I'll just pass along the app. Same thing here. If there is an error, I'm gonna pass that error along directly and that will be the validation message, and there will be no data to go along with it. This is a very typical Node call structure right here. If's there an error, you pass it along in the first argument. If there's any data, you pass it along in the second. There's no trickery here. There's no event emitting, nothing. Alright, well that's it for process application I just need to call that directly. I don't need to pass in the app, but what I do need to do is I need to set up Async and I'm gonna call async.series. In other words, I'm going to give you a bunch of functions, execute them one after the other. Each one of those functions will be called; however, one of them might have an error. If it does return an error, then this callback will be called directly and I'll be able to handle that error as I'm doing here. If there is an error, I'll just say success is false and then I will pass along any error message. Notice I am not returning the error itself. I don't wanna throw. I don't wanna have any assertions trip on this. Down below, in this case of success, I'm passing along null as the first argument. That would be any error that happened. Of course, there are none. And then second, I'm just going to say that success is true and welcome to Mars! Okay, well let's string together the functions that we're gonna call and they're just one after the other that you've seen above. So the first one that I wanna call is ensure role compatible. And the next one I'll call here directly is, find next mission and role is available. This is all the stuff that you saw me do in the event chain, but in this case I'm just passing the functions in an array and they will be executed in the order of the array. My next task here is to deal with all this event stuff and spying. I'm gonna take it all out for right now because I just changed a bunch of stuff around. I'm gonna skip all of the spy tests down below. I just wanna make sure that it returns success as I expect. And then, I just need to pass along the membership application in the constructor because I changed the way the API worked, if you recall. Alright, well that looks pretty good. Let's give this a whirl and see if I did things right. Nope. Oh boy. I wish I could spell. You know, sometimes I just get going so fast and I start refactoring, I misspell words like it. And well, almost, okay, can't read property is valid of undefined. Hmmm. Well, let's see. It looks like we have a bit of a problem here, again, just naming of an argument. It should be args.application not app, and wow! Look at that, it worked! Yay! Well, you know what? A few small spelling errors, that doesn't really count. It's almost as if I practiced these demos. Crazy! Okay, now that I've done this we can go back and we can set up our spy the way it should be. So here I'm gonna spy on review and ensure app valid. This is what I wanted to do in the first place before. This time, however, I can call things directly and do things in a simpler fashion. So down below here, I can just say review.ensureAppValid.called. I don't need to interrogate a separate variable like I did before. So I can change each assertion down below here by interrogating the review object and the function directly, making sure that it was called with .called. That looks pretty good. Let's take off those skips and go back and run our test. Do you think it's gonna work? Let's see, and it does. That makes me happy. Let's go back and take a look at our code. Take a look at that. It's nice and clean. Single assertions, they make sense. This is what you're going for. You're going for something that feels good and there's no other way I can put it. Do your tests feel good? Well, there's more we can do here, let's see. Async allows us to work with rays of functions as you see here, but there's a cleaner way. Let's take a look at that. So the first thing I wanna do is I wanna create a brand new method called approve application. That's gonna take a callback, just like the rest of our steps above, and for right now, I'm just gonna pass along true without an error. Why would I do this? Well down below here we are using a kind of brute force way of working with Async, passing in an array of functions. I don't need to do that. I can actually name these functions if I want by passing in an object. Each one of these keys is gonna be passed back to us. The value of each one of these keys is gonna be the return value of the function, if it doesn't cause an error. So success is going to be true, in this case. How can I use this? Well, you can just pass results along as you see me do here. Now let's be sure to pass along our message as well. I don't need to set a success flag, because, as you can see here, success is being set by Async using the approve application function. So let's take a look at what this looks like. I'll console.log it to make sure I did it right. Well, not really. I've got a problem here. There's a lot of extra stuff here. It looks like I'm not passing back the data I need to pass back. That's because I forgot to change things around. Specifically, you can see I'm passing the app back here. I should be passing the result of role compatible. It should be probably true, I guess. Either that or some kind of role. I think for now I can probably just set it to true and I'll do the same above. Role is available. I'll just pass back true and here instead of setting the mission variable on the app, I'm just gonna pass back the mission itself. Man! Things are getting really easy. So app valid, I'll pass back true as well. So let's log the results and I want to see the message output as well to make sure everything is formatted right. Look at that. Man! Things just got so easy all of a sudden. I love that. Just by simply using a different approach. Specifically, I removed the event emitter in favor of something a little simpler, which was to have a review process instance powered by Async. In this case, our tests and tools pushed us to a different pattern that actually made life a lot easier and hopefully this is gonna help our design, which will help our future tests, and you'll see. In this module we got started with simple unit testing and set up the structure of our module using a very basic pattern. We started testing our ideas, working with dates and Moment.js and then we got into a behavior driven design, BDD, with our review process. And finally as you just saw, we cleaned things up with Async and Sinon helping us out.
Data Access Considerations
A Look at Node.js ORMs
Well, we've gotten to a point and we can ignore it no longer. We have to think about data access and our test. So that's what we'll be doing in this module. By the way, if you're curious what you're looking at, that's Hogmanay in Scotland in Edinburgh. What a great time that was. So in this module we are gonna do a overview of ORMs and data access with Node. Just a quick overview in case you're not familiar. Then we're gonna ignore it all with SinonJS stubs. Something you haven't seen just yet. We've worked with spies. Next up, are stubs. Then we're gonna work with a repository and dependency injection. We're gonna use some patterns to help us ignore data access altogether. Finally, we're gonna use some helpers and we're gonna finish up the review process, and try and keep our tests light and lean. For the first segment, however, let's take a look at ORMs in NodeJS. If you're familiar with these things already, you can go ahead and skip to the next clip. The first one I wanna talk about is ORM2. This supports, as you would expect, various databases including Postgres, SQLite, Mongo, and so on. It has a syntax here; you just require it. You connect to your database and then you define your model as you see here, and this includes all kinds of schema information, various methods and validations. If you're familiar with Active Record, or things like it, well, this kinda works pretty much the same way. You can see person.find. That's how you do the querying with it. Next up is Sequelize. Sequelize works in pretty much the same way. It supports a bunch of different databases and you basically declare a model and one additional feature that Sequelize has is it allows you to also update your database when you change the underlying schema in your model. I think that's pretty neat. It's also very involved. We'll talk more about that in just a bit. You'll find that as you look over these ORMs, they pretty much all work the same way. You define a model by passing in some kind of object that has schema information on it and it also abstracts data types for you. Here we have generic data type, string, binary, integer, big int, float, decimal, date, bullion, etc. It'll do its best to translate that for you and put it in your database. Alright, well, let's take a look at another one here. It's Waterline. This is the arm that is behind sales.js. It's pretty fascinating. I've used this thing before and I really like it and it does, as you would expect, the same deal. Here you define a model. This is a user and it's got attributes on it and then you tell it what type it is, if it's required, all those good things. It works with the concept of adaptors and as you can see, it works with a bunch of databases, Mongo, Riak, Postgres, MySQL, and so on. If you wanna get underneath the covers, you sure can. You can go in and tell it what table to use, if there's a given schema, if not, if you're using Mongo well then ignore it altogether. There's that find syntax. That is pretty much ubiquitous with some small changes between all of the ORMs that you're about to see. Then there's Bookshelf. This is the one that's used by the ghost project, ghost blogging project. It actually uses Backbone under the covers, and it's again, fairly simple to use. It's built on top of Knex. Knex is kind of a low-level way of working with a database. You tell it what client you're using and where that database is and then Bookshelf works on top of that. It abstracts it a little bit. It allows you to define models, as you see here, using same old strings, Booleans and so on. Now if I sound a little bit jaded a little bit, oh, I don't know, fatigued I guess, with all these ORM discussions, well, that's because I am. I find ORMs to be distracting and unneeded. I really don't like the idea of abstracting the power of SQL away and that's what you get when you work with these ORMs. They sorta wanna work across all database platforms and you really can't get into the nuances of any one particular. I think that's a little bit silly, especially if you're working with something like Postgres. That's a subject for a whole other video. The only reason I bring this is up is to transition to the next part here, which is something that I created. This is MassiveJS. Massive's been around for years in the .NET world, but also in the JavaScript world and I redid it with Massive 2.0 to work directly with Postgres. I just wanna tell you that it's out there. If you're a SQL lover like me, and you wanna work directly with SQL files as functions and what not, well, here it is. It's not really an ORM, and I guess it's more of a data access tool, and as I mentioned you can have SQL files as functions, which I think is pretty neat. You can also work with your typical find syntax, although there's no concept of a model so you're free to query as you please. It also allows you to work directly with JSONB as if you're working with a NoSQL situation, which could be very, very useful when you're working with Node. In fact, it's more than just slightly useful. Often when you're building out your application, you don't wanna think about schema, but you might think about it later on. I'm gonna touch on this later. For right now, let's do our best to ignore this decision and what I'm gonna do is I'm just gonna go in and I'm gonna create a brand new folder. I'm gonna call it DB for now. Inside of DB, I'm just gonna create a brand new file and for lack of better words, I'll just call it index.js. The reason I'm doing this is I want to abstract away the notion of data access for now and I'm gonna use Sinon to stub this out and I'll show you that in 2 seconds. First though, I want to create the methods that I'll be calling later on and I'm just gonna do this using straight up exports functionality. This is going to make our DB module here a singleton. If you don't know what I'm talking about, maybe pause the video for a second and go have a quick review on some of the other Pluralsight courses or go take a quick look on Google at Node modules and how they work. This is actually really, really important. It's a big design consideration when you're designing out your module. Do you wanna have a singleton here that always works with the same instance, or do you wanna return individual instances? I have to tackle this very question in one of the upcoming clips.
Stubbing with SinonJS
One strategy for dealing with data access during testing is to ignore it altogether. And you can do that using a stub system. Now I'm gonna do that with SinonJS. Before we get started, I wanna point out that I created a brand new model called Mission. If you recall, I had to work with the concept of a mission in our review process so I decided to formalize that into its own module here. I'm using a constructor pattern and that constructor is gonna return individual mission instances. There's a status here, commander, MAVP pilot, colonists, tourists, and a launch date. That is important because I'm gonna be working with launch dates that later on. Next we wanna check if the mission needs a given role and that goes along with some logic and we're also gonna be wanting to assign a role to a given mission and then finally, we do wanna know if it's even flying. Next, I created another class called Mission Control. MissionControl is the thing that handles missions, and this, if you notice here in the constructor, it's going to take a database instance. This is dependency injection. Rather than Mission Control creating its own database instance, I'm gonna pass it in. Why? Because testing. When we test we wanna make sure that we can mock and stub and handle all the dependencies that a given class has so that we can test it better. Moreover, if I was to require the DB above, I'm actually binding this Mission Control module to my database module. That makes coupling happen. We don't want that. We want our Mission Control module to be as flexible as possible so I'm gonna pass in a DB instance through the args. Why do I need a DB instance? Well, for this method right here, current mission. The Rocket Shop is going to launch a brand new mission to Mars every start of the month and I need to make sure that I am working with the correct mission. If the current mission isn't set, then I'm gonna wanna create it and I do that down here with insert. In other words, if there's no found mission, I'm gonna use insert here and I am going to create the next current mission. Hooray! Here's a quick test that I wrote for Mission Control, and here you can see that I have an outer block, that is our feature, Mission Control, and then I have my variable that I am going to be interrogating down below with my assertions. Inside of here I am calling missionControl.currentMission. I want to check to be sure that a current mission is created if one doesn't exist, and indeed, that's my assertion down below, and I'm just gonna assert that it is not undefined. This something comes back that's defined or null, or falsely in JavaScript. The problem I have, however, is that when I create Mission Control I'm not passing in any instance. It's just null. That's not good. Well, I didn't quite know what I was gonna do here and so I just decided to pass in null for now and because I wanna see things fail indeed, boom! My assertion goes off. I need a DB instance. How are we gonna get that DB instance? Well, if you recall, I created a module with nothing in it. I could require that module and I can use Sinon to stub it. What's a stub? Well, that's just like a spy, except it actually does something. In this case I'm gonna tell it to yield some values. In this case that means fire the callback that is passed in. It's gonna return null and an object ID1. I should also point out that I can just do this by hand. I can just override the find method on the DB object and I can set it to my own function, as you see here. However, I'm gonna be using Sinon a lot and the more that we can avoid coding things by hand like this, the more we can avoid problems with our test later on. So, that's it. I'm stubbing out our DB instance. Now I can just pass this off to Mission Control. Let's see if it works. Running their tests, and it does. It's that simple.
Setting up a Repository
Repository pattern is a very old pattern. A lot of people don't really like it, especially in the .NET space because it tends to get out of control rather quickly, but since we're doing things modularly with Node, it tends to fit really well. So before if you recall, we were stubbing the find method and it doesn't really make much sense. Let's get explicit and make this thing a repository and if you notice here, I'm trying to get the current mission. That doesn't make much sense. Let's rename it to get mission by launch date and that way I can send in a launch date that I would expect. In addition, we need to have something that inserts a mission here so let's create a method that's explicit. Create next mission. Very nice. I always like things when they're a little bit more explicit. Okay, get mission by launch date. For this we just need to pass in the date. You don't need to have any arguments that can be changed so let's be explicit. We expect a launch date, and there we go. (typing) And if we don't find a mission, I'll need to create a new one and so I'll use the create next mission method on our new repository. Alright, well let's go back over to our tests. Now I just have to stub that new method and see if it works. Running our tests. Yep! However, if you've been watching closely, then you'll notice that, well, this test is really not doing what it says. My stub is yielding an object with an idea of 1. That is not testing to see if the mission is created if it doesn't exist. It does exist. Our stub says it does. Well, you can back yourself into problems easily this way. Well, if we change it to null, which means the mission doesn't exist, well, we have a time out problem. That is because we are not stubbing create next mission. It is being called and that callback is not firing. So that means we need a new stub. That means that we need to create the next mission and in here, we can put a new mission and just leave it empty for now. It would've been really easy for me to miss that. Sometimes mocking and stubbing can be a great benefit, but other times if you don't quite pay attention to what you're doing, you can back yourself into a corner. I'm gonna talk about this a lot later on. For now, here is a properly mocked DB repository and that makes me happy. The repository pattern, as I mentioned before, is very powerful, but if you try and use it over a large code base, man the thing can spiral out of control and the next thing you know, you have overlapping repository methods. In our case, since we're using the module pattern of Node, well, it keeps things kind of small and contained, so I am okay using the repository pattern here. In fact, I think it fits quite nicely. Alright, well with all of this talking, I forgot to run my test to see if it would actually work. Let's do that. Whoops. Well, it looks like I forgot to reference our mission module. We can do that really fast. Drop in the mission, and let's run this again. Yay! Everything works as we'd expect and I feel a little bit more confident about these tests this time. Well, we're almost finished with stubs, but let's take a look at one of the best parts. Down below here, notice the code that I can write. I wanna be sure that get mission by launch date is called so I'm going to do it in the same way that I used the spy before and it works. And this is something to remember about Sinon. Spies are the first level. Stubs are kind of the second. They're just like spies, but they actually return something. The fact is, you can see here I used it exactly as a spy. In fact, having this functionality exist is going to help me write a few more tests. This is good tooling. I like it. Okay, well let's rename our feature to mission planning and then we'll have two scenarios here. No current mission and then if we do have a current mission, I wanna test both possibilities. We have the first scenario covered already with our existing stubs, so what I'll do is I will just copy this down below and put it in line and here I'll just return a new mission when get mission by launch date is called, so I'll just re-stub our DB instance. But I wanna be sure that something comes back that I expect, so rather than just returning a brand new mission, how about I return an object with an ID of 1000? That way I can return mission 1000 down below and I can have a better test. So running this, we've got a problem. Attempted to run, get mission by launch date. Oh boy. It turns out with Sinon that you can't actually wrap a method twice. Moreover, if you take a look at the test I actually wrote, I am just testing that my mocking framework works. It doesn't really do much for me, does it? Ohhh. I'm not exactly happy about this, but let's solve one problem at a time. Sinon is choking on this because it doesn't want to magically decide which stub to use when. So just because I stubbed it in that before block, well Sinon doesn't know that, so it's not gonna make that choice for you. Instead, it's just gonna throw if you're trying to do it twice. It's trying to tell me gently, dude, you're doing it wrong. So there's a way to unwrap it and that's by using restore on my wrapped object. Solving problems with more code. Ooof. I don't like this. I'll fix it later on. For now, let's just see if that is actually gonna work, and it does. Now I just need to go back and tweak my assertions, so down below, I'm asserting that it returns mission 1000 so let's make sure we do an assert.equals here and we'll say currentmission.id is 1000. Well, at least it works. That's a good thing, but have a look at this. Returns mission 1000, and I'm asserting that the current mission ID is 1000, so that's validating that my mocking framework is indeed working, and down below we're making sure that get mission by launch date was called. Well, that's good too, but this test right here smells really bad and as I said before, you wanna feel good about the tests you write. Looking at this, I just don't feel very good about it. I'll have to fix that.
Making Things Easier with Helpers
I think we're off to a decent start, but we're starting to get some repetition and we are also getting some tests that don't exactly smell very good so let's do a little refactoring here. Now the first thing I'm gonna do is I want to remove this repetition, so for that I'm going to use helpers. Using helpers with Node and Mocha, that's a little bit of a manual process. Just create yourself a module and inside of here I'm gonna create a helpers directory with an index.js file. Next, I'm gonna create an export here called valid application and it's just gonna be an object and it's gonna be a new membership application, and that's it. Just like that. Then I just need to make sure I require membership application inside of our helpers and this is actually gonna reduce the code nicely in our tests. So let's format this so it looks a little bit better, and then going back into our tests, I'm gonna require our helpers and that's just right in the same directory here and I can take all of this code out and I'll just say helpers.validApplication and that's going to return the instance of that valid app. Now let's jump over into our other test file here, our mission application spec, and I'll replace it here so now we are working with the exact same valid application. This is good because this is keeping our code nice and dry and that is important, as I keep saying, we want things to look good and make you feel good. We're working with JavaScript after all and we need all the reassurance we can get.
When Helpers Hurt
Every time you use a tool to help you out, whether it's stubbing with Sinon or drying things up with helpers, you gotta be sure you don't abuse things. So looking at this, well we've got a lot of green checkmarks. What could possibly be wrong? That's kind of fun. Why would there be problems? Well, the problem isn't with my code necessarily, it's with me. It's easy when you start working with helpers to say, you know, I've got a lot of duplication in my code, I really should try and dry it all up, and for instance, take a look at this. We are doing some stubbing with our database and it would be easy to think well, geez, let's put this in a helper because I'm probably going to need to use this database instance again later on. So here I'll just move all the stuff into a helper. It makes perfect sense. Why not just export a stub to database? That way I don't have to keep doing it over and over again in my code. If I wanna have things be flexible, which I do, I can pass in some arguments. Those arguments can contain special return values if I want. So for instance, if I want to return a specific kind of mission, I can pass that in in the arguments. Otherwise, I can just return a new mission as you see here. Well, that looks pretty good. What could possibly be the problem? Look at all the code I'm about to save. So let's take out all of this stuff and I'm just gonna say helpers.stubDB. There it is. Well, things look better, don't they? Let's run this and see what happens. It works! See, this is misleading isn't it? You can only see the problem if I come over here to our review spec and decide to reuse our helpers. So let's instance up our mission control one more time, and for this, of course, I'll need to pass in a DB, so let's ask our helpers for the stub DB. Now you might be thinking at this point, why are you doing all this? Well, our mission control needs to be instanced and I'll need to pass that DB into that instance because I need to pass mission control into our review process. Again, I'm using dependency injection here and that review process is gonna need to use mission control when we ensure that the role is compatible and when we accept the application as well, so I'm just preparing things. Well, taking a look at this a little bit longer, if you're a fan of dependency injection, you're starting to see something happen here, which is that your dependencies sometimes become rather large and this can quickly become a very big pain in the butt, because as your processes grow and your injections grow, things become harder and harder to do. So people have responded by creating things called inversion of control containers. Nate Kohari, a friend of mine, created one of the most well-known ones for .NET called Ninject and he also created one for Node called Forge. So if you're into dependency injection and wanna use inversion control, it's there, I just wanted to show you that. For me, right now, I think we're okay. Speaking of, now that I've wired this up, let's run our test just to be sure nothing is broken. Oh, man! Let's take a look at this error. Attempted to wrap get mission by launch date, which is already wrapped. I didn't really do anything other than, oh yeah, I decided to move the instantiation and stubbing of our database down into a helper. Can you see what the problem is? In short, the problem is that our repository is a singleton and we are trying to stub a singleton. This is our tool set telling us there's a problem. That problem came to light because I'm trying to be efficient here by using helpers. So a bunch of things have gone wrong all at once. This kind of stuff drives me insane because I've, one, tried to get too tricky, and two, I've kinda been using the wrong pattern all along and I didn't really notice it. This is the kind of thing that just drives me bonkers, but for now, let's see if we can fix this, because maybe what I can do here is I can just see if our stub has already been wrapped. This is just not a good idea. So if you take a look at our rough method here, create next mission, it has all these properties including this one here called display name. What I could do is I could just see if this thing has already been wrapped. We could use an if statement here, db.createNextMission.displayName, see if it exists. If it does exist, well then don't wrap it again. If you're looking at this and you're saying, Rob, what are you doing? Yes, I will fix it, I promise you. I'm just gonna show you that it works. Why am I showing you that it works? Because it is so tempting to chase bad code with more bad code. Here I've actually made my test suite work with a failed solution and it's so easy to back yourself into this hole without thinking clearly about how you got here in the first place. So let's take this out of our helpers. In general, it's a good idea to keep the stubs and mocks that you create in the test suite in which you're using them, so I'm just backing my way all the way back of here. In fact, let's here put this in a before block so we're clear about how and when we're using it. There, that makes me feel a little bit better. And let's move the mission control variable above here, so it's being set and we can use it down below. Okay, that makes me feel a little bit better. Things aren't failing and I don't have that ridiculous code checking to see if something is wrapped.
Listening to Your Tools
Let's continue refactoring here and we're gonna actually run into some trouble. Let's listen to our tools and do things the right way. What I mean by that is, here is our review process. It makes more sense to just pass in a database here and then the review process can access the modules that it needs. In this case it needs to use mission control so I'll just reference that directly right here and then, I will new up the mission control down below and I'll just pass in the database that was passed into our review process. There. That makes me feel a bit better and we have an error, as kind of we'd expect. I'm not passing a database instance in to our review process; let's take care of that now. And while I'm in here, let's clean things up a little bit. I don't have a before block where I am working with the variables and spies and so on so let's do that really quickly. There we go. So here I have all my variables declared at the top. Decision, review, validApp, and I'm sending that to the Helpers.validApplication. In the before block here I'm creating a new review process and I'll need to pass in the DB instance when I do that. I should note that I had a little formatting problem with Sinon.spy calls here. They should be indented out so hopefully that doesn't read too weird, and the finally, I'm calling process application as I've been doing before. Alright, well, let's add our DB reference here. I'm gonna stub the database as you've seen me do and then I'll pass it in to the review process. So that looks reasonable. I'm making sure that I'm not stubbing things twice. I have it in a before block. Do I think this is gonna work? No, it is not. We've already covered why this isn't working before. Why do you think it's not working? In attempting to wrap the get mission by launch date, which has already been wrapped. Arrgh. What a pain. Well I talked about this before. The simple reason is, as you see here, my DB repository is a global instance. It is a singleton. I am exporting these methods directly on the module itself. Those modules are cached by Node, which makes them a singleton or a single instance. That's not gonna be a good thing if we're gonna try and mock it. Singletons are not friendly to mocking and you want to have individual instances so let's change that around. Our tools are pushing us to do things a better way. Hooray for that! Here I'll just export the DB prototype directly and there. That's all I need to do. So here I'm gonna capitalize our DB repository. That's just a standard that I like to do, and then I'll create a brand new DB down below. That way we can mock the instance, not the global singleton like we were doing before. Okay, we have our DB variable. We're newing it up to a brand new instance, which makes me feel better. We're stubbing that instance. That makes me feel a whole lot better. Let's see if our tests work, and if I run Mocha, and they do! That makes me feel a lot better. Working with the repository as an instance is something that I should've known from the get go. That's what you're supposed to do. Singletons make life hard. Okay, well, this means that it's possible as well to export a stubbed DB instance that is not going to trample on itself. Oh boy! Do I dare do this? Well, why not? Let's give it a shot. Now that I'm working with an instance I can just pass that instance back and I can say helpers.stubDB. Let's see if this works and causes us problems. I'll run Mocha again and hey, look at that. There's no problem. What if I do this over here in our review spec as well? Let's take a look. Again, no problem because I'm working with individual instances. Hmmm. Is this gonna bite me later on? Possibly, but the nice about this is that our repository is small, again, because we're working with Node modules. I have a feeling I'm not gonna need to stub things out very much, so I'm gonna leave it just the way it is for now.
Finishing up Review Process
Well up until now we've listened to our tools and we've gone through some refactoring. I feel pretty good trying to wrap up this review process so let's push ahead and do that. So off camera I've created a brand new model called assignment. An assignment is a thing that happens after the review process goes off and the applicant is accepted and we assign them to a mission. Having a look through here, it's pretty simple stuff. The only method we have is passenger is compatible and what we're trying to do here is make sure that for the selected role that the passenger's weight, age, height and so on is going to match what we need. As I said, working with rocket-shop, well, space, that's pretty tough stuff. You gotta have the right height and weight and so on. I've also created some specs and inside of here you can take a look at those. I've set these things up as I've done before. I don't wanna spent too much time on this because, well, as I mentioned, we've done all these things before and I'd like to push ahead and finish up our review process. If you want to have a look at the assignment code, go ahead, it's in the downloads. Alright, well, I should point out here that in find next mission it's going out to mission control and grabbing the next mission and it's checking with mission control to make sure that a role is available. I also hooked up assignment to make sure that the role is compatible. That's the code I just showed you on my assignment model. Alright, well, if it is compatible, I'm just passing that along and if I run the tests, everything works. That's good news. Alright, well, let's finish up our review process. Our final step here is to save the assignment in the approved application method. So you can see, I'm just passing on true and I wanna change that, so let's go to our DB repository here and I'll create a new method called save assignment and it'll take in the standard args and callback. Then I'll head down to our helpers and go to our stub DB and stub out this method as well. So I'm gonna stub save assignment and for this, I'm going to accept args.assignment, or I'm just gonna create a brand new assignment, so it'll return an empty assignment. It seems simple, right? Well, it's not exactly. If you notice here, our assignment is going to require a passenger and a role and a mission. Oh, man. Things are starting to get a little bit tough. It looks like our centralized stub routine here is not going to work for us, so instead, I'm going to be good about this. I am going to stub the saved assignment right where I'm using it and that is going to be here when I'm testing the review process, only with a valid application. Here I'm just gonna stub save assignment and say that it was saved. That's that. I don't need to have this be centralized. I'm just gonna put it right here. Alright, okay, well inside of approve application I'm just gonna say save assignment. This is the assignment variable, the instance variable that is prepared above in ensure role compatible. However, it looks like our callback is probably gonna change. We're calling next in line and this is going to pass back our brand new assignment. So rather than call this success, let's change the name of this to be assignment, which means that down below so my tests don't fail and break, I'm gonna have to set result.success = true manually. Alright, that looks pretty good, I think. I'm gonna have to change a few tests around. Well, let's do that. It's going to return success, but let's copy and paste this down and say that it returns an assignment as well and then we'll just leave our tests like that. Okay, that looks pretty good and oh boy, we've got an error. Can't yield since it was not yet invoked. Oh, geez. Spelling error. Oh, it had me worried for a second. You have to make sure you say yields not yield, and look at that! It works! That is great! Now having a final look over our test output here, this is making me feel happy. It reads well, it looks good, and the tests are simple to get through. In this module we took at look at ORMs with Node and then we ignored them entirely. We started stubbing things SinonJS and we used the repository pattern to do it. Then we used helpers, we hated helpers, and we listened to our pain and changed a few things around. Finally, we refactored, finished up our review process, and we are now moving on.
External APIs
A Look at Stripe
In this module we're going to wrap up the complexity as we need to hook up to an external API. That external API is going to be for billing processing of our subscriptions. By the way, what you're looking at here is New Year's Eve in Edinburgh; it was beautiful. So specifically, we are going to take a look at Stripe. I'll give you a tour of Stripe and all the services that it offers as well as a quick tour of the back end, given that we don't want to actually talk to Stripe for each test, we are going to stub and mock the API call and we can use Sinon for that. And then we're going to throw Sinon right out the window and we're going to use Nock, which is going to intercept the HTTP API call and that'll help us test just a little bit better. Then I'll leave you some final thoughts and I will say goodbye. I've been a Stripe user for many years and I have never used a payment system as easy to use and as friendly as this. It handles recurring billing, it handles straight-up credit card transactions. They've also written the check out for you. It helps you with PCI compliance and the documentation, as you see here is amazing. It is so thoroughly and well done and signing up is a breeze, it used to be. But you have to give bank accounts and all kinds of information about your business to these gateway companies and then they would decide whether they would accept you. It usually took weeks. Stripe on the other hand, it's all online. They somehow manage to do it so painlessly, it makes you wonder why other companies don't do this. Here's the Stripe dashboard and this is something you're going to use quite often if you do sign up and start using them. Here you can see at the top left there I've toggled the test switch on, so this is all of our test data. So if we do bounce off of Stripe doing some integration tests later on, we can actually see the test data here. I'm not going to tackle integration testing in this course, but I did want to show you all of the stuff that's up here so when you do run your integration tests, you'll want to be sure that the data that's up here, for instance the plans, mission commander, space tourist, and so on, are represented in your tests as well and indeed they are. So we have four plans here, as you can see. Space tourist, commander, MVAPilot. Okay, well let's get to it. Let's have a look at the API reference. I'll need to work with that API today because I actually am going to call it to make sure that I have the right code written, and if you take a look at this, wow! Look at everything is covered with their API. You can have a look through here as you like. The thing I'm interested in most is subscriptions and on the right side there you can see they have code for Curl, Ruby, Python, PHP, Java, Node and Go. In addition, this documentation is smart enough to know my account details so they actually will input information from my account in here. Specifically, that key right there, that Stripe key that is the actual key from my account, which is pretty neat, and you can see plan. You can see commander right there. They actually seeded it with a real plan. So if I needed to, I could copy and paste this exact code and call it from within Node and have it work. I love Stripe. Alright, well, that's what I'm going to do in the very next segment. Let's give Stripe a call.
Making a Test Call
Let's give Stripe a test call. The reason I'm doing this is I want to see how the API responds. What data I get back. I also want to see how long it takes. So for this I'm going to actually create a customer so here is the API documentation for doing exactly that. Stripe.customers.create. As I mentioned, I can copy and paste that code right there and drop it directly in a file and it's likely to work except for this part right here. This source. What that is is a token and the way Stripe works is it gives you a JavaScript library so on checkout when someone gives you a credit card, that credit card information does not go to your server, it goes to theirs. That helps keep you PCI compliant and what you get back from that call is a token. That's this value right here and you use that token to represent that credit card. Back on our server, we can simply call create customer and pass that token along. Stripe will know what to do with it. It'll attach the credit card to the customer and off you go. So I'm going to copy and paste this code, but I have a problem. I'm not working with the Stripe client library. Not just yet. So I'll need to do something different. Here I'm just going to create a Stripe.js call. This is going to be a file that I call directly using Node. Here and you can see that I just copy and pasted the API documentation and as I mentioned, they drop my information right into the documentation so I can just copy and paste it. So for description, I am going to put in test user and then instead of having source, I'm going to create a plan. I'm also going to send along an email. The final thing I need to do is since I don't have access to a token, I need to pass some card details. Stripe doesn't force you to use their token system. You can pass along the card if you like. Here it just needs a name, a number, and I'm using a test key, 41111, etc., and then I'll pass in expiration month and an expiration year as integers. This code right here will work just fine and it'll do a number of things. It'll create a customer and store this credit card with that customer. It'll then create a subscription for the customer using this credit card and it'll set up recurring billing, all with this small API call. Now as slick as this is, this really isn't the real world. This isn't what you're probably going to do. Instead, what you're probably going to do is use Stripe checkout. That's what you see on the right side. This is a little JavaScript tool that will actually pop up the form that you see here. This is called Stripe checkout and it actually can capture a whole bunch of information, not just email and credit card and so on. You can actually tell it to capture address information. It's really, really neat. I've used it at tech pub for a very long time. I love Stripe check out. It removes a lot of stuff that I have to think about. Anyway, when it's done it'll give you a token. That's the source down below, so you'll probably be using the code or something awfully close to it, that you see here on the right side. Okay, well let's jump back to our test call code and there you are, the last thing I need to do is make sure that I have Stripe installed and the Stripe people are good enough to provide us with a client library for Node that is really easy to use. In fact, that is the code that I copied and pasted. You can see all the other API code for different languages up at the API documentation. So let's log both things out to the screen, an error and the customer that is returned from this call. Whoops. It looks like I forgot a comma and boom! Yes, that was pretty quick. So when we made this call we used our test key and to confirm that, we want to look at live mode and we can see live mode is false here so we are bouncing off their test server; that's good to know. It gave us back a customer ID, that is a test customer ID and it gave us back a bunch of information down below that we can use to interrogate the API back end if we want later on. Refreshing, here we see test@test.com. That is the customer that we just created. As I mentioned before, when you're doing integration tests, it's a good idea to go and, of course, check to make sure that everything is as you expect it to be, back here in the Stripe test dashboard and we know it's test because the toggle on the top left says test. That's good to know. Well, what I really came here for was this response data and I can use this when I start stubbing things with Sinon. What I really want to do is I want to have a valid response come back. I would like to stub using something that exactly comes from Stripe, not something I've made up myself, so I'll just pop it right here in my helpers. So I'll say good Stripe response and I'll return back the response that I just got from Stripe just by copying and pasting it, dropping it straight in. You might be wondering why are you putting this in a function. The reason is that I want to have some args be able to be sent in here, so I can send in a plan and I'll just default it to commander, however, and we're good to go. I managed to talk to Stripe. I have some good data to use now in my tests, let's keep rolling.
The Billing Process
Our test code went off pretty well. Now let's wrap it in a little bit of abstraction so we don't have to use the Stripe library directly. First thing I will do is I'm going to create a brand new process and I'm going to call it billing and it'll just be billing.js and then inside of here I'm going to learn from my mistakes of the past and make sure that this is an instance that I can stub later on. We'll drop in an assertion here. We want to be sure that we have a Stripe key. If we don't have a Stripe key, then yes, this should fail. It won't work at all. Here I'll just require the Stripe library directly inside the instance and I'll pass along the Stripe key that was passed in. Next, I'll create three public methods that will allow me to work with the Stripe API as I need to and right now I can only think of three things that I'll want to do. Create a subscription, cancel a subscription, and change a subscription, and notice that I'm using this. I'm making sure that these methods are accessible from the outside. Why? Because I'll want to stub them out. I want to be sure that I can create mocks for them and I can't do that if they're private. Okay, let's take the code that we just wrote and pop it right here inside of create subscription and we want to be sure that we have an email, name, and a plan provided. If we don't have that, well, this won't work. So here what I can do is I can make sure that I have an asserition going once again. This will throw if I don't have that information and that makes perfect sense. I'll then just reformat the stuff down below to use the arguments that are passed in, including the card itself. And then finally, down below I will just simply remove this callback and pass along the callback that was passed to this method. Great. So this is going to send everything off to Stripe. Now to test this out, I can go back to our Stripe.js test page here and instead of using the Stripe library directly, I'm going to call our billing process and I should be able to use the exact same arguments down below. I'll new it up and I'll pass in the Stripe key that I was using before; that's this guy right here, and by the way, I will be changing that. It is a test key so it doesn't really matter if you see it, but I just want tell you I will be changing it. Okay, so I'm going to say billing.createSubscription and it'll pass along this test data including my name this time, and yeah, we'll give this code a run. I do want to be sure that it'll actually work and call off to Stripe so I'm going to do this small little visual integration test, very rudimentary stuff, but I just want to be sure that it's going to work. And giving it a whirl, nope. How many times in my life have I made this error? I forgot to export our billing module, there we go. Alright, well let's try this one more time and success! Yes, I know. This is a bit of a hacky integration test, but I just want to be sure that the code I just wrote actually works. Since I'm not covering integration testing in this course, I thought that, well, I might as well just do this here and now to be sure that the code I wrote actually works. I'm going to talk more about this in a bit. Okie-dokie. So now what I want to do in my review process here, is I actually want to have a method devoted to starting a subscription, so for that, I will need a payment processor and I'm also going to need some payment information on the application itself. This should return a subscription object. That's what we'll do next.
Stubbing Stripe
We are upping the complexity of our application and therefore our tests. Now we need to insert some stubbing in here. Let's see if things can go well this time. I have a start subscription method and that is going to work with our billing process and that's going to start things of. That means we need some billing information sent in, so I can send in the entire card here with this card information if I wanted to, or why do I need to do that if I'm not even talking to Stripe? What if I just sent in the number 1? Why would I do that? Well, I'll show you in just a couple seconds. Just remember that I set the value of the card here to the number 1. Now that we're sending billing information in on our test application, I have to be able to do something with it, so here let's assert that we are sending in a subscription processor. I'll just call it billing and that'll be sent in through the args and then I'll create a little variable that I can use down below. Now that we have that, we can go all the way down here to our start subscription method and I can just say billing.createAsubscription and I'll pass it off some information and then I'll just handle the callback. The only thing I need to do now is I just need to format the date that our create subscription method expects to see. There it is. We're going to concatenate the first and the last. We're going to send in an email and for the plan I'll just send in the selected role and the card. Remember that card value is a 1. I'll just send that along, too. Then I will pop this into the Async series call right above the approve application. It's going to be just about the last thing I do. I want to be sure that I can start the subscription before everything is considered a success. Alright, well back in our tests, let's require the new billing module and then down below here I will new it up and I'll pass in a fake key. Now that's critical. If I somehow screw up the stubbing, I don't want to accidentally call Stripe and have a ton of subscriptions created, even if it is, even if it is the test back end, it's kind of a pain in the butt to have, oh no! Look at that! 50 brand new subscriptions. So that is going to be the way that I new up our billing process. Then I'm going to stub it and I'm just going to give the billing instance and say create subscription and I'm simply going to return ID XYZ as our brand new subscription ID. The last thing I need to do is just pass it along here as an argument to our review process. Well, while I'm in here, let's make this look a little bit better. I'm going to drop this down so we can read it and finally, I'm going to take care of that weird indentation that I had for the spies. Okay, well all of that went kind of fast. Let's take a minute and review. I've created a brand new billing process that abstracts the call to the Stripe API and I've put it into a module called billing. Here I'm newing it up and I'm passing in a fake Stripe key and I'm doing that because just in case I stub this thing in the wrong way, I don't want to have a bunch of messy data up on our Stripe test server. Next, I am stubbing the billing instance here. I'm stubbing the create subscription method and I'm having it yield. In other words, when the callback goes off, I'm having null and then an object with ID equal to XYY returned to me. And then finally I'm passing that billing instance to the review process so it can be used. Alright, well I forgot to label this Stripe key and now it actually looks correct. Let's give it a whirl and see if it'll work. And flipping over to our console, and look at that. You know, I have to say, sometimes, sometimes I'm pretty sure it's going to work. Other times I just have no idea and that time it worked on the first shot. Good times! Good times! Okay, let's add a test here and make sure that a subscription is returned when it should be because Async should tack on the result right in our return object. So running our tests, and yes, there it is. Man, that is a good feeling when everything works as expected. Okay, well, the last thing I need to do is let's come up here and instead of using ID of XYZ, let's return our good Stripe response. Remember, I want to work with solid test data because I might be using this later on, so I'll just return helpers.goodstriperesponse and it still works just fine.
Handling Stripe Failure
Well, we've covered the case in setting up a subscription and everything just works. Well, what if Stripe returns a bad result and we want to handle that? So heading over to Stripe, let's take a look at their documentation. In here they have a lot of information about testing and different ways that you can interact with their API and test their web hooks and so on, doing integration tests. Here you can see which card numbers you can use for testing and this works great if you're using Stripe checkout or if you're just bouncing a web page off their API. Down below here, if you want to test specific errors, you sure can. Card declined is, well, one that I can imagine, having it happen, so let's do that. I'm going to go over to Stripe.js here and I'm just going to change this to be the card declined number and that should hopefully trigger a Stripe error. And I'll just change some information here to have a different email address. So let's see what happens. I'm just going to call Node and then Stripe.js and bounce a bad request off of them, and good. As expected, we got a bunch of error information back including a big fat dump here for stack tracing, and I can use that in my test later on. Right now what I need to do is I need to work my helpers a little bit because I need to be able to encapsulate the call arguments that I'm sending off to Stripe. I'm going to need that for stubbing. So what I'm going to do here is I am simply going to copy this information and go and crack open my helpers and I'm going to set up a thing called good Stripe args and this is just going to be a straight up object. It's going to be a test user, test@test.com, and I'll just set this to be commander for the role and the card is going to be 1. Now remember that, card number 1. Then I'm going to have bad Stripe args. What does that mean? Well, for bad Stripe args, I'm going to set the card to 2. Now why did I do this? Well it has to do with the way that SinonJS allows stubbing. The only way I can really explain that is to just show it to you. I'm going to use with args and that means that if I pass in good Stripe args, then it's going to yield a good Stripe response, as you see here. So I'm stubbing the billing instance and when I call create subscription, if I pass those good args, I'm going to expect that I get a good response and I do. That is what with args is all about, because it allows you to change which stub fires. Here, I'm going to say with args, bad Stripe args. When that happens, I want to bad Stripe response to come back. This is going to allow me to tweak my stub, if you will, and while very useful, a lot of red flags and bells and whistles are going off in my head. If you find yourself doing things like this, you know, tread lightly and carefully. Let's see if this is going to cause problems. Well, looking at our test, I've got a lot going on in that before function and it's occurring to me that I should clean things up before I go much farther. Really what I should be doing is I should be doing all that stubbing and creating the billing process as well as the DB and all that other stuff. I should be doing that in the outer most before block. That's going to orchestrate all this stuff for me, right here at the very top level for the review process itself. I don't need it down below for receiving a valid application scenario. Alright, let's see if it works. Ahh! Not this error again. Well, thank goodness this is just a silly thing that I did here. I'm calling stub twice on the billing instance and I'm stubbing the create subscription method twice so of course it's going to fail, but then how did we use with args? Well the short answer is that we create a billing stub without all the rest of this stuff and then we use that billing stub down below. So here, I'm just saying, hey billing stub with args, this is what I want you to do, and then again I'm saying billing stub with args. This is just using the instance, not trying to re-stub it. Okay, well now that I've done that, hopefully our tests will pass. Let's see. And not quite. It looks like I biffed. Yep, there it is. I biffed having that variable available to the entire test suite. There it is. I need that at the very, very top. Same with billing. Now you might be thinking, why is Rob showing me all of this? Didn't you practice this demo? And I did. The thing about it is, I'm trying to show as much of an organic process as I possibly can. Arranging, organizing, and moving things around your test suite is as much a part of testing, as well, writing assertions. So here I wanted to show how important it is to think constantly about your test organization, where variables are declared, and how you're using them. Okay, well, let's see if our test passes now and it does. Very nice. Okay, well that was a bit of a tangent. We were in the middle of trying to test out what happens with failed billing and I was in the process of coming over here and putting this test in and I got a little bit side tracked. Now what I want to do is I want to take this information right here that I declared above and I'm going to drop it in this block down below and I'm going to format it so that everything looks right. And then, right here I'm going to take our bad billing app and I'm going to try and change that card number. I'm going to set it to card = 2. Seems simple enough. So this is going to trip up the billing process. When it sees 2, Sinon is going to return the bad Stripe response. That bad Stripe response will hopefully cause our review process to fail and at least that's the goal. Alright, well skipping ahead a little bit here. Here is our entire test block. Valid application, with failed billing, and I am reusing the valid app from above and I'm resetting the card to be a 2. That should trip everything up hopefully. And then I'm calling the review process as I was doing above, and then I have an assertion down below here that is making sure that it is not successful. Okay, well, let's give a whirl. Flipping back over to the console, running our tests. Hmmm. Nope. False does not equal true. Can you think of why that would be? Well, if we take a look at the test here, everything seems to be put together okay. Hmmm. How about we console.log our bad billing app, just to be sure that it's what we expect? And up here, indeed we have a card number of 2. Can you tell what I did wrong? Let's go back and take a look at the test code. It's right here. The bad response is being returned, but Async won't trip unless it sees an error in the callback. Card was declined. We actually don't need the bad Stripe response. I just need to send it an error, so that's what I'll do here. Let's give our test a whirl and see if this works, and ha-ha, nope. This kind of error is insidious and I'll admit to you I'm doing it a bit on purpose. Take a look at this failure. The test that we want to pass is passing, but in making it pass, we've made our other tests fail. This is ridiculous. Do you know why this is happening? Unfortunately, this kind of thing is all too common with Node. Let's isolate our one failing test above using only. This is the very first test, the one that says things should be successful, and we'll run it again. Card was declined. Now that's strange. Card #2. Why is card #2 being used in this test block up on top? This is supposed to be the successful call with card #1. In fact, I remember very clearly setting it, but it's card #2. I think that Mocha is trying to tell us something. When you see weird errors like this, it usually means you're not doing things right and indeed, the problem is Node's module caching. Every module in Node is a singleton. When you require it, it will be cached in memory, so if you'll use exports.someObject and you set it to be an object that gets exported, then that is treated as a global singleton. Ouch! But that doesn't explain why things are failing in this way. Well the short answer is that the described blocks are read by Mocha first and then the code is executed. Here I am setting the card number outside of the before block. Oh man! Lots of problems going on here. So it actually resets the card number above before any tests are run. Good organization, not getting too tricky, respecting Node's module casting. All of these things go into writing good tests so you don't bang your head on the table. Okay, well let's just prove to ourselves that we have solved the problem. I will comment out the card reset right here and let's rerun the tests, and yep, the test passes, but if I was to rerun all of our tests, our invalid card down below, that test would fail. Arrgh. So how can we fix this? Well, there's two ways. Let's take a look at the first and most obvious one. If I go into the helpers and rewrite our valid application object to be a function, a constructor if you will, that returns a new membership application instance, then it'll work. That will work just fine. That means that I need to go back into my spec file, my review spec, and make sure above here that our bad billing app is set to a new helpers valid application. That'll give me back a brand new instance. And then I'll need to go to the describe block at the top of our test file here and make sure that I am returning a new valid application and then over here to our mission application spec and change the code there. It's not all that much, but if I didn't feel like changing everything, I could use underscore to do this. Let's see how. I could use the clone method. That basically looks at an existing object and clones its properties at that point in time and then you can play with it in a brand new instance. So I'll require underscore above, and then down below here, rather than calling it newHelpers.validApplication, I'll just clone it. In some ways this is clear. In other ways, it's a little hacky and it's up to you. Either way when I rerun the tests, it works. In fact, all our tests work the way we expect because we are now using separate instances, so this is a good example once again of our tests driving us to use tools, driving us to use patterns, pulling our hair out and rethinking what we're doing. If I had to sum up this entire experience, I would say that singletons and caching in Node can equal pain. In fact, twice now we have been bitten by Node's caching of modules and I think our tests are trying to tell us to stop doing that to make sure that we always return instances from our modules, even if they are helpers. Yeah, I think that's good advice.
Mocks with SinonJS
SinonJS is a really helpful library. We've been using spies to make sure that callbacks are called and stubs to jump in and fake out certain bits of functionality in our app. Let's take a look at third thing, which is mocks. Now mocks are a little bit different. They are spies and they are also stubs; however, you can set expectations for a mock, basically saying, I expect this call to be called and you can arrange all kinds of things, so it's kind of a little bit like testing, if you will, making sure that certain things are called when you expect them to be called. To me, this is a little bit much. I don't use mocks, but I know some people find them really useful. So I say, if you want to know when you should use them, use them when you write an assertion to make sure that a method is called. So you saw me do that before. I asserted that the method ensure app valid was being called on a review process. Well, if I was using the mocks here, I could have set that expectation up front and it would've failed our test for us rather than me having to write an explicit assertion. So this kind of thing is up to you. If it fits your testing style, hooray! Let's take a look at how to implement this. Back in our code, as you can see here, I'm stubbing out the create subscription method on our billing object and let's say I want to be sure that it is actually called, so for this, Sinon gives us a mock and for that I just need to say mock the billing object. Next I just need to tell it the expectations I have for our billing instance. So down below here, I'm just going to write out that expectation and I'll say mock.expectsCreateSubscription and that's it. This effectively replaces the stub that I had before. Right now, we are just literally using the mock as a straight-up stub, but if I type in once right here, this is going to create the expectation that that method is going to be called at least once, with this specified argument. If it's not, our test will fail. The nice thing about this is, is if you have a lot of assertions down below that assert that certain methods were called, well, you can replace them all up top and sometimes for people it's a little bit easier to read. Now in case you're wondering, hey wait, are you sure the create subscription is being called once in each case, and it is. Right here in the before block with our valid application where the good card is being called, and then down below, another application where the bad card is being called down there. So since we arrange our mock at the very top, we set our expectations here. The last thing we need to do is we need to tell mock to verify our expectations and we do that using Mocha's after block. This gets called after every test is run. In here, we just say mock.verify. So let's run this and see if it works. And it does. Hooray! Well how do we know that this is actually working and that I didn't goof up? Well let's go back to our test code here and say that we expect the first create subscription we called twice. And if we rerun this, boom! We'll see a failure here, and taking a look at the message, it says twice, but called once. Not the most readable thing, but at least it's fairly clear if you read through the message. So that's the mocking functionality from SinonJS. I don't use it all that often; however, if you have some deep implementation details that you want to be sure are going off the way expect, well the mock object might be the thing for you.
Simplifying Things with Nock
Mocking in any framework is a skill that you have to develop over time and usually that skill involves restraining yourself because, you know, mocking can be destructive at certain points and make you want to smash your head against the wall. In our case, we are mocking the call to Stripe and while that works, sometimes you got to wonder, am I mocking too much? There could be logic and create subscription that we are completely just trampling. When really what we really want to do is we just want to make sure that the API up in Stripe is not being hit and for that we could use a tool like Nock. This thing is bleedingly simple. All it does is mock the HTTP calls from your code to an external API. So take a look at the way you use it down below. You just require Nock and then you instance it, giving it the core URL that you want to use. Then you give it a verb like .get and then some additional part of the URL, and finally, with .reply you specify the return information that you want returned to your code. To me, this is just right. The least amount of interference that have, the better. So let's head back over to the straight API documentation. I'm going to need to find the URL, so for stripe.customers.create, let's hit curl above and this will give us the straight up URL information that we need. Here we can take the core URL, which is https://api.stripe.com/v1 and we can use that with Nock. So now what I need to do is install Nock and I'll save it as a develop dependency, alright? So now that that's in, let's just require it above. Simple enough and now for the best part. I can take out all of this mocking stuff from Sinon. I'm not going to be mocking the billing object anymore. So down here I can leave this stub for our database because I do want to stub that and I will set up Nock. (typing) That's pretty straightforward. I give it the base URL of the API, https://api.stripe/com/v1. We'll just send a post of to /customers and as a reply I have to specify two things. The first is the http code that I expect and that's 200, and we're 200, okay. And then I'm going to tell it the data that I expect to come back and this I can use my helpers and just say I expect back a good Stripe response. It almost seems too easy. Well in case I get anything wrong, I do want to reiterate that I am using a fake Stripe key up here so if for some reason a call goes through, it doesn't matter, it will get rejected. Let's try it out. Wow! That is impressive. It's nice and fast and the only thing that's getting mocked is the http call, which means that the code in my create subscription method is actually getting called. That's great! Okay, well let's copy and paste this down below here for a bad Stripe response and what I'll do down here is I'll just drop this before the same way I was doing above and I'll call this a bad call and it's going to post to the exact same thing, except this time I'm going to return a bad Stripe response. This is the one with the error. It seems like it should work, doesn't it? What do you think? Could it be this easy? Let's give it a whirl. Flipping over to our console and, oh geez. False is not equal to true. Yeah, so it looks like it's still successfully working. Oh, alright, well let's see if we can troubleshoot this. Our bad Stripe response is formatted as the way it should be, but just console.log the result that comes back across this application. I want to be sure that it is indeed returning an error for a subscription and it is. There's our subscription object returned from Async and yeah, your card was declined, yet for some reason success is true. Welcome to Mars. Hmmm. It seems like we are getting into trouble with a very, very simple tool. And as I said before, mocking is easy, sometimes too easy, which makes it hard. Mocking is one of those things that you really have to work on in order to understand when you blow it. And there's an old saying when it comes to mocking that you should never mock something that you don't own. Now I could mock the billing process myself because I coded it, but here I am mocking the http library. I'm intercepting the call and bad things are happening and I have no idea where to even start. And now for a little behind the scenes at Pluralsight, in preparing this demo I actually had to sleuth this little problem here quite thoroughly. It led me to quite a few interesting discoveries, because to me it didn't make any sense. I'm just returning the data that the Stripe API returns. The Stripe library that I have installed here in Node, it should've seen that data and said, ah-hah, an error happened. Well, for some reason it didn't and the only way I was able to figure this out was to actually crack open the Node modules directory and go into the Stripe library and see what it expected. You would think it would be something like an error code being returned. Nope, it was this guy right here. It's looking for an error object on the response from the API. If it sees that then it says okay, an error happened and then it'll stop things. I also tried to return a 402. You would think that that would trip up the library because if you go over and take a look at the Stripe documentation, it'll tell you what the API return codes are all about. In this case, 402 means request failed. Parameters were valid, but the request failed. That is precisely the response that you would think would trip up the library, but no, it's looking for an error object. Oh well. That is something that I had to sleuth and I managed to fix it. But that's the problem with mocking something that you don't own. You have to crawl into someone else's code and figure out how to fail it, which is just never a good idea. So where does this leave us with Nock? Should we use it? Should we not use it? Well, it's a great tool and as you can see, it's quite effective, but when you have to have something fail, make sure you know how it's going to fail and this usually means you're going to have to know a lot about the library that you're trying to manipulate with Nock. So I don't think it's a bad thing. It just requires some investment on your part.
Involving the Database
Many developers I know that use dynamic language frameworks like Ruby or Node or anything with Python, they have just thrown mocking right out the window and they involve the database because as you've seen, sometimes mocking can actually cause more trouble than it's actually worth. So if you're in that boat and you want to be able to persist some information just for your tests, something like nedb might be really useful for you, if you don't want to think about persistence until later on. Nedb is basically like SQLite for document databases. It's incredibly fast. You can use it as an in-memory database if you want, or you can have it write out to files. Installing is as simple as any node module, npm install nedb. Querying with it is equally as simple and it basically tries to use the MongoDB API as closely as possible, so if you're eventually going to graduate to some like MongoDB, nedb is a brilliant first step, especially the in-memory only use, which you can use for tests. Alright, I'll leave it to you to go on up to GitHub and have a look at nedb. For now, I have installed it in my application here using npm install nedb, and I've redone my index.js that's in my DB directory. There really was nothing in here before, but I have now added in a missions and an assignments database and these are just file stores. Basically these things are just JSON documents and you write to them and that's it. So to create these I just create them right at the top here and I can use them down below in my code. As mentioned, if you've ever used MongoDB before, then using this stuff should be really familiar. Up above, I've added a method called clear stores, which drops all the data from missions and assignments using remove. Then for get mission by launch date, down below, I'm using find, using the same kind of selectors that you would use with MongoDB. Really simple stuff and that's the idea. Sometimes it can be just so much simpler to persist this stuff and if I ever needed to transition over to MongoDB as I mentioned before, it's easy to do. So taking a look at how our specs have changed, I'm not using Sinon at all anymore. I've commented it out completely. I'm still using Nock because I don't want to hit the Stripe API. Before everything goes off, I'm clearing all stores of data. Simple enough. And then down below, I'm just letting it all ride. It's persisting the data, my tests still run pretty fast as you're about to see, and it's not causing me any problems. In addition, I have to say I feel much more comfortable knowing that the data is being written and I can get rid of a lot of code just by letting the database go and that's something that's really arguable. I know a lot of people react very strong to that, especially in the .NET world, but just know that in other places, like Rails and sometimes in Node community, people just don't care that much because their tests actually run really fast. Take a look. I'll run Mocha here. I don't have a lot of tests, but it just runs as fast as it has before. One of the things that you got to keep in mind too is that since we're using Node and the modules should stay small and focused, you're not going to have hundreds of tests running at the same time. Ideally, you're going to be testing each module in its own little space. One of the nice things about using nedb is you can just hop on over and take a look at the data that it generates. Here I'm keeping the data inside of my DB directory, right next to my code. You can put it anywhere you like, but here you can see how it's storing the data. It's just JSON and it's got some special system flags in there, identifying records that have been deleted and so on. Here's the one for missions. This kind of thing is helpful if you just want to eyeball the data that's being persisted by your data store. No? Now as I mentioned before, I know this is a fairly controversial topic and I just want to state my opinion and this is my opinion. I know that you're experience is probably different, but let's take a look at a project that I just released. This is MassiveJS and for MassiveJS I had to create a whole suite of tests. Right now I think we're up to about 160. For each test that goes off, I'm running this init routine and this init routine executes a SQL file. That SQL file drops and reloads our entire database including a bunch of data. Here's the SQL file right here. And we're dropping tables, we're reloading tables, we have documents going in. We have all kinds of stuff happening here, and the reason I'm pointing this out is, this is a pretty involved test suite and this is a query that needs to run before each test block is executed. Now a lot of people would look at this and say, man, are you out of your mind? You're going to slow down your testing. Why would you ever do something like this? Well, I kind of have to with Massive because it's a data tool and I have to make sure that it actually works. Take a look this. Look how fast this runs! Right here we have 133 passing tests and it runs in 2 seconds. Now as I mentioned, it's a lot more than that, but we're getting Postgres here and we're dropping the entire database, all the schema, all the files, and reloading. That's a big operation. And it's happening really, really fast. So I guess my whole point is this. Sometimes it's easier just to involve persistence from the get go, and sometimes it's not. That part's up to you, but don't be afraid to involve your tests as you need to. Here as you can see, things are running rather quickly. Yeah, they may run quickly for you, too. In this module we played around with Stripe, got to know the billing processor and how it can help us with recurring billing with our subscriptions. We then created a billing process, a wrapper for the Stripe API and then we stubbed it and mocked with Sinon. And then we got into the weeds and simplified things with Nock, realized how painful that working with singletons and module caching can be in Node when it comes to testing, and finally we took a look at simplifying our lives and just hitting the database and not worrying about mocking at all.
Summary and Parting Thoughts
In this course we went deep into testing with NodeJS and we used some tools to help us out and sometimes back us into a corner. And we're pretty much done with all that. Our client is pretty happy. The Rocket Shop is signing people up and they're going to be sending people to Mars. Okay, not really. I might have made that whole part up, but still I thought it was kind of fun. Wouldn't it be neat to go to Mars? I think so. My goal with this course was to show you how good tests can lead you to want to use some tools to help you with those tests. Those tools can push you to use good patterns, which helps the design of your application, which helps you write better tests. This is a recursive process, and if you stay attentive to the way your code is talking to you, the way your tests are causing you problems, or sometimes a joy to put together. If you pay attention to all that, you end up writing a better application that just feels good. Where we eventually end up is understanding that simplicity always wins, especially in the long run. When you go to pick up your code a year or two from now, like I did with MassiveJS when I iterated it to version 2, you'll look back at the tests you've written and you wonder, what was I thinking? What was I doing? Why did I hack this little thing in here? Oh my goodness! But if you keep it simple, that is always the goal because sometimes other developers are going to come in as well. Sometimes keeping it simple means not leaning on that external library, maybe not taking a dependency on the little test helper, and also, speaking of helpers, not dumping all of the code you want into your helpers' library and just leaving it clear inside of your tests. And that's kind of where we ended up. At the end of the day I didn't do too much outside of Node and Express, Mocha, Sinon, and Nock. In fact, later on I just removed Sinon almost entirely from the equation as I involved the database and all I really needed was Nock to make sure that I didn't hit the http API. To me, this is the key of happiness when it comes to testing with Node and speaking of me, well, that's all I've got for you. I do hope you've enjoyed this course here at Pluralsight and I also hope you enjoyed the videos and little pictures I added in from Scotland, a country I really had a good time travelling in recently. Anyway, thank you so much for watching. If you have any comments or anything, drop me a line on Twitter or send me an email. Thanks again.
Course author
Rob Conery
Rob Conery co-founded Tekpub and created This Developer's Life. He is an author, speaker, and sometimes a little bit opinionated.
Course info
LevelIntermediate
Rating
(134)
My rating
Duration2h 39m
Released14 Apr 2015
Share course