Rails Freak

Permalink

Mocking and Stubbing can be evil

Lately I’ve been seeing some things that make me worry. Mocking and stubbing can be very helpful tools when you use them right, but I am not seeing them used right!

Why stubbing is dangerous

Don’t get me wrong, stubbing is good when you need to decouple one class from another, or your app from a service. My problem is with functional testing and view testing with mocks and stubs - what’s the point? If I make a change to a model and the controller throws a 500 because of the change, don’t you want to know about it? If you setup a stub and then add expectations for model calls and fake return values, you won’t get any red flags.

Twitter Poll

Before we get into the nitty gritty with some real code examples, let’s take a minute to look at some exchanges on this very subject via twitter:

  • @joshowens - “Do you stub model calls in your controller tests? I don’t think you should, if the model breaks, so does the controller, tests should fail.”
  • @aeden - “@joshowens if your controller tests are functional tests then you should consider it, if they’re integration then you shouldn’t.”
  • @joshowens - “@aeden They are functional, but if the action breaks on the controller level and not in the view, shouldn’t the test catch that?”
  • @aeden - “@joshowens a functional test should catch broken controller logic…not broken model logic (that’s reserved for unit tests)”
  • @dougalcorn - “@joshowens @aeden with how little logic should be in your controller, I’ve quit writing functional tests and only do integration with webrat”

Some code for your enjoyment

Let’s assume we have a Saving Model and a Savings Controller. Saving has a state field and uses a state machine plugin to offer transition methods.

Now let’s look at a quick spec sample with some stubs

Alright, everything is looking good, we are green and the pages are loading in the browser. But wait, the client changes their mind, they don’t like the states “pending” and “used”, they want to use “new” and “completed”. With this change, we are going to change the transition methods to match, we will now use @saving.complete! in our update action.

I change our model specs, they go red, then I fix the code to support the new states and methods. I run the full suite and get green… Wait, green?!? Yip, because we stubbed out the calls, we never catch the broken controller.

A better shade of green

Here is how I would approach a better test that would catch our failure:

Let’s get real!

So now that I’ve given you a few examples, let me give you some real experience on why false passing tests are bad. At change:healthcare we have 19,000 LOC with a 1:2.5 code to test ratio - that doesn’t include our custom plugin across shared apps. Even with a full six months of working on the app, doing a major overhaul of the system can be problematic if you don’t have good tests. How can you be 100% confident of rolling out your new code when you haven’t seen and touched 100% of the app?

At change:healthcare we rely on a robust test suite, a hand rolled continue integration suite, and vigorous staff testing to ensure we get everything right. Even with all these efforts, we miss occasional bugs. Better tests (avoiding false passing tests) and better code coverage (81.9% right now) are the best way to catch these bugs! With a code bed so large, the test suite is used as much for regression testing as it is for anything you gain with TDD/BDD.

Personally, I am with Doug’s tweet above, forget functional testing and even view testing and head straight for the integration testing. If you are doing it yet, look at webrat and cucumber. After all, we want a rails app that works from top to bottom, right?

Comments
Permalink

Building an api

Ever thought about building an api for your web app? Not sure where to start, or perhaps the best practices for a rails app? This post will explore some of the choices I made when building a new API for Change:Healthcare.

Versioning

One of the key things I knew needed to happen was versioning. Having used many other APIs before, un-versioned APIs like twitter can be very frustrating. They require you to stay lock and step with the development team, especially breaking API changes. I won’t go into the how too much, I followed John Barnette’s advice.

Versioning covers a lot of pain points in deal with APIs, but it definitely falls flat in a few spots. RESTifarians argue that using versioning in your url means you break with strict RESTful routing and principals. There is also the matter of just doing a straight copy from one version to the next, it creates a lot of extra code to get that versioned benefit.

Encrypt for security

Another key concern when writing this API was to ensure passing sensitive data was as secure as possible. We already require SSL for all API interactions, but we needed to take it farther. We quickly settled on a two way encryption scheme, using AES 256 Bit.

Why AES 256 bit? We wanted something well supported and well documented. We use ruby to build our apps, but not everyone does. A quick search turned up easy ways to deal with AES in Java, Php, etc. One other key point to remember is to use AES256-CBC, as it requires a initialization variable (ivar) to start the encryption and is more secure than just using all 0’s to start encrypting all the blocks.

Supporting JSON

The decision of what formats to support was left up to me, and I decided we would start with JSON, as it is so easy to implement and support. Since rails 2.3 switched to rack, it is very easy to switch out your own Param Parser if you need it, but the built in one works nicely.

In some spots we used the built in .to_json support for objects, and it worked out very nice - we implemented some of those methods very quickly. In other areas we needed custom output, so we wrote helpers to take the object and output the strict JSON we needed. Note the use of .to_json in our helper method below - we used it to easily output proper null when ruby had a nil piece of data.

Write documentation

API adoption relies on a few different factors, but none of more important than good documentation. Having worked with other APIs from eBay, amazon, and twitter - it is easy to realize you can’t get started without good docs. I made the decision to follow a format similar to twitter’s wiki documentation for their API.

We really liked twitter’s inclusion of example return values and curl examples. The example return values are great because you can easily copy and paste them into tests/fixtures/factories when writing your tests. The curl examples are super helpful as well because you can actually figure out how to play around with the API without writing a single line of code.

Bringing it all together

If you glean only one thing from this post, I hope it is that writing an API should be something you put a real effort into. Don’t half ass it, people will be able to tell. Spend the time to figure out your approach, your input and output formats, and above all write documentation!

Comments