0

The Rails app in question uses Rspec, Capybara, and WebMock in tests.

In one test scenario, this code WORKS:

response = RestClient.post 'the.domain.com', 
  {
    "dados": {
      "head": {
         "servico": "autenticacao",
         "chave": ""
       },
       "data": {
         "empresa": "my_name",
         "login": "my_login",
         "senha": "my_password"
       }
     }
   }.to_json

RSpec.configure do |config|
  config.before(:each) do
    stub_request(:any, 'the.domain.com').
      with( body: hash_including(
        {"dados": {
          "head": {
            "servico": "autenticacao",
            "chave": ""
          },
          "data": {
            "empresa": "my_name",
            "login": "my_login",
            "senha": "my_paasword"
          }
        }}.to_json
      ) ).to_return(
        status: 200,
        body: {"dados": {"btms": {"chave": "returned_key"}}}.to_json,
        headers: {}
      )
  end
end

But this one, that happens right after, in the same scenario, DOES NOT:

data = []
data << {
  "cdg_tipo_transporte": "3"
}

response = RestClient.post 'the.domain.com',
  {
    "dados": {
      "head": {
        "servico": "reserva_salva_lote",
        "chave": response["dados"]["btms"]["chave"]
      },
      "data": data
    }
  }.to_json

RSpec.configure do |config|
  config.before(:each) do
    data = []
    data << {
      "cdg_tipo_transporte": "3"
    }
  stub_request(:any, 'the.domain.com').
    with( body: hash_including(
      {"dados": {
        "head": {
          "servico": "reserva_salva_lote",
          "chave": "returned_key"
        },
        "data": data
      } }.to_json
    ) ).to_return(
      status: 200,
      body: {"Status?": "OK"}.to_json,
      headers: {}
  )
  end
end

The error I get is about how real HTTP connections are disabled and this is an unregistered request. I took this to mean that Rails and/or WebMock are not matching the json somehow.

I looked into different things and tried different things. It seems like the square brackets are the issue. When I build just about anything without them, it works. When I put them back, the code breaks again.

How do I work with the square brackets in this scenario? I need them to be there, because the real API that I'm posting to requires this format.

By the way, the development environment communicates with the actual API and the codes works just fine. Human tests are perfect. It's just the automatic tests that keep failing!

UPDATE

Here's the error stack:

The variable that holds the response says <WebMock::NetConnectNotAllowedError: Real HTTP connections are disabled. Unregistered request: then shows the request, a suggested stub (which matches mine), and the registered ones.

Below that, I get:

#<ActionDispatch::Cookies::CookieOverflow: ActionDispatch::Cookies::CookieOverflow>
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/actionpack-5.1.6/lib/action_dispatch/middleware/cookies.rb:594:in `commit'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/actionpack-5.1.6/lib/action_dispatch/middleware/cookies.rb:465:in `[]='
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/actionpack-5.1.6/lib/action_dispatch/middleware/session/cookie_store.rb:117:in `set_cookie'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/session/abstract/id.rb:363:in `commit_session'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/session/abstract/id.rb:234:in `context'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/session/abstract/id.rb:226:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/actionpack-5.1.6/lib/action_dispatch/middleware/cookies.rb:613:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/actionpack-5.1.6/lib/action_dispatch/middleware/callbacks.rb:26:in `block in call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/activesupport-5.1.6/lib/active_support/callbacks.rb:97:in `run_callbacks'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/actionpack-5.1.6/lib/action_dispatch/middleware/callbacks.rb:24:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/actionpack-5.1.6/lib/action_dispatch/middleware/debug_exceptions.rb:59:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/actionpack-5.1.6/lib/action_dispatch/middleware/show_exceptions.rb:31:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/railties-5.1.6/lib/rails/rack/logger.rb:36:in `call_app'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/railties-5.1.6/lib/rails/rack/logger.rb:24:in `block in call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/activesupport-5.1.6/lib/active_support/tagged_logging.rb:69:in `block in tagged'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/activesupport-5.1.6/lib/active_support/tagged_logging.rb:26:in `tagged'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/activesupport-5.1.6/lib/active_support/tagged_logging.rb:69:in `tagged'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/railties-5.1.6/lib/rails/rack/logger.rb:24:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/actionpack-5.1.6/lib/action_dispatch/middleware/remote_ip.rb:79:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/actionpack-5.1.6/lib/action_dispatch/middleware/request_id.rb:25:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/method_override.rb:22:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/runtime.rb:22:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/activesupport-5.1.6/lib/active_support/cache/strategy/local_cache_middleware.rb:27:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/actionpack-5.1.6/lib/action_dispatch/middleware/executor.rb:12:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/actionpack-5.1.6/lib/action_dispatch/middleware/static.rb:125:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/sendfile.rb:111:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/railties-5.1.6/lib/rails/engine.rb:522:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/urlmap.rb:68:in `block in call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/urlmap.rb:53:in `each'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/urlmap.rb:53:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/capybara-3.2.1/lib/capybara/server/middleware.rb:44:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/puma-3.11.4/lib/puma/configuration.rb:225:in `call'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/puma-3.11.4/lib/puma/server.rb:632:in `handle_request'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/puma-3.11.4/lib/puma/server.rb:446:in `process_client'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/puma-3.11.4/lib/puma/server.rb:306:in `block in run'
/Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/puma-3.11.4/lib/puma/thread_pool.rb:120:in `block in spawn_thread'

AND

Failure/Error: raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE

ActionDispatch::Cookies::CookieOverflow:
   ActionDispatch::Cookies::CookieOverflow
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/session/abstract/id.rb:363:in `commit_session'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/session/abstract/id.rb:234:in `context'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/session/abstract/id.rb:226:in `call'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/railties-5.1.6/lib/rails/rack/logger.rb:36:in `call_app'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/railties-5.1.6/lib/rails/rack/logger.rb:24:in `block in call'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/railties-5.1.6/lib/rails/rack/logger.rb:24:in `call'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/method_override.rb:22:in `call'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/runtime.rb:22:in `call'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/sendfile.rb:111:in `call'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/railties-5.1.6/lib/rails/engine.rb:522:in `call'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/urlmap.rb:68:in `block in call'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/urlmap.rb:53:in `each'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/rack-2.0.5/lib/rack/urlmap.rb:53:in `call'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/capybara-3.2.1/lib/capybara/server/middleware.rb:44:in `call'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/puma-3.11.4/lib/puma/configuration.rb:225:in `call'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/puma-3.11.4/lib/puma/server.rb:632:in `handle_request'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/puma-3.11.4/lib/puma/server.rb:446:in `process_client'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/puma-3.11.4/lib/puma/server.rb:306:in `block in run'
 # /Users/ViviPoit/.rvm/gems/ruby-2.3.7/gems/puma-3.11.4/lib/puma/thread_pool.rb:120:in `block in spawn_thread'
