 Today we're going to be talking about testing your services, but before we get into that. I want to introduce myself My name is Neil. I'm originally from Iowa. So I feel at home here in the Midwest Now I live in California in LA specifically and I'm a software developer and independent consultant who does rails javascript HTML CSS basically if you name an acronym, I probably coded in it at some point so Today's talk is going to be the what the why and the how of testing your services This is not a talk about building testable services. I could do an entire talk about that on its own It's also not necessarily a talk about test-driven development While I'm a practitioner. I don't think that the principles applied here correspond directly to test-driven or not test-driven development so Got that one good So first we're going to talk about the what so we have to ask ourselves. What is the service? So I break it down into two main categories First of all, we have external services like the Twitter API Facebook's API or Amazon web services and the other category is internal software oriented architecture a buzzword We all know and love so Basically for the purpose of this talk it means any time you're making an HTTP request to an endpoint in another repository So basically any network request you're making outside of your application So now we're going to talk about why? Because we need some justification before we go ahead and test all of our services without question So first we have to ask ourselves why our services themselves important I think a lot of these are pretty self-evident. It allows us to build things faster. It allows us to scale more easily and We use them on basically every application I don't think I can personally think of an app I've built in the past few years that hasn't glued on multiple services And I'm also noticing that we're using more and more services for every application So I would argue that services are critical to modern Rails development So we have to ask ourselves then why is testing those services important? Well, first of all, you should be testing everything else so why would it be different for services and Services often compose critical features of your application for example a stripe integration if your billing's down You're gonna have a lot of issues You know if you have an API request to S3 and not going to be able to serve images if that's down And you might also encounter problems with these APIs. I know I sure have Basically any time I've worked with an API there's been some unexpected results so the first example I'm going to take you through is You know an internal API built by consultants in another part of the company so this is the software oriented architecture we're talking about and They were exposing this API for our Rails app to consume and But we had issues all along the way and it served to Increase the project length significantly Sometimes we'd have random null responses when we were supposed to get objects. There was random inconsistencies where we'd get weird symbols being printed out and Different formatting and in general is a catastrophe so it definitely lengthened the time to completion and this was a lot due to a failure on our part to you know test the API thoroughly so we couldn't express to them You know the problems we were having until we put it into production So this is one problem that could have been solved by testing first So now we're going to talk about a few problems I've had with external APIs and I'm sure all of you have encountered similar issues with APIs in the past So do we have any NHL fans in the house here? Yeah, Chicago Blackhawks doing pretty well in the playoffs so far. We'll see how they go I mean obviously they're gonna get crushed by the Kings and a few rounds here are the sharks possibly But we'll see I don't want to start a sports rivalry today So, you know this basically ranged from small annoyances to you know Major issues with this API So we'd have annoyances like this where some responses would come back with an ID for the team and others would come back with a code and in this case both of them refer to Anaheim So this is a minor annoyance you can code around that here We have an undocumented bug where basically the goals were all supposed to be as a part of an array But if you only had one goal it would be an object and sadly we discovered this one in production During a game so that wasn't ideal But worst of all after we had gone through all of the trouble of fixing these We realized that there was no versioning on this API so even if we fixed it We might be fixing it again a week later So this is basically what it felt like to work with their API So another project I worked on this is just kind of a fun side project was a snapchat API client so I could you know work with a snapchat private API and Well, one of these examples is extreme in that there was haphazard documentation or no documentation in this case I think we've all worked with API's that have improper documentation But in this case we didn't even know what the requests were so we had to figure that out There's also bizarre obfuscation Implanted inside of the app itself that basically encrypted on their iPhone So people like me couldn't go in and build things like this And there's a github link if you're curious So now that we've talked a little bit about why it's important and outlined some of the problems you might encounter We're gonna talk about how you're actually going to test these So first we need to ask ourselves. What is different about services than regular code that we're testing? Well, first of all we have external network requests that are being made and Second of all, you don't own the code so you can't really do unit testing on it It all has to be done from an integration test perspective So what I propose for your tests in general is that you turn airplane mode on This I find is the best way to think about your tests because first of all failure is really bad in testing and You shouldn't be making any network requests So I think of this kind of in two ways first of all so it's airplane mode in the test mode So you can't do these things, but also it should be a test that you can run on an airplane Basically meaning that if you're you know on a long transit Atlantic flight or in the RailsConf lobby you can still make your tests and And they won't fail because of network issues So this means you should not interact with your services from your testing environment And we have a few caveats, which I'll get into now So this includes dummy API's so there's some API makers that have their real API And then they have a fake API which you can hit with requests, but it doesn't make any changes to your data So you can't hit those because those are somewhere else on the network But I do allow you to make pre-recorded responses to those endpoints And that means you can record them within your test suite, which we'll get into in a bit more detail later So for these examples, I'm going to be assuming that you're using rails Obviously and that you're using our spec for simplicity sake So it's time to stub out these requests So when you're stubbing an object, you're basically for those who don't know It's basically putting like a fake object in front of that object So you're hitting that object instead of hitting the real one and you know saving time with like setup processes and stuff like that And we're doing a similar thing when you're stubbing a request to an endpoint Except we're saving a lot more time when we're doing so because we don't have to make that additional network request So there's some libraries that include it built-in stubbing. So type foeus if I pronounce that correctly Faraday and Xcon are three examples of pretty widely used HTTP libraries built on top of net HTTP I think That have built-in stubbing functionality But we can simplify this a little bit and use something called web mock which I'm sure many of you have worked with in the past Which is a general purpose stubbing library for your test suite So you don't have to learn each individual libraries stubbing methods So we'll take you through a quick example. Here's a basic spec helper Nothing really interesting about this except you have to include Disable net connect at the bottom the rest is boilerplate so I've highlighted that and Obviously with all these examples you should be putting the gem in your gem file Bundle installing before you start So when you put this in your code for the first time You'll get a really great error with this and I really like getting these errors because it tells me Exactly in my code where I'm making network requests. So if you're not already doing airplane mode tests You should just plug This disable net connect in and then you'll get this error Which will tell you where you're making these network requests And it also is really handy and it gives you actually the request you're making at the bottom so you can copy and paste that into Your test in order to stub it automatically and obviously you'll have to collect the body and the headers yourself If you need to use those as well So for the following examples, we're going to use probably the most face Sorry most simple Facebook wrapper ever invented basically all we're doing here is sending a get request to Facebook graph the public API for a user and what this does is it just returns like very basic Facebook information about you it has your Facebook username Your name and an ID and a few other fields and then what we're doing with user ID up at the top is We are just pulling out the value for key ID So this all it does is return your Facebook ID super super simple and Make sure since we're putting in a lib that you required at some point in your loading So now we're gonna look at a test for this So this is a test where we're not making a network request, but we're stubbing it out with web mock so at the bottom you can see we're doing our testing case and We're setting up an expectation that our user ID is equal to Arjun's user ID and I'm using Arjun because he was the maker of the Facebook graph API wrapper and You can see now above we are stubbing the request just like you'd stub an object We're setting the method of the HTTP request and then we're putting the link as the second argument Next we have to set up what it returns and this is just an HTTP response that we're returning So we want to put a status you can set headers which I generally don't do But if you're doing any operations with the headers, you should definitely set up these in your responses and the body we have a really simple JSON string I've cut out a few fields for brevity, but you can see it has an ID a first name and a username So this test will pass and we're also making no network work requests So the reasons it's better is it first of all, it's faster and We also aren't getting this intermittent failure that we were talking about earlier from the network requests So that's a good general way, but there's ways we can also save time with this So a lot of the really popular libraries for API wrappers also include mock services within themselves Or as an additional library on the side and they use that for you know internal testing with their gems So I recommend if if you can find one to use this before you go and use web mock because it'll save you a lot of time We'll take you through a quick example. So we're going to use Facebook graph mock here and All we are doing is putting it into spec helper We're just including the methods and requiring it pretty straightforward and now we're going to look at a spec so basically all we're doing is we're wrapping the test case within a Rapper that mocks out the request. So basically all this one's doing is saying we're sending a get request to Facebook graph backslash argin and then the third argument in this case is users are Jim public which is Where the JSON file of this response is located in the gem? So you can also specify your own Responses and I recommend you do that because I found actually some issues with the Facebook graph mock Mocking like responses have some outdatedness in them. So But this you know example I'm not going to take you through all of the gems that have this but this can go to show that There are some benefits that you get from using this. It's already stubbed for you you don't have to learn the API endpoints in order to use it and Some of these provide pre-recorded responses for your use So you don't have to go out and collect these so it's just a good way of saving time if you're using some popular libraries Next I'm going to take you through shamrack, which is one of my more Favorite ways of doing this. I kind of find this to be a fun way Basically, what shamrock does sorry shamrack does is it allows you to mount rack-based apps Which include rails and Sinatra and others and it allows you to make requests against these fake apps So in this case, we're going to get a little help from Sinatra in order to stub out these endpoints So spec help are the only thing interesting is that we leave in web mock pretty boring there But then we get to our fake So I usually just put this in spec support and then fake whatever it is in this case fake Facebook And this just means it'll be loaded when you run your specs automatically But it won't be obviously loaded into your production or staging environments or development So in this case at the top we can see we're calling shamrack And we're setting up the endpoint which we're hitting against which in this case is graph dot Facebook comm and 433 is just specifying that we're using the HDTPS SSL link and dot Sinatra just means we're going to be passing it in a Sinatra app So basically contained within this block is a Sinatra app And you can do virtually anything you can do with a regular Sinatra app, which is really cool So you can just you're just basically mounting this and testing against it So for those of you who don't use Sinatra very much All we're doing here is specifying with the get keyword that we're making a get request to Backslash something and just like rails when you run rake routes, you'll see the parameterization of things with the colon before it We're doing the exact same thing here with username So you'll see in the middle in the link we interpolate params username, and that's how you pull that out So this is essentially just returning a string that is this response You can obviously spice this up by setting status codes adding conditionals in here if you need some more dynamic power and also setting up the headers and You can also which I sometimes do this in my testing is back it with like a small yaml database So you can get some more realistic data than just a simple string So that's the response and Now when we're writing our spec for sham rack all we're doing is Keeping it on this base level. We don't have to wrap it with anything because it will automatically in your tests Pick up the fact that you have sham rack mounted and it will automatically hit against that endpoint Rather than hitting against the network So you might ask why is this better? I think there are a few reasons first. I find it more dynamic. I Find it more expressive as well And you can really you know add as much functionality you need to test your integrations As you want and you can also back it with yaml if you need, you know some pre-population of you know real data And it's also more readable. Let's go back to this for a second and you can see like Reading through this is a lot easier to parse through and you know where the API requests are being made to Versus the stubbing we showed in the first example with the web mock is a little bit hard to read So that's why I prefer to use this So next we're going to talk about VCR, which is a pretty widely used gem and this one has some other benefits But I think are really important to use basically it pre-records your responses and We'll take you through an example so Spec Helper only thing interesting here We have the VCR configuration block and all we're doing is setting a cassette library. So that's basically where these Responses will be saved and then we're hooking into web mock because that's a stubbing library. We're using So here's a spec and As you can see it's really really similar to the Facebook graph mock so basically what this does is you're wrapping it in a block with VCR so VCR what it does is it goes out to the network and makes a request for you in your testing environment and It pulls that response back and saves it in this case at Facebook user Arjen and The nice thing about this is you don't have to go out and collect your own Responses which I find to be pretty tedious and also error prone but it also means you don't have to Break airplane mode with your test because you can run this before and you can cache all of the JSON responses and play them back in your build So when you're running it on Travis CI or circle or whatever you happen to use You're not going to break your build because of network failure. You're going to be using these cache responses and that also Just allows you to verify the responses. So like I mentioned, it's a little error prone I've tried collecting these responses on my own and you know Sometimes I copy and paste them wrong and come up with an issue So this kind of allows you to like have a nice programmatic way of pulling those in So there's also an additional build process you can add so for the NHL example I talked about the problem was there was no versioning so what you can do is if you want bonus points and You're really dependent on an API that doesn't have vernet versioning You can do some kind of build process or you know test setup where you're basically running it outside of your normal test mode and you check the cassettes for diffs and Verify the three responses are not changed from before so this can help you avoid versioning issues So I recommend that if you're using something like NHL API so the next one we're going to briefly talk about is puffing Billy and Aside from having a really cool name and a nice logo on their github This is an interesting gem to use. We're not going to use an example here But basically what it is is for in browser requests So basically if you're having integrations that are browser based you can record and reuse Just like vcr and use those responses again So I don't want you guys to think that all of this has to be done in a ruby and that you have to Use vcr to first record your responses. There's a lot of tools out there that will help you To collect these responses test API endpoints faster, and I want to share some of those with you so Chrome dev tools has anyone heard of this in here? Yeah, probably probably all of you But this is the first one I'm mentioning because I use it probably every day Obviously it gives you a really nice way of you know viewing responses and requests and resending them So super useful. I'm not going to get too far in depth in that one because I'm assuming most people worked with it But it doesn't hurt to mention So next postman if you want to stay within Chrome This is an extension you can use and it basically gives you a user interface around running these requests So that you can have kind of an easier way to play with Request and responses and allows you to save them. It gives you you know a time and milliseconds of completion and this one I think is I was working on a Tender API client for fun. So That's what these requests are for so that one's actually up on my github too if you're curious So I use that a lot But if you like to stay a command line based I would recommend HTTP it's basically an easier to use version of curl and It doesn't have like quite the archaic syntax curl has so I think it's you know worthy worthy of use and You know be easier obviously to run a script around this than it would be to run around postman So if you need to do something more programmatic, this is probably your best option and one last tool I really like to use is called Charles and Charles does a lot of the same things as Chrome dev tools does but it acts as a proxy. So it basically captures You know requests between you and your network. So you can set this up to capture any requests from your Mac machine or you can proxy in your phone as well So I found this really valuable when I was testing out the requests from the snapchat client because it allowed me to see what my phone was making for requests and record those and especially when we didn't know what the request was is very helpful in that case and It's it's also cool because you know when you're building an API on Ruby And you want to build an iOS client with it and you're not really sure how often to pull and stuff I sometimes pull this up and I'll just see what other apps are doing So it's a good way of debugging other people's work and you know seeing how they're doing it. Well, so I Highly recommend you check it out. It's pretty easy to use and you can use it with SSL requests as well So here's some additional reading. I know you won't have time to write this all down. I'll post the slides on my Twitter But next up let's bring it all together. So We went over the what the why and the how of testing services So we've shown that testing services is crucial. They make up really important parts of your app So skipping tests is pretty dangerous. I'd have to say if you're in doubt stub it out Determine when you're making choices between, you know, web mock sham rack or Puffing billy even you want to determine the amount of flexibility you need and the amount of extra work You're going to have for example it probably takes more time to make a sham rack server and have dynamic responses then copying and pasting the request you get from The web mock error so you kind of just need to look at the project you have and determine what use case best fits these options and Also record responses to save time. I wish I would have started doing this sooner. It's like super useful I would highly recommend you do that And next up after me I'd recommend you stick around I had a pleasure of pairing with Austin yesterday And I think his talk plays off my talk a lot in that it Talks a lot about inconsistent test failures and he goes a lot more in-depth on you know other kinds of inconsistent test failures and You just should definitely stick around if you have the time So that's it for today. Thank you for taking the time to listen to my talk And if I don't get to your question, feel free to shoot me an email or if you just want to chat And you can also find me on Twitter. So thanks a lot guys