Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jdickey/29bac445745734c174a0 to your computer and use it in GitHub Desktop.
Save jdickey/29bac445745734c174a0 to your computer and use it in GitHub Desktop.
Fortitude Issue 28 supporting code and remarks

I'd opened Fortitude Issue #28 over some problems I'd been having with unit testing of Widgets that conditionally yielded to their blocks. I've included the two directly relevant code files as they existed when the issue was opened.

A slightly simplified version of my attempted spec (using MiniTest::Spec) is included in simple_container_v1_test.rb, along with the test_helper.rb support file. This is what led to the filing of the issue, when running that test produced the output


# Running tests with run options --seed 42296:

E
Error:
Views::Welcome::Index::SimpleContainer::produces output from the #to_html method that#test_0001_is wrapped in a :div element with the .container CSS style:
Fortitude::Errors::NoBlockToYieldTo: You're trying to call 'yield' (or 'yield_from_widget', or the Erector-compatibility method 'call_block')
from the widget #<Views::Welcome::Index::SimpleContainer:0x007fceb2d12968>; however, there is nothing to yield to. Fortitude
looks for something to yield to in this order:
  1. A block passed to a yield at render time directly (usually via the 'widget' call);
  2. A block passed to the constructor of the widget;
  3. The layout the widget is being rendered in.
