How to #let a JSON file in RSpec on Rails

rspec-json-expectations
rspec file upload
rspec file_fixture
rspec json matchers
rspec fixture_file_upload
rspec api testing
rspec-rails
get method in rspec

EDIT:

The solution seems to start with describe KayNein::Twitter do instead of RSpec.describe KayNein::Twitter do.

Why is this the case?

Rails 5.2.1 Ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]

Original Question

I have a class that initializes with a JSON file:

module KayNein
  class Twitter < Handler

    def initialize(json_file)
      @website_hash = JSON.parse(json_file)
    end

And I'm trying to write an RSpec:

RSpec.describe KayNein::Twitter do
  let (:json_file) {File.read(PATH_TO_FILE)}
  context "Initialized" do
    subject = KayNein::Twitter.new(json_file)
  end
end

But RSpec gives me this error:

An error occurred while loading ./spec/app/kay_nein/sites/twitter_spec.rb.
Failure/Error: subject = KayNein::Twitter.new(json_file)
  `json_file` is not available on an example group (e.g. a `describe` or `context` block). It is only available from within individual examples (e.g. `it` blocks) or from constructs that run
 in the scope of an example (e.g. `before`, `let`, etc).
# ./spec/app/kay_nein/sites/twitter_spec.rb:8:in `block (2 levels) in <top (required)>'
# ./spec/app/kay_nein/sites/twitter_spec.rb:7:in `block in <top (required)>'
# ./spec/app/kay_nein/sites/twitter_spec.rb:5:in `<top (required)>'

What am I doing wrong?

Even this gives me an error, so it has nothing to do with the file:

RSpec.describe KayNein::Twitter do
  let (:json_file) {"Hello world"}
  context "Initialized" do
    subject = KayNein::Twitter.new(json_file)
  end
end 

Can you try something like this ?

let(:file) { File.read(File.join('spec', 'fixtures', 'public', filename)) }

describe "File" do
  it "should be a file" do
    expect(file).to be_a File
  end

  it "should initialize and read the contents"
    initialize_file = KayNein::Twitter.new(file)
    expect(response.body["key"]).to eq(..)
  end
end

JSON data for rspec tests, In cases like this, I'll create fixture files for the JSON I want to import. you could use FactoryGirl to set attributes, then get it into JSON and import it. Following Jesse's advice, in Rails 5 now you could use file_fixture (docs). Managing HTTP Requests in Rails Rspec Test. Let's add a module for stubbing request if the type of test requires stubbing. module JsonReader def json_response(response_code, file_name)

We have two ways to DRY up tests (before and let) that share an intersecting purpose, to create variables that are common across tests. For common variable instantiation, the Ruby community prefers let, and while before is often used to perform actions common across tests.

when you use the context you can initialize the let inside the context block like this, will work

describe KayNein::Twitter do
  context "Initialized" do
    let (:json_file) {File.read(PATH_TO_FILE)}
    subject = KayNein::Twitter.new(json_file)
  end
end

otherwise if you don't want to use context, can try this way

describe KayNein::Twitter do
let (:json_file) {File.read(PATH_TO_FILE)}
  it "Initialized" do
    subject = KayNein::Twitter.new(json_file)
  end
end

Validating JSON Schemas with an RSpec Matcher, Use RSpec and JSON Schema to create a test-driven process in which If you'​ve worked on a test-driven JSON API written in Ruby before, get v1_current_user_url, {}, auth_header expect(response.status).to eq 200 Next, we'll define a custom RSpec matcher that validates the response object in our  RSpec Rails Swagger. This gem helps you generate Swagger docs by using RSpec to document the paths. You execute a command to run the tests and generate the .yaml or .json output. Running the tests ensures that your API and docs are in agreement, and generates output that can be saved as response examples.

You can think of let as defining accessors. And those accessors are available only inside example blocks it or specify.

You are trying to use json_file (defined in let) inside a context. And this is exactly what the error message is trying to tell you:

json_file is not available on an example group (e.g. a describe or context block). It is only available from within individual examples (e.g. it blocks) or from constructs that run in the scope of an example (e.g. before, let, etc).

You can fix it like this:

RSpec.describe KayNein::Twitter do
  let (:json_file) {File.read(PATH_TO_FILE)}
  context "Initialized" do
    specify do # it instead of specify also works
     subject = KayNein::Twitter.new(json_file)
     expect(subject).not_to be_nil # this is just an example, not needed to 
                                   # get rid of the issue
    end
  end
end

or even better - define subject outside of example block (for reusability etc) like this:

RSpec.describe KayNein::Twitter do
  let (:json_file) {File.read(PATH_TO_FILE)}
  subject { KayNein::Twitter.new(json_file) }
  context "Initialized" do
    specify do # it instead of specify also works
     expect(subject).not_to be_nil # this is just an example, not needed to 
                                   # get rid of the issue
    end
  end
end

Rails API Request Specs with RSpec - Lindsay Criswell, Remember to install the RSpec gem and run rails g rspec:install to generate a spec folder end it "JSON body response contains expected recipe attributes" do to require rails_helper at the top of your spec files to make sure RSpec will run. $ rails generate rspec:install Generate a scaffold $ rails generate scaffold Widget name:string This generates files in the app and spec directories. The files in the app directory are generated by Rails, and Rails delegates the generation of the files in the spec directory to RSpec. Run migrations $ rake db:migrate && rake db:test:prepare Run RSpec $ rake spec or $ rspec spec --format documentation

How to Properly Test a Rails API with Rspec, Ruby on Rails creates a tests folder by default. Since we will be using the Rspec gem for our Rails API's tests, we'll want to get rid of that folder and proceed to initialize an rspec directory in expect(JSON.parse(response.body).size).to eq(​20) In RSpec, assertions are called expectations, and every expectation is built around a matcher. When you expect(a).to eq(b), you’re using the eq matcher. In addition to the matchers that come standard in RSpec, here are some extras that make it easier to test the various parts of a Rails system:

request spec - Request specs - RSpec Rails - RSpec, Request specs provide a thin wrapper around Rails' integration tests, and are Rails integration methods; requesting a JSON response; requesting a JSON response Given: a file named "spec/requests/widget_management_spec.rb" with: page" do get "/widgets/new" expect(response).to render_template(:new) post  Let’s start creating a JSON file: You just need to open any text editor paste below code and save that file with .json extension that’s it, your JSON file is ready. You can save this file like sample-json.json.

controller spec - Controller specs - RSpec Rails - RSpec, Given: a file named "spec/controllers/widgets_controller_spec.rb" with: :​controller do describe "GET index" do it "has a 200 status code" do get :index expect(response.status).to setting a different content type for example json (​request type). For common variable instantiation, the Ruby community prefers let, and while before is often used to perform actions common across tests. when you use the context you can initialize the let inside the context block like this, will work describe KayNein::Twitter do context "Initialized" do let (:json_file) {File.read(PATH_TO_FILE)} subject

Comments
  • have you defined: ` {File.read(PATH_TO_FILE)}`?
  • also, does File.read return a value? I couldn't find it the documentation.
  • @BKSpurgeon yes it is defined, I'm just not disclosing the file path here for the sake of privacy
  • @BKSpurgeon yes, File.read returns a value, it reads it as a JSON file.
  • are you pasting in the full file? is your test wrapped in a it "returns a file" do {write your test here} end?
  • I think this inadvertantly helped me solve it. I removed "RSpec" from Rspec.describe and now I can #let.