What's The Problem?

Learn about testing and validating problems.

We'll cover the following

What’s the big deal if we want to use normal, ordinary ActiveRecord#create in the tests? We use it in the code. What could go wrong? Let’s see what happens.

You’ll start with a simple test involving two users:

it "can tell which user is older" do
  eldest_user = User.create(date_of_birth: '1971-01-22')
  youngest_user = User.create(date_of_birth: '1973-08-31')
  expect(User.eldest).to eq(eldest_user)
  expect(User.youngest).to eq(youngest_user)
end

That test is deliberately simple so as not to distract from the data-creation issue. The only weird thing here is testing a hypothetical finder method, eldest, that goes into the database, so it’s necessary that the objects we create come from the database for the test.

We make the test pass and forget about it. The test silently continues to pass every time we run our test suite.

But then, we add authentication. And even though this test has nothing to do with authentication, it fails. Instead of returning eldest_user, the eldest call in the first assertion returns nil.

Validation problems

The problem is that adding authentication adds two new validations:

  • Requirements that a user must have an email address.
  • Requirements that a user has a password.

The test data no longer matches those requirements, so the objects aren’t saved to the database, and therefore the finder methods can’t find them—hence the nil.

With a heavy sigh, add the required fields to the test:

it "can tell which user is older" do
  eldest_user = User.create!(date_of_birth: '1971-01-22',
    email: "eldest@example.com", password: "password")
  youngest_user = User.create!(date_of_birth: '1973-08-31'
    email: "youngest@example.com", password: "password")
  expect(User.eldest).to eq(eldest_user)
  expect(User.youngest).to eq(youngest_user)
end

Okay, that’s not horrible. It’s not great, either, but life goes on. We’ve switched to create! so that any further validation will fail at the point of creation and make diagnosing the failure much easier.

Sometime later, the marketing department insists on obtaining full demographic data for all our users, including height in centimeters, zip code, and handedness (handedness? Sure, let’s say our company makes golf clubs). The database administrator insists that means that all demographic categories must be required in the database. Now the test looks like this:

it "can tell which user is older" do
  eldest_user = User.create!(date_of_birth: '1971-01-22',
    email: "eldest@example.com", password: "password",
    height: 185, zip code: "60642", handedness: "left")
  youngest_user = User.create!(date_of_birth: '1973-08-31'
    email: "youngest@example.com", password: "password",
    height: 178, zip code: "60642", handedness: "ambidextrous")
  expect(User.eldest).to eq(eldest_user)
  expect(User.youngest).to eq(youngest_user)
end

This is starting to get out of control. Now we need to type three text lines in a test to create a single user. Additionally, it’s nearly impossible to pick out of this data the one attribute (date of birth) relevant for the test.

Not only that, but this problem happens every time a user is created in a test and every time a new validation is added to users. In other words, all the time.

A known valid object is at most one place to update when new validations get created. Fixtures and factories are two different mechanisms for solving this problem.

Get hands-on with 1200+ tech skills courses.