rubyunit-testingsinatrarack-test

Can I test that a Sinatra post method successfully saves to a YAML store?


I can't find a basic explanation anywhere about how I can test, with Rack::Test, that a Ruby/Sinatra post method successfully saves data to a YAML store/file. (This explains testing get, which I can do(!), but not post; other mentions of testing post methods with rack/test seem irrelevant.) For self-study, I'm building a "to do" app in Ruby/Sinatra and I'm trying to use TDD everything and unit test like a good little boy. A requirement I have is: When a user posts a new task, it is saved in the YML store.

I was thinking of testing this either by seeing if a "Task saved" was shown in the response to the user (which of course isn't directly testing the thing itself...but is something I'd also like to test):

assert last_response.body.include?("Task saved")

or by somehow testing that a test task's description is now in the YML file. I guess I could open up the YML file and look, and then delete it from the YML file, but I'm pretty sure that's not what I'm supposed to do.

I've confirmed post does correctly save to a YML file:

get('/') do |*user_message|
  # prepare erb messages
  @user_message = session[:message] if session[:message]
  @overlong_description = session[:overlong_description] if
    session[:overlong_description]
  session[:message] = nil # clear message after being used
  session[:overlong_description] = nil # ditto
  @tasks = store.all
  erb :index #, user_message => {:user_message => params[:user_message]}
end

post('/newtask') do
  @task = Task.new(store, params)
  # decide whether to save & prepare user messages
  if @task.complete == true # task is complete!
    @task.message << " " + "Task saved!"
    session[:message] = @task.message # use session[:message] for user messages
    @task.message = ""
    store.save(@task)
  else
    @task.message << " " + "Not saved." # task incomplete
    session[:message] = @task.message # use session[:message] for user messages
    session[:overlong_description] = @task.overlong_description if
      @task.overlong_description
    @task.message = ""
    @task.overlong_description = nil
  end
  redirect '/'
end

As you can see, it ends in a redirect...one response I want to test is actually on the slash route, not on the /newtask route.

So of course the test doesn't work:

  def test_post_newtask
    post('/newtask', params = {"description"=>"Test task 123"})
    # Test that "saved" message for user is in returned page
    assert last_response.body.include?("Task saved") # boooo
  end

Github source here

If you can give me advice on a book (chapter, website, blog, etc.) that goes over this in a way accessible to a relative beginner, I'd be most grateful.

Be gentle...I'm very new to testing (and programming).


Solution

  • Nobody answered my question and, since I have figured out what the answer is, I thought I would share it here.

    First of all, I gather that it shouldn't be necessary to check if the data is actually saved to the YAML store; the main thing is to see if the web page returns the correct result (we assume the database is groovy if so).

    The test method I wrote above was correct; it was simply missing the single line follow_redirect!. Apparently I didn't realize that I needed to instruct rake/test to follow the redirect.

    Part of the problem was that I simply hadn't found the right documentation. This page does give the correct syntax, but doesn't give much detail. This page helped a lot, and this bit covers redirects.

    Here's the updated test method:

      def test_post_newtask
        post "/newtask", params = {"description" => "Write about quick brown foxes",
          "categories" => "writing823"}
        follow_redirect!
        assert last_response.body.include?("Task saved")
        assert last_response.body.include?("Write about quick brown foxes")
      end
    

    (With thanks to the Columbus Ruby Brigade.)