What not to test in Rails?

Viewed 2100

I've been writing tests for a while now and I'm starting to get the hang of things. But I've got some questions concerning how much test coverage is really necessary. The consensus seems pretty clear: more coverage is always better. But, from a beginner's perspective at least, I wonder if this is really true.

Take this totally vanilla controller action for example:

def create
  @event = Event.new(params[:event])
  if @event.save
    flash[:notice] = "Event successfully created."
    redirect_to events_path
  else
    render :action => 'new'
  end
end

Just the generated scaffolding. We're not doing anything unusual here. Why is it important to write controller tests for this action? After all, we didn't even write the code - the generator did the work for us. Unless there's a bug in rails, this code should be fine. It seems like testing this action is not all too different from testing, say, collection_select - and we wouldn't do that. Furthermore, assuming we're using cucumber, we should already have the basics covered (e.g. where it redirects).

The same could even be said for simple model methods. For example:

def full_name
  "#{first_name} #{last_name}"
end

Do we really need to write tests for such simple methods? If there's a syntax error, you'll catch it on page refresh. Likewise, cucumber would catch this so long as your features hit any page that called the full_name method. Obviously, we shouldn't be relying on cucumber for anything too complex. But does full_name really need a unit test?

You might say that because the code is simple the test will also be simple. So you might as well write a test since it's only going to take a minute. But it seems that writing essentially worthless tests can do more harm than good. For example, they clutter up your specs making it more difficult to focus on the complex tests that actually matter. Also, they take time to run (although probably not much).

But, like I said, I'm hardly an expert tester. I'm not necessarily advocating less test coverage. Rather, I'm looking for some expert advice. Is there actually a good reason to be writing such simple tests?

5 Answers

The only absolute rule is that testing should be cost-efficient.

Any set of practical guidelines to achieve that will be controversial, but here are some advices to avoid tests that will be generally wasteful, or do more harm than good.

Unit

  • Don't test private methods directly, only assess their effects indirectly through the public methods that call them.
  • Don't test internal states
  • Only test non-trivial methods, where different contexts may get different results (calculations, concatenation, regexes, branches...)
  • Don't assess things you don't care about, e.g. full copy on some message or useless parts of complex data structures returned by an API...
  • Stub all the things in unit tests, they're called unit tests because you're only testing one class, not its collaborators. With stubs/spies, you test the messages you send them without testing their internal logic.
  • Consider private nested classes as private methods

Integration

  • Don't try to test all the combinations in integration tests. That's what unit tests are for. Just test happy-paths or most common cases.
  • Don't use Cucumber unless you really BDD
  • Integration tests don't always need to run in the browser. To test more cases with less of a performance hit you can have some integration tests interact directly with model classes.
  • Don't test what you don't own. Integration tests should expect third-party dependencies to do their job, but not substitute to their own test suite.

Controller

  • In controller tests, only test controller logic: Redirections, authentication, permissions, HTTP status. Stub the business logic. Consider filters, etc. like private methods in unit tests, tested through public controller actions only.

Others

  • Don't write route tests, except if you're writing an API, for the endpoints not already covered by integration tests.
  • Don't write view tests. You should be able to change copy or HTML classes without breaking your tests. Just assess critical view elements as part of your in-browser integration tests.
  • Do test your client JS, especially if it holds some application logic. All those rules also apply to JS tests.

Ignore any of those rules for business-critical stuff, or when something actually breaks (no-one wants to explain their boss/users why the same bug happened twice, that's why you should probably write at least regression tests when fixing a bug).

See more details on that post.

Related