ruby-on-railsrspecrspec-railsdryrswag

Make rspec more DRY by accessing let variable in describe block


I'm trying to DRY up rspec tests that keeps on declaring let(:createBody) do...end. I thought to take out the let statement and just modify the variable on each block as needed. However the variable (createBody) is not accessible. Added a comment on sample code below to see exactly where I want to modify createBody.

post "Creates object", controller_action: :create do
  ...
  let(:createBody) do
    {
      name: "yes",
      type: "my_type",
      amount_cents: 200,
      parameters: {
        day: "today"
      },
      user_group_ids: [],
      user_ids: []
    }
  end

  # no users or groups
  response 201, "object created" do
    ...

    run_test! do |res|
      # this block works fine and can access createBody
      body = JSON.parse(res.body)
      expect(body["name"]).to eq(createBody[:name])
      expect(body["amount_cents"]).to eq(createBody[:amount_cents])
      expect(body["type"]).to eq(createBody[:type])
    end
  end

  # with users
  response 201, "autocharge rule created" do
    ...

    # want to make some modifications on createBody here, like adding user_ids, but it is out of scope
    
    run_test! do |res|
      # this block works fine and can access createBody
      body = JSON.parse(res.body)
      expect(body["user_ids"]).to match_array(createBody[:user_ids])
    end
  end
  ...
end

Is there a way to achieve this approach? Are there any alternatives?


Solution

  • I assume response block works in the same way as context

    post "Creates object", controller_action: :create do
      let(:createBody) do
        {
          name: "yes",
          type: "my_type",
          amount_cents: 200,
          parameters: {
            day: "today"
          },
          user_group_ids: [],
          user_ids: user_ids # now we can add a let(:user_ids) to each context
        }
      end
    
      response 201, "object created" do
        let(:user_ids) { [] } # empty since we're not using it here
    
        ...
    
        run_test! do |res|
          # this block works fine and can access createBody
          body = JSON.parse(res.body)
          expect(body["name"]).to eq(createBody[:name])
          expect(body["amount_cents"]).to eq(createBody[:amount_cents])
          expect(body["type"]).to eq(createBody[:type])
        end
      end
    
      # with users
      response 201, "autocharge rule created" do
        let(:user_ids) { [1, 2, 3] } # add required ids
    
        ...
        
        run_test! do |res|
          # this block works fine and can access createBody
          body = JSON.parse(res.body)
          expect(body["user_ids"]).to match_array(createBody[:user_ids]) # we can match by user_ids now
        end
      end
      ...
    end