While pairing with Steph, who was helping me improve a spec file, I was asking how I could get rid of the top level let.

The file before was:

describe FooSerializer do
  context "foo serializer something something" do
    let(:model) { FactoryBot.create(:foo, :with_bar_trait) }

    subject { described_class.new(model) }

    it_behaves_like "a serialized model with status"

    it "has a name attribute" do
      expect(subject.attributes[:name]).to eq(model.name)
    end

    it "has a phone attribute" do
      expect(subject.attributes[:phone]).to eq(model.phone)
    end

    [etc etc for many attributes]
  end
end

After refactoring it looked like this:

describe FooSerializer do
  it_behaves_like "a serialized model with status" do
    let(:model) { FactoryBot.build(:foo, :with_bar_trait) }
    subject { described_class.new(model) }
  end

  it "has foo attributes" do
    model = FactoryBot.build(:foo, :with_bar_trait)
    subject = described_class.new(model)

    expect(subject.attributes[:name]).to eq(model.name)
    expect(subject.attributes[:phone]).to eq(model.phone)
    [etc etc all the attributes grouped here]
  end
end

1) Grouping the long list of attributes was beneficial and we could call the model only once in the it.

2) We successfully used build instead of create everywhere in the file.

3) The top level let was unavoidable because it_behaves_like, which came from the shared_examples, needs to have the let specified when passed as a block.

From Stack Overflow:

let(:data) { "saved data" }

shared_examples "saves data to right place" do
  it { expect(right_place.data).to eq data }
end

context "when something it saves to one place" do
  it_behaves_like "saves data to right place" do
    let(:right_place) { create(:place) }
  end
end

context "when whatever it saves to other place" do
  it_behaves_like "saves data to right place" do
    let(:right_place) { create(:place) }
  end
end