質問

I'm using the Mobvious gem in a Rails project to determine the kind of device being used to access a page. The user story I'm having issues with is:

  • A user on a mobile device is on the mobile version of the site
  • The mobile user clicks a 'Visit Full Site' button which takes them to the desktop site
  • A desktop value is put into the session to make sure their preference for viewing the desktop site on their mobile device is recorded (when mobile users first access the home page on a mobile device, they get automatically re-directed to the mobile site)
  • On the desktop site home page, the mobile device user can see a 'Switch to Mobile Site' banner to give them to do so (the desktop user obviously never sees this banner)

The code to implement the conditional rendering with Mobvious is trivial:

app/views/welcome/index.html.haml

- for_device_type :mobile do
  .switch-to-mobile-site-banner
    # code for banner

The code itself works as expected: the issue is that I want to write a RSpec feature test for this, but I think I can't seem to simulate a mobile user agent properly in a RSpec feature test scenario where I don't have direct access to the request object. My rationale is that a feature spec is more appropriate here than a request spec as I am testing for the presence of specific content on a page.

So far, taking cues from the helper specs of the Mobvious gem, I've stubbed out a request object in my feature spec that sets device_type to :mobile as I want, but I can't seem to get the content in the .switch-to-mobile-site-banner to display:

spec/features/mobile_navigation_features_spec.rb

feature 'Switch to mobile site banner' do
  include Mobvious::Rails::Helper

  given(:env) { double('env') }
  given(:request) { double('request') }

  background do
    allow(env).to receive(:[]).with('mobvious.device_type').and_return(:mobile)
    allow(request).to receive(:env).and_return(env)
  end

  scenario 'mobile user prefers using the desktop site' do
    visit root_url(desktop: 1)
    puts "Device type is #{device_type}" # => correctly returns :mobile
    expect(page).to have_selector('.switch-to-mobile-site-banner') # fails
  end
end

The expectation is failing, and using save_and_open_page to look at what's rendering on the page shows no banner, and I don't know why. I don't seem to be getting any errors when the code runs through Mobvious' for_device_type method, so I'm thinking perhaps there's something I'm missing in the request object mocking(?).

I'm not tied specifically to any implementation/test-type, so I'm open to any other ideas on how I can test the functionality I want to. I thought initially the rack_session_access gem could help out, but unless I'm using it wrong, I couldn't use it to do what I want.

Clarification: I am using Poltergeist for Capybara's javascript driver.

Update with solution

I think what really threw me with this problem is that in the mobvious-rails gem, when you want to get the device_type, it calls the request object directly and I figured I needed to mock this out in order to get the helper working properly in my specs. As pointed out in the answers, this kind of low-level mocking in a high-level feature spec is a code (spec?) smell, and in hindsight I should have listened to the code practically telling me I was doing it wrong. Also, for some reason, adding js: true in this case didn't even cross my mind, regardless of having used it in other tests of mine.

I ended up using parts of both Billy Chan and Kaleidoscope's answers to formulate a solution I like that is much simpler and cleaner:

spec/features/mobile_navigation_features_spec.rb

feature 'Switch to mobile site banner' do
  background do
    page.driver.headers = { "User-Agent" => "mobile" }
  end

  scenario 'mobile user prefers using the desktop site', js: true do
    visit root_path(desktop: 1)
    expect(page).to have_selector('.switch-to-mobile-site-banner')
  end
end

Since Kaleidoscope was the first to come up with a working set of specs that took me most of the way, I'm awarding him the bounty, but I'm going to accept Billy Chan's answer as correct because I think it is a more 'canonical' solution that I would direct others to reference in the future.

役に立ちましたか?

解決

At first, you need to turn on Javascript in this feature as mobile detection is done by Javascript in this gem, and many other similar solutions.

feature 'Switch to mobile site banner', js: true do

Update: According to Andrey's comment, Mobivious dosen't use JS to detect mobile so you don't need JS enabled.

Secondly, I suggest you not to use low level mock in the integration testing when possible. What you need to do is to fully behave like a mobile user and see what he will experience. You don't even need the Mobvious helpers which are low level as well.

I searched around and found this gem could be of help: https://github.com/mururu/capybara-user_agent

Though I havn't used that gem before, the syntax looks easy

feature 'Switch to mobile site banner', js: true do

  background do
    set_user_agent(:iphone)
  end

  scenario 'mobile user prefers using the desktop site' do
    visit root_url(desktop: 1)
    expect(page).to have_selector('.switch-to-mobile-site-banner')
  end
end

Additional answer to OP's comment

  1. About reaching 'example.com'. The fix is to use root_path instead of root_url in visit line. This is a recommended practice to remove dependency on domain settings.

  2. About user agent setting. I checked the gem, the syntax looks different from what Poltergeist can do. So, try not to use the gem but set the header as per Poltergeist's issue

    page.driver.headers = {"User-Agent:" => "iphone"} 
    

    Reference: https://github.com/jonleighton/poltergeist/issues/127

    If this still doesn't work, you can use Webkit temporally in this tests. After all they are all headless drivers. I always install these two gems and use Poltergeist as the main one as well. Just set it as below after gem installation.

    In the test

    require 'spec_helper'
    require 'capybara-webkit'
    
    feature 'Switch to mobile site banner', js: true do
      capybara.javascript_driver = :webkit
    

他のヒント

Enable js in your spec and stick this in IMMEDIATELY before you request the page you're spec'ing (it only needs to go before the request you care about but just try this first):

page.driver.headers = { "User-Agent" => "Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Mobile/9A334 Safari/7534.48.3" }

UPDATE: here's the code for my working spec:

root markup:

<% for_device_type :mobile do %>
  HAHA LOL MOBILE
<% end %>

spec:

require 'spec_helper'

feature 'Switch to mobile site banner' do
  scenario 'desktop content', js: true do
    visit "/"
    expect(page).to_not have_content('LOL')
  end

  scenario 'mobile content', js: true do
    page.driver.headers = { "User-Agent" => "Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Mobile/9A334 Safari/7534.48.3" }
    visit "/"
    expect(page).to have_content('LOL')
  end
end

UPDATE 2 As for why your original spec isn't working, you aren't actually mocking the env afaik. I'd be surprised if feature specs let you access the env or request objects that easily. You should be working from the POV of the browser and so setting the user agent seems like the way to go.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top