test: cover TransactionalFixtures, Widget
This commit is contained in:
1 parent
a52d7861fe
commit
1ecdf2b899
2 files changed
+154
No files matched your search
@@ -0,0 +1,142 @@
|
|||||||
|
require 'minitest/autorun'
|
||||||
|
require 'prism'
|
||||||
|
require 'tmpdir'
|
||||||
|
|
||||||
|
# Fixtures shared by the two test classes below live in their own module so
|
||||||
|
# the dedicated Store/Model pair is built exactly once, at file-load time,
|
||||||
|
# and both classes (the "victim" whose #run gets exercised, and the class
|
||||||
|
# that verifies it) see the same Widget table.
|
||||||
|
module TransactionalFixtures
|
||||||
|
DIR = Dir.mktmpdir('prism-transactional-tests')
|
||||||
|
STORE = Prism::Orm::Store.new(File.join(DIR, 'test.pstore'))
|
||||||
|
STORE.create_table(:widgets, %w[id name])
|
||||||
|
|
||||||
|
class Widget < Prism::Orm::Model
|
||||||
|
self.store = STORE
|
||||||
|
table_name :widgets
|
||||||
|
|
||||||
|
class << self
|
||||||
|
attr_accessor :rollback_count
|
||||||
|
end
|
||||||
|
self.rollback_count = 0
|
||||||
|
|
||||||
|
after_rollback { self.class.rollback_count += 1 }
|
||||||
|
end
|
||||||
|
|
||||||
|
# A second, INDEPENDENT Store (own file, own PStore, own TransactionRegistry)
|
||||||
|
# standing in for something like a separate jobs queue database that a
|
||||||
|
# single test method might write to alongside the main one.
|
||||||
|
OTHER_STORE = Prism::Orm::Store.new(File.join(DIR, 'other.pstore'))
|
||||||
|
OTHER_STORE.create_table(:gadgets, %w[id name])
|
||||||
|
|
||||||
|
class Gadget < Prism::Orm::Model
|
||||||
|
self.store = OTHER_STORE
|
||||||
|
table_name :gadgets
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Auto-discovered and run by Minitest like any other test class in this
|
||||||
|
# suite (contributing its own pass/fail to the run) - AND instantiated and
|
||||||
|
# `.run` manually, more than once, by TestTestingTransactional below to
|
||||||
|
# prove writes never survive between runs. Both uses must be safe together,
|
||||||
|
# since transactional rollback makes every run self-contained: whichever
|
||||||
|
# caller runs it, the widgets table is back to empty by the time #run returns.
|
||||||
|
class TransactionalVictimTest < Minitest::Test
|
||||||
|
include Prism::Testing::IntegrationTest
|
||||||
|
use_transactional_tests(store: TransactionalFixtures::STORE)
|
||||||
|
|
||||||
|
def test_creates_exactly_one_widget
|
||||||
|
TransactionalFixtures::Widget.create!(name: 'gizmo')
|
||||||
|
assert_equal 1, TransactionalFixtures::Widget.count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Writes to BOTH independent stores in a single test method - proves the
|
||||||
|
# `stores:` (plural) option rolls back every one of them, not just the
|
||||||
|
# first or the last.
|
||||||
|
class MultiStoreVictimTest < Minitest::Test
|
||||||
|
include Prism::Testing::IntegrationTest
|
||||||
|
use_transactional_tests(stores: [TransactionalFixtures::STORE, TransactionalFixtures::OTHER_STORE])
|
||||||
|
|
||||||
|
def test_creates_a_widget_and_a_gadget
|
||||||
|
TransactionalFixtures::Widget.create!(name: 'gizmo')
|
||||||
|
TransactionalFixtures::Gadget.create!(name: 'sprocket')
|
||||||
|
assert_equal 1, TransactionalFixtures::Widget.count
|
||||||
|
assert_equal 1, TransactionalFixtures::Gadget.count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Regression guard for classes that never call `use_transactional_tests` -
|
||||||
|
# their #run must behave exactly as plain Minitest::Test, unaffected by the
|
||||||
|
# override living in the shared module.
|
||||||
|
class PlainIntegrationSampleTest < Minitest::Test
|
||||||
|
include Prism::Testing::IntegrationTest
|
||||||
|
|
||||||
|
def test_noop
|
||||||
|
assert true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TestTestingTransactional < Minitest::Test
|
||||||
|
Widget = TransactionalFixtures::Widget
|
||||||
|
|
||||||
|
def test_writes_are_rolled_back_once_the_test_finishes
|
||||||
|
assert_equal 0, Widget.count
|
||||||
|
result = TransactionalVictimTest.new(:test_creates_exactly_one_widget).run
|
||||||
|
assert result.passed?, result.failures.map(&:message).join("\n")
|
||||||
|
assert_equal 0, Widget.count, 'the widget created inside the test should not survive it'
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_running_the_same_test_twice_never_sees_a_leftover_row
|
||||||
|
result1 = TransactionalVictimTest.new(:test_creates_exactly_one_widget).run
|
||||||
|
result2 = TransactionalVictimTest.new(:test_creates_exactly_one_widget).run
|
||||||
|
assert result1.passed?, result1.failures.map(&:message).join("\n")
|
||||||
|
assert result2.passed?, result2.failures.map(&:message).join("\n")
|
||||||
|
assert_equal 0, Widget.count
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_after_rollback_callbacks_fire_when_the_transaction_unwinds
|
||||||
|
before = Widget.rollback_count
|
||||||
|
result = TransactionalVictimTest.new(:test_creates_exactly_one_widget).run
|
||||||
|
assert result.passed?, result.failures.map(&:message).join("\n")
|
||||||
|
assert_equal before + 1, Widget.rollback_count
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_classes_that_never_opt_in_are_unaffected
|
||||||
|
result = PlainIntegrationSampleTest.new(:test_noop).run
|
||||||
|
assert result.passed?, result.failures.map(&:message).join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_transactional_tests_store_is_inherited_by_subclasses
|
||||||
|
subclass = Class.new(TransactionalVictimTest)
|
||||||
|
assert_equal TransactionalFixtures::STORE, subclass.transactional_tests_store
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_default_store_option_resolves_to_prism_orm_store_lazily
|
||||||
|
klass = Class.new(Minitest::Test) do
|
||||||
|
include Prism::Testing::IntegrationTest
|
||||||
|
use_transactional_tests
|
||||||
|
end
|
||||||
|
assert_equal :default, klass.transactional_tests_store
|
||||||
|
assert_equal [:default], klass.transactional_tests_stores
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_multiple_stores_all_roll_back_after_the_test_finishes
|
||||||
|
assert_equal 0, TransactionalFixtures::Widget.count
|
||||||
|
assert_equal 0, TransactionalFixtures::Gadget.count
|
||||||
|
|
||||||
|
result = MultiStoreVictimTest.new(:test_creates_a_widget_and_a_gadget).run
|
||||||
|
assert result.passed?, result.failures.map(&:message).join("\n")
|
||||||
|
|
||||||
|
assert_equal 0, TransactionalFixtures::Widget.count,
|
||||||
|
'the widget in the FIRST configured store must not survive the test'
|
||||||
|
assert_equal 0, TransactionalFixtures::Gadget.count,
|
||||||
|
'the gadget in the SECOND, independent store must not survive the test either'
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_stores_option_is_exposed_as_an_array_and_store_returns_its_first_element
|
||||||
|
assert_equal [TransactionalFixtures::STORE, TransactionalFixtures::OTHER_STORE],
|
||||||
|
MultiStoreVictimTest.transactional_tests_stores
|
||||||
|
assert_equal TransactionalFixtures::STORE, MultiStoreVictimTest.transactional_tests_store
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -52,4 +52,16 @@ class ViewTest < Minitest::Test
|
|||||||
error = assert_raises(Prism::View::TemplateNotFound) { @view.render('posts/missing') }
|
error = assert_raises(Prism::View::TemplateNotFound) { @view.render('posts/missing') }
|
||||||
assert_includes error.message, 'posts/missing'
|
assert_includes error.message, 'posts/missing'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_render_emits_a_render_template_event_per_template_file
|
||||||
|
events = []
|
||||||
|
subscriber = Prism::Instrumentation.subscribe('render_template.prism') { |event| events << event }
|
||||||
|
begin
|
||||||
|
@view.render('posts/index', locals: { greeting: 'hi' }, layout: :application)
|
||||||
|
ensure
|
||||||
|
Prism::Instrumentation.unsubscribe(subscriber)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal %w[posts/index layouts/application], events.map { |e| e.payload[:identifier] }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
Reference in new issue
Block a user