Here's a quick bite for those using result monads provided by dry-monads to code workflows and test them with Minitest . Nothing fancy, please leave suggestions to make it more useful in the comments.
As a quick reminder: Result monads are simple objects which wrap the return value of methods in a uniform manner thus turning them into building blocks which can be stacked on top of one another like Lego bricks. Operations in Hanami 2 make use of them thru dry-operations, a thin extra layer which adds steps to the mix. Together, these great gems can turn a bowl of messy if
/then
/else
/rescue
spaghetti into a well layered lasagne. You can eat both of them alright, but only the lasagne will earn you applause for the optics and the ability to add more layers ad lib.
You still want to test this though, so here are the custom Minitest (assertions and) expectations for the job:
# frozen_string_literal: true
require 'dry-monads'
module Minitest::Assertions
def assert_success(expected, actual, msg=nil)
msg = message(msg) { "Expected #{mu_pp(actual)} to be Success with #{mu_pp(expected)}" }
assert(expected == actual.value!, msg)
end
def refute_success(expected, actual, msg=nil)
msg = message(msg) { "Expected #{mu_pp(actual)} not to be Success with #{mu_pp(expected)}" }
assert(expected != actual.value!, msg)
end
def assert_failure(expected, actual, msg=nil)
msg = message(msg) { "Expected #{mu_pp(actual)} to be Failure with #{mu_pp(expected)}" }
assert(expected == actual.failure, msg)
end
def refute_failure(expected, actual, msg=nil)
msg = message(msg) { "Expected #{mu_pp(actual)} not to be Failure with #{mu_pp(expected)}" }
assert(expected != actual.failure, msg)
end
end
Dry::Monads::Success.infect_an_assertion :assert_success, :must_be_success
Dry::Monads::Success.infect_an_assertion :refute_success, :wont_be_success
Dry::Monads::Failure.infect_an_assertion :assert_failure, :must_be_failure
Dry::Monads::Failure.infect_an_assertion :refute_failure, :wont_be_failure
Say you want to test the following nonsensical example:
class Lasagne
include Dry::Monads[:result]
def cooking_time(tool)
case tool
when :microwave then Success(10)
when :oven then Success(30)
when :nuke then Failure(:epic)
else Failure(:unsupported_tool)
end
end
end
Here are the tests in spec notation (because I like it so much better than assertions):
describe Lasagne do
subject { Lasagne.new }
it "quickly cooks in a microwave (but don't tell any Italians)" do
_(subject.cooking_time(:microwave)).must_be_success 10
end
it "cooks perfectly in an oven" do
_(subject.cooking_time(:oven)).must_be_success 30
end
it "ends us all when using a :nuke" do
_(subject.cooking_time(:nuke)).must_be_failure :epic
end
it "doesn't cook with just any tool" do
_(subject.cooking_time(:nose_hair_trimmer)).must_be_failure :unsupported_tool
end
end
You can of course do without, but I find the vanilla alternative less appealing:
it "cooks perfectly in an oven" do
_(subject.cooking_time(:oven)).must_equal Dry::Monads::Success(30)
end
Bon appetit!
Banner image by Rainer Stropek
Author Of article : Sven Schwyn Read full article