Skip to content

Instantly share code, notes, and snippets.

@rosiehoyem
Created November 11, 2014 02:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rosiehoyem/2a306a247e2627886285 to your computer and use it in GitHub Desktop.
Save rosiehoyem/2a306a247e2627886285 to your computer and use it in GitHub Desktop.
Testing Best Practicies
#Testing Best Practices
##Unit Testing with Rspec
Rspec Core Syntax
Rspec Expectations
Rspec Matchers
Rspec Hooks
Rspec Rails
##Model Tests
###What to test in models?
Sandi Metz’ rules. -Rosie
Unit test complex business logic (or refactor it into a library and test it there) -Read this somewhere...
###What not to test in models?
Sani Metz’ rules. -Rosie
Don’t test standard Active Record associations, validations, or scopes. -DHH (Discussion?)
##Controller Tests
###What to test in controllers?
Test controller nuances that step outside of Rails conventions. (Easy way to test this functionality without the overhead of feature specs.)
Opinion: Why testing controllers is really important:
There are a few good reasons to explicitly test your controller methods:
Controllers are classes with methods, too, as Piotr Solnica indicates in his blog post (see opinions). And in Rails applications, they’re pretty important classes (and methods)–so it’s a good idea to put them on equal footing, spec-wise, as your Rails models.
Controller specs can often be written more quickly than their integration spec counter- parts. For me, this becomes critical when I encounter a bug that’s residing at the controller level, or I want to add additional specs to verify some refactoring. Writing a solid controller spec is a comparatively straightforward process, since I can generate very specific input to the method I’m testing without the overhead of request specs. This also means that
Controller specs usually run more quickly than integration specs, making them very valuable during bug fixing and checking the bad paths your users can take (in addition to the good ones, of course).
###What not to test in controllers?
Opinion: Why testing controllers may not be so important:
So why is it you don’t see controller specs used heavily in many open source Rails projects? Here are some thoughts:
Controllers should be skinny–so skinny, some suggest, that testing them is fruitless.
Controller specs, while faster than feature specs, are still slower than specs of Rails models and plain Ruby objects. This will be mitigated somewhat when we look at ways to speed up our specs in chapter 9, but it’s a very valid point.
One feature spec can accomplish the work of multiple controller specs–so maybe it’s simpler to write and maintain a single spec instead of several.
Controller Specs:
Responses
Template rendering
Actions
Routing Specs
##Reqiest Specs: Testing APIs
[Rails API Testing Guidelines](http://matthewlehner.net/rails-api-testing-guidelines/)
What to test?
A properly designed API should return two things: an HTTP response status-code and the response body. Testing the status-code is necessary for web applications with user authentication and resources with different permissions. That being said, testing the response body should just verify that the application is sending the right content.
Rack::Test to drive the tests, RSpec request specs make the most sense. There’s no need to get fancy and add extra weight to your testing tools for this.
Request specs provide a thin wrapper around Rails’ integration tests, and are designed to drive behavior through the full stack, including routing (provided by Rails) and without stubbing (that’s up to you). To test requests and their responses, just add a new request spec.
# spec/requests/api/v1/messages_spec.rb
describe "Messages API" do
it 'sends a list of messages' do
FactoryGirl.create_list(:message, 10)
get '/api/v1/messages'
expect(response).to be_success # test for the 200 status-code
json = JSON.parse(response.body)
expect(json['messages'].length).to eq(10) # check to make sure the right amount of messages are returned
end
end
To DRY things out for future tests, pull the json parsing logic into an RSpec helper. This is what I’ve done:
#!ruby
# spec/support/request_helpers.rb
module Requests
module JsonHelpers
def json
@json ||= JSON.parse(response.body)
end
end
end
spec_helper.rb
RSpec.configure do |config|
config.include Requests::JsonHelpers, type: :request
end
[Building a tested, documented, and versioned JSON API using Rails 4(http://www.emilsoman.com/blog/2013/05/18/building-a-tested/)
##Feature specs
Feature specs are high-level tests meant to exercise slices of functionality through an application. They should drive the application only via its external interface, usually web pages.
Feature specs require the capybara gem, version 2.0.0 or later. Refer to the capybara API documentation for more information on the methods and matchers that can be used in feature specs.
###What to test?
If there is a piece that routinely breaks, write a test to catch it before it goes to production.
###What not to test?
1. The Lists: What Some Heavy Hitters Think
###Sandi Metz - Testing Magic Tricks RailsConf Talk (REALLY good talk)
-Make assertions about state for incoming messages.
-Make assertions that you send outgoing messages. [Only do #2 for outgoing /command/ messages. Don't bother testing query methods at all, though you may need to stub those to make your tests work right.]
-Ignore private methods.
-Test roles. Make tests prove they are playing the correct role (and not just testing the mock / double)
###DHH - Testing Like the TSA (Uber opinionated post in typical DHH fashion)
-Don’t aim for 100% coverage.
-Code-to-test ratios above 1:2 is a smell, above 1:3 is a stink.
-You’re probably doing it wrong if testing is taking more than 1/3 of your time. You’re definitely doing it wrong if it’s taking up more than half.
-Don’t test standard Active Record associations, validations, or scopes.
-Reserve integration testing for issues arising from the integration of separate elements (aka don’t integration test things that can be unit tested instead).
-Don’t use Cucumber unless you live in the magic kingdom of non-programmers-writing-tests (and send me a bottle of fairy dust if you’re there!)
-Don’t force yourself to test-first every controller, model, and view (my ratio is typically 20% test-first, 80% test-after).
###Code Climate - Deciphering Ruby Code Metrics
-Test drive new code.
-Don’t backfill unit tests onto non-TDD’ed code. You lose out on the primary benefits of TDD: design assistance. Worse, you can easily end up cementing a poor object design in place.
-Start with high level tests (e.g. acceptance tests) to provide confidence the system as a whole doesn’t break as you refactor its guts.
-Write failing tests for bugs before fixing them to protect against regressions. Never fix the same bug twice.
###Avdi Grim
-Don’t test the framework.
###Yan Prizker -
-Test-first (that is, writing the test before writing any code) can slow you down if a. You’re an experienced engineer and see good design in your head. b. You’re working on something new where the design is nebulous, then spiking and trying different things is a better way than writing tests. If you write tests too early you may find it hard to refactor your design (introducing collaborator classes, etc) at the time that it needs refactoring the most.
-Complex tests indicate complex code. I like this rule and I use it. Tests can help you spot code smells like code that is overly coupled with collaborators.
-Test first when dealing with a bug is almost a given. You write a test to capture the bug, then squash it.
-Tests in general are a really great way to prevent regression and ensure your code is maintainable. According to research, they are not however the best way to spot bugs (code review is one method that’s more effective, for example).
Untested code is legacy code. It’s impossible to work with because it’s too brittle and leads to people being afraid of refactoring, which leads to more legacy code, which leads to codebase rewrite.
Tools:
Code Coverage in RubyMine
SimpleCov
Factories (Factory Girl)
Faker
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment