spec/contracts_spec.rb in contracts-0.5 vs spec/contracts_spec.rb in contracts-0.6

- old
+ new

@@ -1,10 +1,8 @@ -include Contracts - RSpec.describe "Contracts:" do before :all do - @o = Object.new + @o = GenericExample.new end describe "basic" do it "should fail for insufficient arguments" do expect { @@ -71,10 +69,163 @@ }.to raise_error(RuntimeError, /contract violation/) end end end + describe "usage in singleton class" do + it "should work normally when there is no contract violation" do + expect(SingletonClassExample.hoge("hoge")).to eq("superhoge") + end + + it "should fail with proper error when there is contract violation" do + expect { + SingletonClassExample.hoge(3) + }.to raise_error(ContractError, /Expected: String/) + end + + context "when owner class does not include Contracts" do + let(:error) { + # NOTE Unable to support this user-friendly error for ruby + # 1.8.7 and jruby 1.8, 1.9 it has much less support for + # singleton inheritance hierarchy + if Contracts::Support.eigenclass_hierarchy_supported? + [Contracts::ContractsNotIncluded, Contracts::ContractsNotIncluded::DEFAULT_MESSAGE] + else + [NoMethodError, /undefined method `Contract'/] + end + } + + it "fails with descriptive error" do + expect { + Class.new(GenericExample) do + class << self + Contract String => String + def hoge(name) + "super#{name}" + end + end + end + }.to raise_error(*error) + end + end + end + + describe "no contracts feature" do + it "disables normal contract checks" do + object = NoContractsSimpleExample.new + expect { object.some_method(3) }.not_to raise_error + end + + it "disables invariants" do + object = NoContractsInvariantsExample.new + object.day = 7 + expect { object.next_day }.not_to raise_error + end + + it "does not disable pattern matching" do + object = NoContractsPatternMatchingExample.new + + expect(object.on_response(200, "hello")).to eq("hello!") + expect(object.on_response(404, "Not found")).to eq("error 404: Not found") + expect { object.on_response(nil, "junk response") }.to raise_error(ContractError) + end + end + + describe "module usage" do + context "with instance methods" do + it "should check contract" do + expect { KlassWithModuleExample.new.plus(3, nil) }.to raise_error(ContractError) + end + end + + context "with singleton methods" do + it "should check contract" do + expect { ModuleExample.hoge(nil) }.to raise_error(ContractError) + end + end + + context "with singleton class methods" do + it "should check contract" do + expect { ModuleExample.eat(:food) }.to raise_error(ContractError) + end + end + end + + describe "singleton methods self in inherited methods" do + it "should be a proper self" do + expect(SingletonInheritanceExampleSubclass.a_contracted_self).to eq(SingletonInheritanceExampleSubclass) + end + end + + describe "anonymous classes" do + let(:klass) do + Class.new do + include Contracts + + Contract String => String + def greeting(name) + "hello, #{name}" + end + end + end + + let(:obj) { klass.new } + + it "does not fail when contract is satisfied" do + expect(obj.greeting("world")).to eq("hello, world") + end + + it "fails with error when contract is violated" do + expect { obj.greeting(3) }.to raise_error(ContractError, /Actual: 3/) + end + end + + describe "anonymous modules" do + let(:mod) do + Module.new do + include Contracts + include Contracts::Modules + + Contract String => String + def greeting(name) + "hello, #{name}" + end + + Contract String => String + def self.greeting(name) + "hello, #{name}" + end + end + end + + let(:klass) do + Class.new.tap { |klass| klass.send(:include, mod) } + end + + let(:obj) { klass.new } + + it "does not fail when contract is satisfied" do + expect(obj.greeting("world")).to eq("hello, world") + end + + it "fails with error when contract is violated" do + expect { obj.greeting(3) }.to raise_error(ContractError, /Actual: 3/) + end + + context "when called on module itself" do + let(:obj) { mod } + + it "does not fail when contract is satisfied" do + expect(obj.greeting("world")).to eq("hello, world") + end + + it "fails with error when contract is violated" do + expect { obj.greeting(3) }.to raise_error(ContractError, /Actual: 3/) + end + end + end + describe "instance methods" do it "should allow two classes to have the same method with different contracts" do a = A.new b = B.new expect { @@ -94,15 +245,15 @@ end end describe "class methods" do it "should pass for correct input" do - expect { Object.a_class_method(2) }.to_not raise_error + expect { GenericExample.a_class_method(2) }.to_not raise_error end it "should fail for incorrect input" do - expect { Object.a_class_method("bad") }.to raise_error(ContractError) + expect { GenericExample.a_class_method("bad") }.to raise_error(ContractError) end end it "should work for functions with no args" do expect { @o.no_args }.to_not raise_error @@ -178,18 +329,55 @@ end it "should fail for incorrect input" do expect { @o.do_call(nil) }.to raise_error(ContractError) end + + it "should handle properly lack of block when there are other arguments" do + expect { @o.double_with_proc(4) }.to raise_error(ContractError, /Actual: nil/) + end end describe "varargs" do it "should pass for correct input" do expect { @o.sum(1, 2, 3) }.to_not raise_error end it "should fail for incorrect input" do expect { @o.sum(1, 2, "bad") }.to raise_error(ContractError) + end + end + + describe "varargs with block" do + it "should pass for correct input" do + expect { @o.with_partial_sums(1, 2, 3) { |partial_sum| 2 * partial_sum + 1 } }.not_to raise_error + expect { @o.with_partial_sums_contracted(1, 2, 3) { |partial_sum| 2 * partial_sum + 1 } }.not_to raise_error + end + + it "should fail for incorrect input" do + expect { + @o.with_partial_sums(1, 2, "bad") { |partial_sum| 2 * partial_sum + 1 } + }.to raise_error(ContractError, /Actual: "bad"/) + + expect { + @o.with_partial_sums(1, 2, 3) + }.to raise_error(ContractError, /Actual: nil/) + + expect { + @o.with_partial_sums(1, 2, 3, lambda { |x| x }) + }.to raise_error(ContractError, /Actual: #<Proc/) + end + + context "when block has Func contract" do + it "should fail for incorrect input" do + expect { + @o.with_partial_sums_contracted(1, 2, "bad") { |partial_sum| 2 * partial_sum + 1 } + }.to raise_error(ContractError, /Actual: "bad"/) + + expect { + @o.with_partial_sums_contracted(1, 2, 3) + }.to raise_error(ContractError, /Actual: nil/) + end end end describe "contracts on functions" do it "should pass for a function that passes the contract" do