instance_variable_get returns nil when it's an empty string

484 views Asked by At

I use mail, nokogiri, and reverse_markdown gems to read an email and process the body, stripping out external tags and a unicode zero width character (u200b) to get an instance variable @body that refers to a string. Here's the relevant code:

@body = email.html_part.decoded.delete("\u200b")
@body = Nokogiri::HTML.parse(@body).at("div")
@body.css("br").each { |node| node.replace('<br />') }
@body.css("div").each { |node| node.replace(node.inner_html)}
@body = @body.inner_html
@body = ReverseMarkdown.convert(@body)
if @body.gsub(/\s+/, "").length == 0
  unless email.attachments.length > 0
    raise StandardError, "Empty email"
  end
end
puts @body.class # => "String"

After ReverseMarkdown.convert, the empty email is still three characters long, likely due to whitespace from stripping out tags, or because the Markdown converter forces it to have a whitespace with two spaces. That's why I do gsub length check to strip out white spaces.

When I run my rspec test:

context 'with no body' do
  it 'outputs the body to markdown' do
    puts instance_variable_get(:@body).class # => 'NilClass'
    expect(instance_variable_get(:@body)).to eq("")
  end
end

when the email is empty and has no attachments, it shows:

expected: ""
got: nil

I can't imagine why the class of the variable changes.

1

There are 1 answers

2
Andy Waite On

You haven't specified a receiver for instance_variable_get, so it will default to self which here is an RSpec example group.

You would need email.instance_variable_get(:@body) (assuming your subject is email).

However: using methods such as instance_variable_get in a test is a danger signal. You should aim to test a class by its public interface, not by examining its internals.