-1

I have a piece of code which has:

requests.each do |request|
  request.each do |rqst|
    select_keys = ["author", "author_name"]
    selected_unit_req = rqst.select { |m| select_keys.include?(m) }
    user = Etl::Models::User.where(username: selected_unit_req["author"]["username"] )
    user.merge!({ author_name: selected_unit_req["author"]["name"] })
  end
end

I am unable to test the merge! method , getting NoMethodError:undefined method '[]' for nil:NilClass when I run my existing unit tests. Any suggestions?

anothermh
  • 9,815
  • 3
  • 33
  • 52
  • Why would you write a test for another (builtin) class's methods? And from a style perspective, [avoid using `{}` for multi-line blocks](https://github.com/rubocop-hq/ruby-style-guide#single-line-blocks). Trying to decipher your example is making my head hurt. – anothermh Dec 18 '18 at 21:36
  • I am getting the following error when I run my unit tests. `NoMethodError: undefined method '[]' for nil:NilClass` – shitgetsreal Dec 18 '18 at 21:55
  • Your example is way, way too complex. Simplify it first. You're making something like ten method calls, chaining multiple method calls, including a conditional, and including a rescue all in a single line. The error you're showing doesn't help decipher it at all because you haven't included the code for the test, and on top of that you're calling `[]` _four times_ in that one line. Make your life easier: refactor this code into something readable and sensible, _then_ test it. – anothermh Dec 18 '18 at 22:04
  • I am modifying my code( providing a simpler example). – shitgetsreal Dec 18 '18 at 22:10
  • @anothermh i just added a different piece of code – shitgetsreal Dec 18 '18 at 22:20
  • Can you show the failing test, too? – aridlehoover Dec 19 '18 at 08:37

1 Answers1

0

Let's assume that in the example you gave rqst is returning an empty hash {}.

When you set selected_unit_req it is set to an empty hash:

selected_unit_req = rqst.select { |m| select_keys.include?(m) }
=> {}

When you subsequently call selected_unit_req["author"] the hash is checked for a key named author, and this returns nil:

selected_unit_req["author"]
=> nil

So running the full call selected_unit_req["author"]["username"] can be seen to be equivalent to the following:

nil["username"]
NoMethodError: undefined method `[]' for nil:NilClass

That's why the error is being raised; you expect selected_unit_req["author"] to return a hash with a key username but it is instead returning nil.

Ensure that selected_unit_req has the hash keys and values that you expect first.

Update

In the comments below you asked how to verify it has the keys you require. The answer to that depends on a few things, like what behavior you want to see in the event it doesn't have it and the version of Ruby you're using.

I'm going to assume that it's okay to skip rqst if it doesn't have the keys and that you're using a modern version of Ruby (>= 2.3.0).

Here's one way to do it using the dig method for the Hash class:

request.each do |rqst|
  next unless rqst.dig('author', 'username') && rqst.dig('author', 'name')
  user = Etl::Models::User.where(username: rqst['author']['username'] )
  user.merge!({ author_name: rqst['author']['name'] })
end

What we're doing is calling next if rqst does not have the key author with a sub-key named username and another sub-key named name. This works because dig will return nil if the hash does not have those keys in it.

Let's pretend for a moment that rqst == {}. How that line would be interpreted is:

next unless nil && nil

Since nil is falsy in Ruby, it ends up calling next to skip over the current rqst value and move on to the next one in the iteration.

I've also removed selected_unit_req because it's unnecessarily repetitive; since you already know exactly what keys/values you want out of rqst there is no need to create a new hash with just those values; instead, you can just extract those values and use them directly where needed.

anothermh
  • 9,815
  • 3
  • 33
  • 52