Question

I am trying to generate a successful test in Rspec with FactoryGirl.

Spec file:

require 'spec_helper'

describe Manager::Synchronization do

    it "contains all parameters" do
        build_stubbed(:synchronization, elapsed_time: nil).should_not be_valid
        build_stubbed(:synchronization, updated_records: nil).should_not be_valid
    end

    it "processes asset downloads" do
        # Normally created via delayed_job calling method download_assets_for,
        # we are going to do it right now instead
        p = create(:photograph)
        s = build(:synchronization)
        puts "SLEEPING"
        # By this point, the Photograph has been saved and should appear
        # in the test database. If I write below: puts Manager::Photograph.all.inspect,
        # it gives me a result set that shows the Photograph was indeed saved
        # to the database. However s.download_assets_for(p.class, p.id) raises an
        # error ("Couldn't find Manager::Photograph at id:1").
        sleep 10
        # Expect no errors if the instance should be found
        expect { s.download_assets_for(p.class, p.id) }.to_not raise_error
        # Expect this error
        expect { s.download_assets_for(p.class, 0) }.to raise_error(RuntimeError, "Couldn't find #{ p.class } at id:0")
    end

end

As my comment in the spec file illustrates, I would normally expect no error to be raised by the call to download_assets_for on a model which by all accounts should exist. download_assets_for uses RestClient to make an API call to the 'master' server - which in this case is the same application housing the same test database; the one I just saved a Photograph to. It sends a response back to the method calling it. However the created Photograph DOES NOT appear in the database when the server gets the request (sent by RestClient). I've confirmed this by refreshing the MySQL query in the MySQL Workbench during the sleep 10 period.

So to summarize: Rspec is telling me the record is saved when it really isn't. I'd have to modify my download_assets_for method to check whether or not I'm in the test environment and just return a different result instead of using RestClient - but that feels like a cruddy workaround. The main question is: Why does Rspec say the record exists when it really doesn't?

The most simplest way to test what I'm talking about is to:

1) Open up MySQL workbench, then go to the test database / table and then view all rows. A this point there should be no rows as the test database should be empty.

2) Make an Rspec test in which you use FactoryGirl's create call (or is that Rspec?).

3) Right after the call to create, puts the record you just created. It should show as being saved.

4) Put a sleep call right after that so you can manually click back to the MySQL workbench and reload your query. You should see no records in there. i.e., Rspec said it was created, when it wasn't.

Was it helpful?

Solution

FactoryGirl, and DB testing in general in unit-testing is intended for internal representation of the DB for the test you are running, and provide no assurances about changing the state the DB outside the test.

Your architecture as you describe it calls a REST call inside the test. This is very discouraged in respc:

Requests to external services during test runs can cause several issues:

  • Tests failing intermittently due to connectivity issues.
  • Dramatically slower test suites.
  • Hitting API rate limits on 3rd party sites (e.g. Twitter).
  • Service may not exist yet (only documentation for it).
  • Service doesn't have a sandbox or staging server.

IMHO, you should not test it as a DB use-case, but as a RestClient use-case, since your code does not directly call the database, but rather calls the REST API.

Write one test which stubs the REST API for the client side of the test, and another, setting the DB, to test the server-side of the REST API (testing that the REST call receives the expected result).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top