Manasi Salvi

RSpec mocks

Writing tests is important (yes, we all know that) and it’s taken me a couple of years to get familiar with writing tests with RSpec. Learning to write method stubs, fakes, working with test doubles and working around caveats - big and small. For example: not every class is required to test what the usecases or classes within it return. It might be sufficient in these scenarios to get away with rspec-mocks, RSpec’s test double-framework. While first learning about RSpec I often got stuck with the test setup using mocks. There’s a plethora of blogs and Stack Overflow posts about this, these are just some that I didn’t come across as easily and the ones I use repeatedly in testing.

  Class Apple
    loop do
      x = Foo.something
    end

    break unless Bar.find_by(x: x)
  end

  let(:foo) { Foo.new }

  before do
    foo.stub(:something) do
      foo.unstub(:something) do
    end
  end
  Class GoodApple
    def foo
      # use_case returns value of do_something
      use_case = Apple.knife
      ...
    end
  end

  let(:use_case_double) do
    instance_double(Apple, do_something: did_something)
  end

  before do
    allow(Apple).to_receive(:knife).and_return(use_case_double)
  end
  Class GoodApple
    def foo
      use_case = Apple.knife(plate: funky_plate)
      ...
    end
  end

  let(:plate) { create(:plate) }
  let(:use_case_double) do
    instance_double(Apple, do_something: did_something)
  end

  before do
    allow(Apple).to_receive(:knife).with(plate: plate).and_return(use_case_double)
  end
  Class GoodApple
    def foo
      use_case = Apple.knife(plate: funky_plate)
      return use_case.core.seed
    end
  end

  let(:plate) { create(:plate) }
  let(:use_case_double) do
    instance_double(Apple, core: core_double)
  end
  let(:core_double) do
    instance_double(CoreClass, seed: '123')
  end

  before do
    allow(Apple).to_receive(:knife).with(plate: plate).and_return(use_case_double)
  end
  let(:use_case_double) do
    OpenStruct.new(core: core_double)
  end

Why Rails Delete Request Errors As A Get Request

I’ve been working through the book Ruby on Rails Tutorial (6th Edition) by Michael Hartl. With books such as these, I like to code along and work through the exercises no matter how tedious they may be. Without going into too much detail to avoid copyright infringement I came across a local routing error which seemed quite odd.

In Chapter 9 I came across an instance where a DELETE request to simply follow a rails destroy action threw a rails routing error when I tried to test the logout feature of the app in the browser. The app uses jQuery to render the front-end. The rails code was a standard DELETE request:

<%= link_to "Log out", logout_path, method: :delete %>

The error I received was a Routing Error: No route matches GET /logout. Except this wasn’t a GET request but a DELETE request which should be destroying a record in rails terms.

After much digging I discovered the link_to tag is tied to a GET request. So as a temporary patch I changed this to button_to with the method param set to delete which creates a POST request to delete the record.

<%= button_to "Log out", logout_path, method: :delete %>

This works, however the tests will fail if you have been coding along as the rest of the book relies on this code. But for testing in the browser this patch works.

In the real world you will need a proper way to deal with this and I certainly would fix it and use something like the Devise gem for authentication.

There’s plenty of answers on stackoverflow but the one I found most useful was https://stackoverflow.com/questions/44050190/no-route-matches-get-logout.