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