A Sinatra Controller-Type Test
This morning Mike helped me work through my first controller-style RSpec test, and the “catch” was that I was doing it for my Sinatra app. Since this was my first test like this, and it is slightly different from a Rails app, I’m going to document a walkthrough to help it sink in.
In my main Sinatra app file I have the following action:
The only think I really want to test in this action is that the UserDbInteractor
is called to create a new user. In Rails I could set up this test and then check that my @user
I created is equal to a user
I set up in the test, like so:
assigns[:user].should == user
Unfortunately, the ‘assigns’ action is not available in Sinatra, but I can use message expectations just as you would in a Rails test test to assert that a mock UserDbInteractor
should receive the “new” and “create” methods- and that’s really most of what I need to test here anyway. I can test the UserDbInteractor
explicitly to confirm that the object it returns is what I expect, and I also already have a Cucumber test that fills in a form, posts to “/users” and confirms the output is as expected.
So, here’s how my RSpec test for the "/post"
action looks:
The first three lines below the “it should…” statement are my set-up, including creating my mock interactor.
The next line is my first message expectation, that DataStore::UserDbInteractor
should be called with the “new” method. If I was to change :new
to a method that I didn’t have, for example :zebra
, I would get the following error message from my failing test:
Failure/Error: DataStore::UserDbInteractor.should_receive(:zebra)#.and_return(interactor)
(<DataStore::UserDbInteractor (class)>).zebra(any args)
expected: 1 time
received: 0 times
# ./spec/foundry_spec.rb:23:in `block (2 levels) in <top (required)>'
So in this line I want to make sure it receives the call to new
and when it does I want it to return the interactor
I set up with the and_return
method.
If you look back up at my original “/post” method (the first gist) you can see that I have the create
method chained on after the new
method. So in my second message expectation I’m asserting that the interactor
object I created and returned should now have its create
method called. Since I’ve returned the new interactor object in the first expectation, and I have a create method setup for it (in a separate file), this second expectation passes too.
The slightly odd thing about this test is that it doesn’t follow the typical context -> action-> assertion pattern that I’m used to in testing. It’s actually my final line that calls the action to “/post” after my assertions, but without it the previous message expectations would fail.
If this is anyone’s first look at this type of test I recommend setting up something similar to it (and you could definitely use some simpler classes and objects) and then just playing around with the spec to see what passes and what fails. Once Mike helped me to get it passing I started moved things around and commenting out lines or sections to see how things would fail, and as usual, the error messages helped explain how I was breaking things.