None of these exist here, and so calling 'yield', 'yield_from_widget', or 'call_block' is an
undefined operation.
    /Users/jeffdickey/src/rails/meldd/meldd1/vendor/ruby/2.2.0/gems/fortitude-0.9.4/lib/fortitude/rendering_context.rb:192:in `yield_from_widget'
    /Users/jeffdickey/src/rails/meldd/meldd1/vendor/ruby/2.2.0/gems/fortitude-0.9.4/lib/fortitude/widget/rendering.rb:120:in `_fortitude_yield_from_widget'
    /Users/jeffdickey/src/rails/meldd/meldd1/vendor/ruby/2.2.0/gems/fortitude-0.9.4/lib/fortitude/widget/rendering.rb:126:in `yield_from_widget'
    /Users/jeffdickey/src/rails/meldd/meldd1/vendor/ruby/2.2.0/gems/fortitude-0.9.4/lib/fortitude/widget/rendering.rb:31:in `block in render_to'
    /Users/jeffdickey/src/rails/meldd/meldd1/app/views/welcome/index/simple_container.rb:7:in `block in content'
    (eval):36:in `block in tag_div'
    /Users/jeffdickey/src/rails/meldd/meldd1/vendor/ruby/2.2.0/gems/fortitude-0.9.4/lib/fortitude/rendering_context.rb:114:in `emitting_tag!'
    (eval):6:in `tag_div'
    /Users/jeffdickey/src/rails/meldd/meldd1/app/views/welcome/index/simple_container.rb:7:in `content'
    (eval):3:in `run_content'
    /Users/jeffdickey/src/rails/meldd/meldd1/vendor/ruby/2.2.0/gems/fortitude-0.9.4/lib/fortitude/widget/rendering.rb:35:in `block in render_to'
    /Users/jeffdickey/src/rails/meldd/meldd1/vendor/ruby/2.2.0/gems/fortitude-0.9.4/lib/fortitude/rendering_context.rb:79:in `record_widget'
    /Users/jeffdickey/src/rails/meldd/meldd1/vendor/ruby/2.2.0/gems/fortitude-0.9.4/lib/fortitude/widget/rendering.rb:33:in `render_to'
    /Users/jeffdickey/src/rails/meldd/meldd1/vendor/ruby/2.2.0/gems/fortitude-0.9.4/lib/fortitude/widget/rendering.rb:64:in `widget'
    test/views/welcome/index/simple_container_test.rb:12:in `content'
    (eval):3:in `run_content'
    /Users/jeffdickey/src/rails/meldd/meldd1/vendor/ruby/2.2.0/gems/fortitude-0.9.4/lib/fortitude/widget/rendering.rb:35:in `block in render_to'
    /Users/jeffdickey/src/rails/meldd/meldd1/vendor/ruby/2.2.0/gems/fortitude-0.9.4/lib/fortitude/rendering_context.rb:79:in `record_widget'
    /Users/jeffdickey/src/rails/meldd/meldd1/vendor/ruby/2.2.0/gems/fortitude-0.9.4/lib/fortitude/widget/rendering.rb:33:in `render_to'
    /Users/jeffdickey/src/rails/meldd/meldd1/vendor/ruby/2.2.0/gems/fortitude-0.9.4/lib/fortitude/widget/rendering.rb:45:in `to_html'
    test/views/welcome/index/simple_container_test.rb:8:in `block (2 levels) in <main>'


Finished tests in 0.007187s, 139.1338 tests/s, 0.0000 assertions/s.


1 tests, 0 assertions, 0 failures, 1 errors, 0 skips

UPDATE 19 July 14:23 (GMT+8): I was able to successfully unit test the widget using the spec code in simple_container_v2_test.rb below. What was required was to

  1. Create a subclass of the class under test, whose #content called the widget with a block;
  2. Instantiate that subclass, essentially as the new SUT;
  3. Examine the output of the subclass' #to_html method.

It seems that MiniTest, when evaluating the original widget class, ignores the if block_given? conditional on the yield, and thus chokes when no block is specified.

require_relative 'simple_container'
# Class encapsulating all page-specific view code for Welcome/#Index.
class Views::Welcome::Index < Views::Base
# Bootstrap "Jumbotron" page-header element.
class Jumbotron < Views::Base
needs :title
# This method smells of :reek:NestedIterators
def content
div(class: 'jumbotron') do
widget SimpleContainer do
h1 title
yield if block_given?
end
end
end
end # class Views::Welcome::Index::Jumbotron
end # class Views::Welcome::Index
# Class encapsulating all page-specific view code for Welcome/#Index.
class Views::Welcome::Index < Views::Base
# Utility-level widget for a simple Bootstrap container div.
class SimpleContainer < Views::Base
def content
div(class: 'container') { yield if block_given? }
end
end # class Views::Welcome::Index::Container
end # class Views::Welcome::Index
require 'test_helper'
require_relative '../../../../app/views/welcome/index/simple_container.rb'
describe 'Views::Welcome::Index::SimpleContainer' do
let(:described_class) { Views::Welcome::Index::SimpleContainer }
let(:obj) { described_class.new }
let(:actual) { obj.to_html.strip }
describe 'produces output from the #to_html method that' do
it 'is wrapped in a :div element with the .container CSS style' do
expect(actual).must_match %r{\A<div class="container">.+?</div>\z}
end
end # describe 'produces output from the #to_html method that'
end
require 'test_helper'
require_relative '../../../../app/views/welcome/index/simple_container.rb'
describe 'Views::Welcome::Index::SimpleContainer' do
let(:described_class) { Views::Welcome::Index::SimpleContainer }
let(:obj) { dummy_class.new }
let(:actual) { obj.to_html.strip }
let(:dummy_class) {
Class.new(described_class) do
def content
widget Views::Welcome::Index::SimpleContainer do
text 'Hello.'
end
end
end
}
describe 'produces output from the #to_html method that' do
it 'is wrapped in a :div element with the .container CSS style' do
expect(actual).must_match %r{\A<div class="container">.+?</div>\z}
end
end # describe 'produces output from the #to_html method that'
end
ENV['RAILS_ENV'] ||= 'test'
if ENV['RAILS_ENV'] == 'test'
require 'simplecov'
require 'minitest/autorun'
require 'pry-byebug'
require 'codeclimate-test-reporter'
require 'coveralls'
SimpleCov.start 'rails' do
add_filter '/vendor/'
formatter SimpleCov::Formatter::MultiFormatter[
CodeClimate::TestReporter::Formatter,
Coveralls::SimpleCov::Formatter,
SimpleCov::Formatter::HTMLFormatter
]
end
# FIXME: Uncomment this once Coveralls is properly set up. Including it before
# then *prevents coverage reports from being generated.*
# Coveralls.wear!
end
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'minitest/rails'
# To add Capybara feature tests add `gem 'minitest-rails-capybara'`
# to the test group in the Gemfile and uncomment the following:
require 'minitest/rails/capybara'
# Uncomment for awesome colorful output
# require 'minitest/pride'
require 'minitest/reporters'
Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(
color: true, detailed_skip: true, fast_fail: true)]
# Stock Rails support "class" module.
module ActiveSupport
# Base class for all test cases.
class TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests alphabetically.
fixtures :all
# Add more helper methods to be used by all tests here...
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment