David Savage
David Savage

Reputation: 1552

Have rSpec check if an object was able to be created?

I'm extremely new to rspec, and I'm trying to write a test to ensure that an object that is created actually does exist. Here's what my test looks like now:

require 'spec_helper'

describe "UserService" do
  describe ".new" do
    it "should create a UserService object" do
      service = UserService.new
      service.should_not be_nil
    end
  end
end

The problem is, I'm getting this output when I run the test:

  1) UserService.new should create a UserService object
     Failure/Error: service = UserService.new
     Errno::ECONNREFUSED:
       Connection refused - connect(2)
     # ./spec/requests/user_service_spec.rb:6:in `new'
     # ./spec/requests/user_service_spec.rb:6:in `block (3 levels) in <top (required)>'

I'm expecting the connection to be refused at this point, but how can I test for that instead of it breaking the test? Or, is what I'm seeing the correct output? Thanks!

Upvotes: 0

Views: 1606

Answers (1)

I see two problems with this. First you are actually trying to connect to an external service in a test. Generally you want to feed the object under test some kind of mock that represents the service you normally connect to. I'm guessing by what you have in your question and in the comments that your code will need the following changes for that to work:

You need to build a facade, or proxy, object to "hide" the real service from your UserService object. That object should basically just map the underlying service one-to-one. The reason for this is that you don't want our internals tightly bound to an external service, you want them bound to your objects (and never tightly). The facade should not be unit tested, that would bring you right back to where you are now, and should not need to be unit tested since it doesn't contain any business logic.

The second thing is that you need dependency inversion to decouple your UserService object form a specific underlying service implementation. You want your UserServices constructor to take an object, any object, and call methods on that to do the service related stuff. Since your UserService no longer cares if it's a real service (and it shouldn't) or not you can feed it a simple stub during testing and not have to have your test break when an external service is slow or down or in any other way behaves unexpectedly.

The second thing is that you used should_not raise_error to ignore a specific case in your test. It's fine to use it with a specified error, like should_not raise_error(ConnectionError) but throwing errors is a normal thing to do when you can not rescue yourself and your test will then break when it shouldn't if you later add this behavior.

The test says that you should get a UserService object back when calling UserService.new(), it mentions nothing about not getting one back if ... What you should do to test properly in this case and to document the expected behavior is to feed it a working mock in the test you have now and create a second test like:

describe ".new" do
  it "when service is down should throw error" do
    UserService.new(offline_mock).should raise_error(ConnectionError)
  end
end

Just a few thoughts on how to not end up in this situation to begin with ;) As usual a problem in testing is usually a sign of a architectural problem.

Upvotes: 1

Related Questions