One of the advantages of views as objects is that we can unit test them. We can both understand if a specific presentational logic behaves correctly and/or assert the contents of the rendered markup.

    For the following example we’re gonna use RSpec for the concise syntax for test doubles.

    1. # spec/web/views/books/show_spec.rb
    2. require_relative '../../../../apps/web/views/books/show'
    3. RSpec.describe Web::Views::Books::Show do
    4. let(:exposures) { Hash[book: double('book', price: 1.00), current_user: user, params: {}] }
    5. let(:template) { Hanami::View::Template.new('apps/web/templates/books/show.html.erb') }
    6. let(:view) { Web::Views::Home::Another.new(template, exposures) }
    7. let(:rendered) { view.render }
    8. let(:user) { double('user', admin?: false) }
    9. context "price" do
    10. it "returns formatted price" do
    11. expect(view.formatted_price).to eq("$1.00")
    12. end
    13. end
    14. context "edit link" do
    15. it "doesn't show it by default" do
    16. expect(rendered).to_not match(%(<a href="">edit</a>))
    17. end
    18. context "when admin" do
    19. let(:user) { double('user', admin?: true) }
    20. it "shows it" do
    21. expect(rendered).to match(%(<a href="">edit</a>))
    22. end
    23. end
    24. end
    25. end

    The first part of the test code above is about book’s formatting price. This presentational logic is verified by asserting the returning value of view.formatted_price.

    The remaining code is about permissions related logic: the edit link must be rendered only if the current user is an admin. This is tested by looking at the output of the template.

    Asserting presentational logic directly via view’s methods, or indirectly via rendered markup are two EQUIVALENT ways.

    Notice that exposures includes an unused params key. While this is not strictly required, we recommend providing it since it’s expected by some standard view helpers (e.g. form helpers).

    Let’s have a look at the corresponding production code.

    1. # apps/web/views/books/show.rb
    2. module Web
    3. module Views
    4. module Books
    5. class Show
    6. include Web::View
    7. def formatted_price
    8. "$#{ format_number book.price }"
    9. end
    10. def edit_link
    11. if can_edit_book?
    12. link_to "Edit", routes.edit_book_path(id: book.id)
    13. end
    14. end
    15. private
    16. def can_edit_book?
    17. current_user.admin?
    18. end
    19. end
    20. end
    21. end
    22. end