Testing
dry-view is designed to encourage better testing of your views, with every component designed to support unit testing, in full isolation. This means you can test your views at whatever level of granularity makes sense for you, all the while maintaining a responsive test-driven development cycle.
Testing views
To test a view object in full, initialize it, passing in any dependencies it requires. Provide test doubles for these if you want to simulate certain conditions. Then you can call the view and express the behavior you desire for its rendered output string.
Given this view:
class ArticleView < Dry::View
  config.template = "article"
  attr_reader :repo
  def initialize(repo:)
    @repo = repo
  end
  expose :article do |slug:|
    repo.by_slug(slug)
  end
end
A test could look like this:
RSpec.describe ArticleView do
  subject(:view) { described_class.new(repo: repo) }
  let(:repo) { double(:repo) }
  let(:article) { double(:article, title: "Hello World") }
  before do
    allow(repo).to receive(:by_slug).with("hello-world").and_return article
  end
  describe "#call" do
    subject(:rendered) { view.call(slug: "hello-world") }
    it "renders the article details" do
      expect(rendered.to_s).to include("<h1>Hello World</h1>")
    end
  end
end
Testing exposures
If you'd like to test a view's exposures directly, you can access them after calling the view:
RSpec.describe ArticleView do
  subject(:view) { described_class.new(repo: repo) }
  let(:repo) { double(:repo) }
  let(:article) { double(:article, title: "Hello World") }
  before do
    allow(repo).to receive(:by_slug).with("hello-world").and_return article
  end
  describe "exposures" do
    subject(:rendered) { view.call(slug: "hello-world") }
    it "renders the article details" do
      expect(rendered[:article].title).to eq "Hello World"
    end
  end
end
Testing simple part behavior
To test simple part behavior, initialize a part and make your expectations against its methods:
module Parts
  class Article < Dry::View::Part
    def byline
      "By #{author_name}"
    end
  end
end
RSpec.describe(Parts::Article) do
  subject(:part) { described_class.new(value: article) }
  let(:article) { double(:article, author_name: "Jane Doe") }
  describe "#byline" do
    it "includes the author name" do
      expect(part.byline).to eq "By Jane Doe"
    end
  end
end
Testing part behavior requiring a render environment
To test part behavior that renders partials or accesses the context, the part will need to be initialized with a name and render environment. You can get a render environment from a related view class via its .template_env:
class ArticleView < Dry::View
  config.template = "article"
  config.part_namespace = Parts
  # ...
end
module Parts
  class Article < Dry::View::Part
    def author_details_html
      render(:author_details, author: author)
    end
  end
end
RSpec.describe(Parts::Article) do
  subject(:part) {
    described_class.new(
      value: article,
      name: :article,
      render_env: ArticleView.template_env,
    )
  }
  let(:article) { double(:article, author: double(:author, name: "Jane Doe")) }
  describe "#author_details_html" do
    it "includes author details" do
      html = part.author_details_html
      expect(html).to include "Jane Doe"
    end
  end
end