vivipoit
  • 160
  • 2
  • 11
  • Can you show the stack trace of the error? (error name, line number, etc) You are stubbing the request expecting `"chave": "1234abcd"` why not just use that instead of `"chave": response["dados"]["btms"]["chave"]`? – arieljuod Oct 05 '18 at 03:28
  • Is that possible? To have a variable in the stub that is assigned during the test itself? I can't have `"chave"` hard coded, because the real code needs to get this from the API each time. – vivipoit Oct 05 '18 at 13:44
  • Are you doing real requests on tests? that's a bad idea. The error says `Real HTTP connections are disabled`, you shouldn't do real HTTP requests on tests. Also, is that your real code? or you removed some parts and mixed them? copy your spec as is. – arieljuod Oct 05 '18 at 13:57
  • I'm not doing real HTTP on tests. I meant that in the sense that the app needs to grab this `"chave"` value each time in production, so shouldn't my tests simulate this grabbing each time as well? – vivipoit Oct 05 '18 at 14:01
  • What's in `response` before you do `response["dados"]["btms"]["chave"]`? is it actually a hash? did you verify that? is `response["dados"]["btms"]["chave"]` returning "returned_key" as you expect it? You should create the hash before calling the RestClient post method and inspect the hash to actually be what you want. – arieljuod Oct 05 '18 at 14:06
  • It is. I did verify. I tested without anything for the `"data"` part, only the `"head"` part. When I do that, it passes. Then, if I add a `"data"` part with only {curly brackets} or plain string, it also works. I just tried it again. I commented out the lines of code that build the `data` variable and did `data = { "some_key": "something for value" }` in its place. It passed. I just can't seem to find the specific thing that changes when there are [square brackets], but that's what I need to test, so I need it to work with them. Do you happen to know something I can look into? – vivipoit Oct 05 '18 at 14:13
  • oooh, with "the square brakets" you meant the `data = []` array declaration, I thought you were referring the `responde[...]` line. You are stubind the request, you don't need the params to make sense since the request is not actually happening, you could send `data: "anything"` and it will pass, if using `data` as an array of hashes fails then don't use it, you don't need to send actual valid data since you are not testing that, you are just stubbing the response whatever data you send to it. – arieljuod Oct 05 '18 at 14:50
  • So my test shouldn't send what would be considered valid by the actual API? It can send whatever? In that case, wouldn't I be creating a test situation that's not actually testing the interaction? I'm not sure how to think abou this. I've always thought tests should replicate production as closely as possible. – vivipoit Oct 05 '18 at 15:48
  • 1
    You have to test 2 things: 1) your code calls the API with proper parameters, 2) your code reacts to different API responses. You should also test both things independantly: expect a call to `.post` with given parameters (and don't care about the response), and on another test, stub the response of the API call, call it with anything (doesn't matter) and check that your code does whatever it need to do. – arieljuod Oct 05 '18 at 17:32

1 Answers1

1

hash_including takes key names and key-value pairs to match, you're passing {...}.to_json to it, so it will try to match whole json value as a key.

Also your second request stub expects dados.head.chave to equal "1234abcd", but first stub returns "returned_key" in response["dados"]["btms"]["chave"] and obviously the stub does not match leading to WebMock::NetConnectNotAllowedError.

Vasfed
  • 18,013
  • 10
  • 47
  • 53
  • Sorry, that was a poor and failed attempt to not have an actual key-like value here. They match in my code. As mentioned, when I remove the square bracket, the code works, so I know the `"chave"` is not the problem. – vivipoit Oct 05 '18 at 13:45
  • Just noticed you're passing `{..}.to_json` to `hash_including`, try matching hash – Vasfed Oct 05 '18 at 14:20
  • That worked! Not using `hash_including` made it come through correctly. Is there a place where I can find the explanation to this? Because ALL of the other mocks use `hash_including`. I couldn't get the other mocks to work without it. Now, with these [square brackets], taking it away is what worked?! I'd love a resource to help me understand this better. Also, if you add that to your answer, I can choose it. – vivipoit Oct 05 '18 at 16:08