Fixtures in real life, part 1

If you came here looking for these kind of fixtures, good luck on the way out ;)

This post is part of an ongoing feature about creating a social network in Rails.

There's lots of posts around about Rails fixtures, with critiques ranging from "[they're] great!", "they hurt a little early, but shine with complexity", all the way to "do not use fixtures". Few topics split the Rails community more, the eternal Rspec vs Minitest debate comes to mind.

However, something that I've found lacking in most of these fixtures posts are insights of:
  • how fixtures fare in a real life app,
  • how do they coexist with factories and plain objects, and
  • what growth strategies to follow that will help you out in the long run. 
That's what we're about to tackle on this post.
I'm not here to tell you to use fixtures or not. However, if you're intrigued about them, or are just starting out and want to see what's in for the long run, read up!

Here's some base fixtures inspired by the ones in the Ztory repo for the Piccy app. We've got users how have photos with comments, and user_devices (phone, tablet, browser) for customization and tracking.

First of all, a couple users:
# test/fixtures/users.yml
default:
  email: default@piccy.com
  encrypted_password: 123456
  locale: :en

another:
  email: another@piccy.com
  encrypted_password: 678910
  locale: :en
A user device for each of them. Using the same name for the fixtures makes it easy to mentally link fixtures and means less names to remember.
# test/fixtures/user_devices.yml
default:
  user: default
  arn: "arn:aws:sns:us-east-1:123456789012:piccy:02034b43-fefa-4e07-a5eb-3be56f8c54ce"
  model: "iPhone 6"

another:
  user: another
  arn: "arn:aws:sns:us-east-1:987654321098:piccy:de305d54-75b4-431b-adb2-eb6b9e546013"
  model: "Samgsung Galaxy S5"
Some photos. Here we have a public and a private one for the default user, and a public one for another user to test for visibility and such.
# test/fixtures/photos.yml
default:
  user: default
  description: "My first photo, be gentle pls"
  uuid: "d7391f40-ab17-11e4-bcd8-0800200c9a66"
  public: false

private:
  user: another
  description: "I was feeling kinda inspired"
  uuid: "f1fc2a71-ab17-11e4-bcd8-0800200c9a66"
  public: false

public:
  user: another
  description: "Dynamic entry!"
  uuid: "35a6afa5-97df-4e12-82a6-9272b0bc6d2e"
  public: true
Finally, some comments. Here we're using some embedded ruby to make the comments appear in the correct order. Also having some fun with the descriptions ;D
# test/fixtures/comments.yml
default:
  user: default
  photo: default
  text: "Love this pic."

public:
  user: default
  photo: public
  text: "Next time, less filters though."
  created_at: <%= Time.zone.now + 1.minute %>

another:
  user: another
  photo: public
  text: "Filters rule, I know"
  created_at: <%= Time.zone.now + 5.minutes %>

private:
  user: another
  photo: private
  text: "Too many filters for the world to see"

Tip: Use the annotate_models gem to get a nice a comment summarizing the current schema of the fixture, it saves you a jump to the schema file to check the attributes of a model.

To get to this point, I added a couple fixtures for each model as I was creating them, taking advantage of the Rails generators to create the files and the first fixture. After that, adding or editing fixtures to support new features is just a matter of some light modifications or a little copy & paste.

Now, what does that enables us to do? Here's the whole test for comment visibility, in less than 25 lines:
# test/controllers/comments_controller_test.rb
describe "comments in the user's photos" do
  it 'responds sucessfully' do
    get :show, { id: comments(:default).id }
    assert_response :ok
  end
end

describe "#show" do
  describe "comments in another user's photos" do
    context 'when the photo is public' do
      it 'responds sucessfully' do
        get :show, { id: comments(:public).id }
        assert_response :ok
      end
    end
    context 'when the photo is private' do
      it 'responds not found' do
        get :show, { id: comments(:private) }
        assert_response :not_found
      end
    end
  end
end
Simple, straightforward, and very performant, since all the data is created in bulk at the start of the test. Following the "tests as documentation" idea, reading the controller test and associated fixtures tells us what's implemented already, and gives us a solid foundation to further extend it.

That's it for today! Next post, We'll see how to test photo permalinks, by migrating an existing test with factories, showing some quirks and advantages of fixtures. Questions or comments, type away below :)