ruby-on-railstestingcapybararspec-railsahoy

Instantiating and associating Ahoy::Visit for RSpec System Testing


Env

General Context

We have an app that uses current_visit as a stand-in user model when a user is not authenticated. We allow a user to "trial" the application, then request they register an account to continue after completing our wizard once. We then associate resources from current_visit with current_user using the Devise after_sign_in method. We also use Pundit, and have a UserContext object, but I don't think that's relevant for this particular question.

Problem Context

I am trying to write system tests and want to be able to instantiate a visit to a certain state. For example, we have two resources a user could optionally have and I'd like to be able to instantiate a visit into these states:

Visitor could have associated resource B but not A, but this is not common, nor possible via the UI (which is the focus of this system test).

Problem

I have added an edit below about an additional approach using mocks.

I am able to instantiate a visitor in my system tests, and they are successfully created in the system test. Using the debugger, I can confirm that the visitor responds to the right methods as I expect, as I can call expect(visitor.reload.resource_a.size).to eq(1) and pass the spec

However, it seems that this visitor is not associated with the current_visit on the backend in the system test, as even though the instance of visitor has the traits I want, the frontend does not display this instantiated visit.

I have tried to initiate a tracker in my system test:

let(:visitor) { create(:ahoyvisit, :with_resources) }
let(:tracker) { Ahoy::Tracker.new(visitor_token: visitor.token) }

But this doesn't seem to work to associate the created visitor to current_visit in the controller. I am generally unsure how I should properly bridge this gap in my system testing.

[edit]

Mocks

It was suggested to me to try using a mock, but that also didn't seem to work: allow_any_instance_of(Ahoy::Tracker).to receive(:visit).and_return(visitor)


Solution

  • Using a mock was the way to go. I simply had to make sure I was mocking the right method and response before visiting my route like so:

    it "shows the right thing" do
      allow_any_instance_of(ApplicationController).to receive(:current_visit).and_return(visitor)
    
      visit resource_path
      expect(page).to have_text(visitor.resource.first.title)
    end