Search code examples
ruby-on-railsrubywebmock

Rails WebMock error on the second request try ::NetConnectNotAllowedError


I want to mock custom devise strategies to authenticate user in my feature specs. To stub request to 3th party app I'm using WebMock with the implementation below:

spec/utility/stub_methods.rb

  def stub_aware_auth(creds, returns_token, _valid)
    stub_request(:post, "http://example.com/oauth/token").
        with(body: {"grant_type" => "password", "password" => creds.password, "username" => creds.email},
             headers: {'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type' => 'application/x-www-form-urlencoded'}).
        to_return(status: 200, body: {'access_token' => returns_token, 'token_type' => 'bearer', 'expires_in' => 1200}.to_json, headers: {})
  end

Which is called in the spec by:

context 'AWARxE authenticated user' do
  before do
    stub_aware_auth(creds, return_token, valid)  
  end

  let(:creds) do
    OpenStruct.new(email: '[email protected]', password: "Ican'tb3li3v3itsn0tbutt3r")
  end
  let(:return_token) { SecureRandom.hex(32) }
  let(:valid) { true }
  # other logic (...)
end

spec_helper.rb

WebMock.disable_net_connect!(allow_localhost: true)

Which works quite surprisingly wired because if in one test example it has to use stub_aware_auth twice, the first request returns the defined result but the second throws an error:

WebMock::NetConnectNotAllowedError: Real HTTP connections are disabled. Unregistered request: POST http://example.com/oauth/token with body 'grant_type=password&password=Ican%27tb3li3v3itsn0tbutt3r&username=PHYSICIANSREALLYSHOULDONLYAUTOAUTH%40EXAMPLE.COM' with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Faraday v0.12.1'}

You can stub this request with the following snippet:

stub_request(:post, "http://example.com/oauth/token").
  with(body: {"grant_type"=>"password", "password"=>"Ican'tb3li3v3itsn0tbutt3r", "username"=>"[email protected]"},
       headers: {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'Faraday v0.12.1'}).
  to_return(status: 200, body: "", headers: {})

registered request stubs:

stub_request(:get, "http://example.com/api/v1/user").
  with(headers: {'Authorization'=>'Bearer dc2dcf4c5b47ffcfea28c26490ed2a0e2580f152dfb18dbfc97670028d24ecaa'})
stub_request(:post, "http://example.com/oauth/token").
  with(body: {"grant_type"=>"password", "password"=>"Ican'tb3li3v3itsn0tbutt3r", "username"=>"[email protected]"},
       headers: {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded'})

Which is surprised because WebMock.disable_net_connect!(allow_localhost: true) is defined in spec_helper.

[EDIT]

Full failed example if it helps:

spec/features/login_strategies_spec.rb

  context 'AWARxE authenticated user' do
    before do
      stub_aware_auth(creds, return_token, valid)
    end

    let(:return_token) { SecureRandom.hex(32) }
    let(:valid) { true }
    context 'physician' do
      let(:creds) do
        OpenStruct.new(email: '[email protected]', password: "Ican'tb3li3v3itsn0tbutt3r")
      end
      let(:user) { build(:physician) }

      before do
        stub_aware_user_info(user, 'Physician (MD, DO)', return_token, valid, 1)
      end
      
      # some other it block (...)

      context 'case insensitive' do
        let(:valid) { true }
        let(:uppercase_email_creds) {
          OpenStruct.new(email: creds.email.upcase, password: creds.password)
        }

        scenario 'logging in with upper case email' do
          expect { subject }.to change(Login, :count)
          logout
          expect {
            login(uppercase_email_creds)
          }.to_not change(Login, :count)
          expect(page).to have_current_path(name_search_registrants_url, url: true)
        end
      end
    end

Solution

  • The stub you are creating is still on the original creds with the lowercase email. There is no possibility for webmock to mock the call unless you actually call something like

          stub_aware_auth(uppercase_email_creds, return_token, valid)
    

    If you actualy expect the app to call out with lowercase email in body, then the test fails because it should fail, it is calling out with the email in uppercase:

    You can stub this request with the following snippet:
    
    stub_request(:post, "http://example.com/oauth/token").
      with(body: {..., "username"=>"[email protected]"},
           ...).
      to_return(status: 200, body: "", headers: {})