├── var ├── name ├── title ├── created ├── version ├── organization ├── summary ├── authors ├── copyrights ├── repositories ├── requirements ├── description └── resources ├── lib ├── ae.yml ├── ae │ ├── core_ext.rb │ ├── adapter.rb │ ├── adapters │ │ ├── rspec.rb │ │ ├── minitest.rb │ │ └── testunit.rb │ ├── ansi.rb │ ├── core_ext │ │ ├── exception.rb │ │ └── helpers.rb │ ├── version.rb │ ├── pry.rb │ ├── should.rb │ ├── assertion.rb │ ├── must.rb │ ├── expect.rb │ ├── subjunctive.rb │ ├── assert.rb │ ├── basic_object.rb │ ├── check.rb │ ├── legacy.rb │ └── assertor.rb └── ae.rb ├── demo ├── applique │ └── ae.rb ├── 02_assertion.md ├── 06_counts.md ├── 07_matchers.md ├── 08_check.md ├── 05_expect.md ├── 01_overview.md ├── 04_subjunctive.md └── 03_assert.md ├── Gemfile ├── .gitignore ├── try ├── cucumber │ └── features │ │ ├── support │ │ └── env.rb │ │ ├── cucumber.feature │ │ └── step_definitions │ │ └── cucumber_steps.rb ├── rspec │ └── example.rb ├── minitest │ └── example.rb └── testunit │ └── example.rb ├── .yardopts ├── work ├── Rubyfile ├── cucumber.yml ├── consider │ ├── 08_assay.rdoc │ ├── dot.rb │ ├── counts.rb │ └── detest.rb ├── .rubyspec0 ├── trash │ ├── verify.rb │ ├── subjunctive │ │ ├── will.rb │ │ └── shall.rb │ └── does.rb └── notes │ └── matrix.rdoc ├── .travis.yml ├── .locat ├── etc └── locat.rb ├── Assembly ├── MANIFEST ├── .index ├── NOTICE.md ├── README.md ├── HISTORY.md ├── .gemspec └── DEMO.rdoc /var/name: -------------------------------------------------------------------------------- 1 | ae -------------------------------------------------------------------------------- /var/title: -------------------------------------------------------------------------------- 1 | AE -------------------------------------------------------------------------------- /lib/ae.yml: -------------------------------------------------------------------------------- 1 | ../.index -------------------------------------------------------------------------------- /var/created: -------------------------------------------------------------------------------- 1 | 2008-08-17 -------------------------------------------------------------------------------- /var/version: -------------------------------------------------------------------------------- 1 | 1.8.2 2 | -------------------------------------------------------------------------------- /var/organization: -------------------------------------------------------------------------------- 1 | Rubyworks -------------------------------------------------------------------------------- /demo/applique/ae.rb: -------------------------------------------------------------------------------- 1 | require 'ae' 2 | -------------------------------------------------------------------------------- /var/summary: -------------------------------------------------------------------------------- 1 | Assertive Expressive 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | gemspec 3 | -------------------------------------------------------------------------------- /var/authors: -------------------------------------------------------------------------------- 1 | --- 2 | - Trans 3 | -------------------------------------------------------------------------------- /var/copyrights: -------------------------------------------------------------------------------- 1 | --- 2 | - (c) 2008 Rubyworks (BSD-2-Clause) 3 | 4 | -------------------------------------------------------------------------------- /var/repositories: -------------------------------------------------------------------------------- 1 | --- 2 | upstream: git://github.com/rubyworks/ae.git 3 | -------------------------------------------------------------------------------- /var/requirements: -------------------------------------------------------------------------------- 1 | --- 2 | - ansi 3 | - detroit (build) 4 | - qed (test) 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .reap/digest 2 | .yardoc 3 | doc 4 | log 5 | pkg 6 | tmp 7 | web 8 | -------------------------------------------------------------------------------- /try/cucumber/features/support/env.rb: -------------------------------------------------------------------------------- 1 | require 'ae' 2 | 3 | # World(AE::World) 4 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --title "AE API" 2 | --protected 3 | --private 4 | lib/ 5 | - 6 | [A-Z]*.* 7 | 8 | -------------------------------------------------------------------------------- /var/description: -------------------------------------------------------------------------------- 1 | Assertive Expressive is an assertions library specifically designed 2 | for reuse by other test frameworks. 3 | -------------------------------------------------------------------------------- /work/Rubyfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | import 'VERSION', :type=>'yaml' 4 | import 'PROFILE', :type=>'yaml' 5 | 6 | manifest 'MANIFEST' 7 | -------------------------------------------------------------------------------- /demo/02_assertion.md: -------------------------------------------------------------------------------- 1 | # Assertion Class 2 | 3 | The Assertion class is a subclass of Exception and is the error raised when 4 | and assertion fails. 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | script: "bundle exec qed" 3 | rvm: 4 | - 1.8.7 5 | - 1.9.2 6 | - 1.9.3 7 | - rbx-2.0 8 | - jruby 9 | - ree 10 | 11 | -------------------------------------------------------------------------------- /work/cucumber.yml: -------------------------------------------------------------------------------- 1 | default: --format profile demo/cucumber/features 2 | report : --format progress --format html --out=log/cucumber/report.html features 3 | -------------------------------------------------------------------------------- /lib/ae/core_ext.rb: -------------------------------------------------------------------------------- 1 | require 'ae/core_ext/exception' 2 | require 'ae/core_ext/helpers' 3 | 4 | # We need BasicObject for Assertor. 5 | unless defined?(BasicObject) 6 | require 'ae/basic_object' 7 | end 8 | 9 | -------------------------------------------------------------------------------- /lib/ae/adapter.rb: -------------------------------------------------------------------------------- 1 | if defined?(::MiniTest) 2 | require 'ae/adapters/minitest' 3 | elsif defined?(::Test::Unit) 4 | require 'ae/adapters/testunit' 5 | elsif defined?(::RSpec) 6 | require 'ae/adapters/rspec' 7 | end 8 | 9 | -------------------------------------------------------------------------------- /lib/ae/adapters/rspec.rb: -------------------------------------------------------------------------------- 1 | require 'ae' 2 | 3 | AE.assertion_error = ::RSpec::Expectations::ExpectationNotMetError 4 | 5 | # TODO: Teach RSpec the expanded concept of assertions, as Exception 6 | # classes that respond to `#assertion?` in the affirmative. 7 | 8 | -------------------------------------------------------------------------------- /var/resources: -------------------------------------------------------------------------------- 1 | --- 2 | home: http://rubyworks.github.com/ae 3 | code: http://github.com/rubyworks/ae 4 | docs: http://rubydoc.info/gems/ae 5 | wiki: http://wiki.github.com/rubyworks/ae 6 | bugs: http://github.com/rubyworks/ae/issues 7 | mail: http://groups.google.com/group/rubyworks-mailinglist 8 | -------------------------------------------------------------------------------- /try/cucumber/features/cucumber.feature: -------------------------------------------------------------------------------- 1 | Feature: Cucumber SUpport 2 | In order to please people who like Cucumber 3 | As an AE user 4 | I want to be able to use assert in my step definitions 5 | 6 | Scenario: assert 7 | Given x = 5 8 | And y = 5 9 | Then I can assert that x.assert == y 10 | 11 | -------------------------------------------------------------------------------- /try/rspec/example.rb: -------------------------------------------------------------------------------- 1 | require 'ae/adapters/rspec' 2 | 3 | describe "AE RSpec Support" do 4 | 5 | it "handles passing assert" do 6 | x = 5 7 | y = 5 8 | x.assert == y 9 | end 10 | 11 | it "handles failing assert" do 12 | x = 5 13 | y = 6 14 | x.assert == y 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /work/consider/08_assay.rdoc: -------------------------------------------------------------------------------- 1 | = Assay Integration 2 | 3 | A.E. can integrate with Assay. 4 | 5 | require 'assay' 6 | 7 | Now assertion errors will be more specific. 8 | 9 | expect ::Assay::EqualityFailure do 10 | "abcdefghijklmnopqrstuvwxyz".assert == "abcdefghijklmnopqrstuvw" 11 | end 12 | 13 | QED. 14 | -------------------------------------------------------------------------------- /try/cucumber/features/step_definitions/cucumber_steps.rb: -------------------------------------------------------------------------------- 1 | 2 | Given /^(\w+) = (\w+)$/ do |var, value| 3 | instance_variable_set("@#{var}", value) 4 | end 5 | 6 | Then /^I can assert that (\w+).assert == (\w+)$/ do |var_a, var_b| 7 | a = instance_variable_get("@#{var_a}") 8 | b = instance_variable_get("@#{var_b}") 9 | a.assert == b 10 | end 11 | 12 | -------------------------------------------------------------------------------- /lib/ae/ansi.rb: -------------------------------------------------------------------------------- 1 | require 'ansi/diff' 2 | 3 | module AE 4 | 5 | # Default ANSI mode is "on". 6 | @ansi = true 7 | 8 | # ANSI mode. 9 | # 10 | # @return [Boolean] ANSI mode. 11 | def self.ansi? 12 | @ansi 13 | end 14 | 15 | # To turn of ANSI colorized error messages off, set 16 | # ansi to +false+ in your test helper. 17 | # 18 | # @example 19 | # AE.ansi = false 20 | # 21 | def self.ansi=(boolean) 22 | @ansi = boolean 23 | end 24 | 25 | end 26 | 27 | -------------------------------------------------------------------------------- /lib/ae/core_ext/exception.rb: -------------------------------------------------------------------------------- 1 | class Exception 2 | # Is this exception the result of an assertion? 3 | def assertion? 4 | @assertion || false 5 | end 6 | 7 | # Set +true+/+false+ if the this exception is 8 | # an assertion. 9 | def set_assertion(boolean) 10 | @assertion = !!boolean 11 | end 12 | 13 | # 14 | def negative? 15 | @negative || false 16 | end 17 | 18 | # 19 | def set_negative(boolean) 20 | @negative = !!boolean 21 | end 22 | end 23 | 24 | -------------------------------------------------------------------------------- /lib/ae/version.rb: -------------------------------------------------------------------------------- 1 | module AE 2 | # Access project metadata. 3 | # 4 | # @return [Hash] 5 | def self.metadata 6 | @metadata ||= ( 7 | require 'yaml' 8 | YAML.load(File.new(File.dirname(__FILE__) + '/../ae.yml')) 9 | ) 10 | end 11 | 12 | # 13 | def self.const_missing(name) 14 | key = name.to_s.downcase 15 | metadata[key] || super(name) 16 | end 17 | 18 | # Becuase Ruby 1.8~ gets in the way :( 19 | VERSION = metadata['version'] 20 | end 21 | 22 | -------------------------------------------------------------------------------- /.locat: -------------------------------------------------------------------------------- 1 | 2 | match 'lib/**.rb' do |file, line| 3 | case line 4 | when /^\s+#/ 5 | 'Comment' 6 | when /^\s*$/ 7 | 'Blank' 8 | else 9 | 'Code' 10 | end 11 | end 12 | 13 | match 'test/**.rb' do |file, line| 14 | unless line.strip.empty? 15 | 'Test' 16 | end 17 | end 18 | 19 | match 'qed/**.rdoc' do |file, line| 20 | unless line.strip.empty? 21 | 'Test' 22 | end 23 | end 24 | 25 | match 'demo/**.rdoc' do |file, line| 26 | unless line.strip.empty? 27 | 'Test' 28 | end 29 | end 30 | 31 | -------------------------------------------------------------------------------- /etc/locat.rb: -------------------------------------------------------------------------------- 1 | 2 | match 'lib/**.rb' do |file, line| 3 | case line 4 | when /^\s+#/ 5 | 'Comment' 6 | when /^\s*$/ 7 | 'Blank' 8 | else 9 | 'Code' 10 | end 11 | end 12 | 13 | match 'test/**.rb' do |file, line| 14 | unless line.strip.empty? 15 | 'Test' 16 | end 17 | end 18 | 19 | match 'qed/**.rdoc' do |file, line| 20 | unless line.strip.empty? 21 | 'Test' 22 | end 23 | end 24 | 25 | match 'demo/**.rdoc' do |file, line| 26 | unless line.strip.empty? 27 | 'Test' 28 | end 29 | end 30 | 31 | -------------------------------------------------------------------------------- /Assembly: -------------------------------------------------------------------------------- 1 | --- 2 | github: 3 | gh_pages: web 4 | 5 | gem: 6 | active: true 7 | 8 | yard: 9 | yardopts: true 10 | priority: 2 11 | 12 | qed: 13 | files: demo/ 14 | 15 | qedoc: 16 | files : demo/ 17 | output: DEMO.rdoc 18 | title : AE Demonstrations 19 | 20 | email: 21 | service : Email 22 | mailto : 23 | - ruby-talk@ruby-lang.org 24 | - rubyworks-mailinglist@googlegroups.com 25 | 26 | dnote: 27 | labels: ~ 28 | output: log/notes.html 29 | 30 | vclog: 31 | output: 32 | - log/history.html 33 | - log/changes.html 34 | 35 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | #!mast .ruby .yardopts bin lib man qed spec test [A-Z]*.* 2 | .yardopts 3 | lib/ae/adapter.rb 4 | lib/ae/adapters/minitest.rb 5 | lib/ae/adapters/rspec.rb 6 | lib/ae/adapters/testunit.rb 7 | lib/ae/ansi.rb 8 | lib/ae/assert.rb 9 | lib/ae/assertion.rb 10 | lib/ae/assertor.rb 11 | lib/ae/basic_object.rb 12 | lib/ae/check.rb 13 | lib/ae/core_ext/exception.rb 14 | lib/ae/core_ext/helpers.rb 15 | lib/ae/core_ext.rb 16 | lib/ae/expect.rb 17 | lib/ae/legacy.rb 18 | lib/ae/must.rb 19 | lib/ae/pry.rb 20 | lib/ae/should.rb 21 | lib/ae/subjunctive.rb 22 | lib/ae/version.rb 23 | lib/ae.rb 24 | lib/ae.yml 25 | DEMO.rdoc 26 | HISTORY.md 27 | README.md 28 | NOTICE.md 29 | -------------------------------------------------------------------------------- /try/minitest/example.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'ae/adapters/minitest' 3 | 4 | class MiniTestSupport < MiniTest::Unit::TestCase 5 | 6 | def test_assert_pass 7 | x = 5 8 | y = 5 9 | x.assert == y 10 | end 11 | 12 | def test_assert_fail 13 | x = 5 14 | y = 6 15 | x.assert == y 16 | end 17 | 18 | def test_assert_pass_original 19 | x = 5 20 | y = 5 21 | assert_equal(x, y) 22 | end 23 | 24 | def test_assert_fail_original 25 | x = 5 26 | y = 6 27 | assert_equal(x, y) 28 | end 29 | 30 | def test_exception 31 | raise "This should raise an exception." 32 | end 33 | 34 | end 35 | -------------------------------------------------------------------------------- /try/testunit/example.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | #require 'assay' 3 | require 'ae/adapters/testunit' 4 | 5 | class TestUnitSupport < Test::Unit::TestCase 6 | 7 | def test_assert_pass 8 | x = 5 9 | y = 5 10 | x.assert == y 11 | end 12 | 13 | def test_assert_fail 14 | x = "12345678901234567890" 15 | y = "123456789X1234567890" 16 | x.assert == y 17 | end 18 | 19 | def test_assert_fail_original 20 | x = 5 21 | y = 6 22 | assert_equal(x, y) 23 | end 24 | 25 | def test_assert_pass_original 26 | x = 5 27 | y = 5 28 | assert_equal(x, y) 29 | end 30 | 31 | def test_exception 32 | raise 33 | end 34 | 35 | end 36 | -------------------------------------------------------------------------------- /lib/ae.rb: -------------------------------------------------------------------------------- 1 | module AE 2 | 3 | # TODO: Should we really be reseting a constant for ::Assertion? 4 | # How about using a variable instead? 5 | 6 | # Set Assertion class. This is a convenience method 7 | # for framework adapters, used to set the exception class 8 | # that a framework uses to raise an assertion error. 9 | # 10 | # @param [Class] exception_class 11 | # The Exception subclass used to raise assertions. 12 | # 13 | def self.assertion_error=(exception_class) 14 | verbose, $VERBOSE = $VERBOSE, nil 15 | Object.const_set(:Assertion, exception_class) 16 | $VERBOSE = verbose 17 | end 18 | 19 | end 20 | 21 | require 'ae/version' 22 | require 'ae/assert' 23 | require 'ae/expect' 24 | 25 | class ::Object 26 | include AE::Assert 27 | include AE::Expect 28 | end 29 | 30 | -------------------------------------------------------------------------------- /work/consider/dot.rb: -------------------------------------------------------------------------------- 1 | # Expiremental Nomenclature. 2 | 3 | # 4 | class TrueClass 5 | # Assert true. 6 | # 7 | # (x == y).true! 8 | # 9 | def true!(msg=nil) 10 | true 11 | end 12 | # Assert false. 13 | # 14 | # (x == y).false! 15 | # 16 | def false!(err="not false") 17 | if Exception === err 18 | fail err 19 | else 20 | fail Assertion.new(err.to_s, :backtrace=>caller) 21 | end 22 | end 23 | end 24 | 25 | class FalseClass 26 | # Assert true. 27 | # 28 | # (x == y).true! 29 | # 30 | def true!(err="not true") 31 | if Exception === err 32 | fail err 33 | else 34 | fail Assertion.new(err.to_s, :backtrace=>caller) 35 | end 36 | end 37 | # Assert false. 38 | # 39 | # (x == y).false! 40 | # 41 | def false!(msg=nil) 42 | true 43 | end 44 | end 45 | 46 | -------------------------------------------------------------------------------- /work/.rubyspec0: -------------------------------------------------------------------------------- 1 | --- 2 | name : ae 3 | date : 2010-11-05 4 | version : 1.6.1 5 | 6 | requires: 7 | - syckle (build) 8 | - qed (test) 9 | 10 | title : AE 11 | summary: Assertive Expressive 12 | suite : rubyworks 13 | contact: trans 14 | created: 2008-08-17 15 | authors: Thomas Sawyer 16 | license: Apache 2.0 17 | 18 | description: 19 | Assertive Expressive is an assertions library specifically 20 | designed for reuse by other test frameworks. 21 | 22 | resources: 23 | home: http://proutils.github.com/ae 24 | code: http://github.com/proutils/ae 25 | docs: http://wiki.github.com/proutils/ae/docs/qed 26 | wiki: http://wiki.github.com/proutils/ae 27 | bugs: http://github.com/proutils/ae/issues 28 | mail: http://groups.google.com/group/rubyworks-mailinglist 29 | repo: git://github.com/proutils/ae.git 30 | 31 | copyright: Copyright (c) 2008 Thomas Sawyer 32 | -------------------------------------------------------------------------------- /demo/06_counts.md: -------------------------------------------------------------------------------- 1 | # Assertion Counts 2 | 3 | AE tracks the number of assertions made and the number that failed to pass. 4 | We can reset the count using the +recount+ class method. 5 | 6 | old_counts = AE::Assertor.recount 7 | 8 | For example if we have one assertion pass and another fail. 9 | 10 | assert(true) 11 | 12 | expect Assertion do 13 | assert(false) 14 | end 15 | 16 | We will see that AE counted three assertions and one failure. 17 | 18 | counts = AE::Assertor.counts.dup 19 | 20 | counts[:total].assert == 3 21 | counts[:pass].assert == 2 22 | counts[:fail].assert == 1 23 | 24 | The #expect call is an assertion too, which is why the total count is 3 25 | rather than 2. 26 | 27 | Now that we are done checking counts we will restore them so that 28 | any other demos being run with this will tally correctly. 29 | 30 | AE::Assertor.recount(old_counts) 31 | AE::Assertor.counts[:total] += 3 32 | AE::Assertor.counts[:pass] += 3 33 | 34 | -------------------------------------------------------------------------------- /work/consider/counts.rb: -------------------------------------------------------------------------------- 1 | feature "Assertion Counts" do 2 | 3 | #"AE tracks the number of assertions made and the number that", 4 | #"failed to pass." do 5 | 6 | require 'ae' 7 | 8 | scenario "count assertions" do 9 | before do 10 | ::Assertion.recount 11 | end 12 | 13 | to do |pass| 14 | begin 15 | assert(pass) 16 | rescue ::Assertion 17 | end 18 | ::Assertion.count 19 | end 20 | 21 | ok true => 1 22 | ok true => 2 23 | ok true => 3 24 | ok false => 4 25 | ok false => 5 26 | ok false => 6 27 | end 28 | 29 | 30 | scenario "count failures" do 31 | before do 32 | ::Assertion.recount 33 | end 34 | 35 | to do |pass| 36 | begin 37 | assert(pass) 38 | rescue ::Assertion 39 | end 40 | ::Assertion.fails 41 | end 42 | 43 | ok true => 0 44 | ok true => 0 45 | ok true => 0 46 | ok false => 1 47 | ok false => 2 48 | ok false => 3 49 | end 50 | 51 | end 52 | 53 | -------------------------------------------------------------------------------- /lib/ae/pry.rb: -------------------------------------------------------------------------------- 1 | require 'ae/basic_object' 2 | 3 | module Kernel 4 | 5 | # TODO: Is th cache really neccessry? 6 | 7 | # 8 | $PRY_TABLE = {} 9 | 10 | # Pry allows you to test private and protected methods 11 | # thru a public-only interface. 12 | # 13 | # Generally one should avoid testing private and protected 14 | # methods directly, instead relying on tests of public methods to 15 | # indirectly test them, because private and protected methods are 16 | # considered implementation details. But sometimes it is necessary 17 | # to test them directly, or if you wish to achieve *absolute 18 | # coverage*, say in a mission critical system. 19 | # 20 | # @return [Pry] pry functor 21 | def pry 22 | $PRY_TABLE[self] ||= Pry.new do |op, *a, &b| 23 | __send__(op, *a, &b) 24 | end 25 | end 26 | 27 | # Pry Functor 28 | class Pry < AE::BasicObject 29 | #instance_methods.each{ |m| private m unless m.to_s =~ /^__/ } 30 | def initialize(&function) 31 | @function = function 32 | end 33 | def method_missing(op, *a, &b) 34 | @function.call(op, *a, &b) 35 | end 36 | end 37 | 38 | end 39 | 40 | -------------------------------------------------------------------------------- /demo/07_matchers.md: -------------------------------------------------------------------------------- 1 | # Matchers 2 | 3 | Matchers are simply Procs or objects with the proper interface that can be 4 | passed to #assert or #refute (or other Assertor) as an ecapsulated test. 5 | 6 | ## Proc or #to_proc 7 | 8 | Passing a Proc object or an object that responds to `:to_proc`, will use it 9 | as if it were a block of the method. This allows for a simple way to quickly 10 | create reusable assertions. 11 | 12 | palindrome = lambda{ |word| word == word.reverse } 13 | 14 | "abracarba".assert palindrome 15 | 16 | ## #matches? 17 | 18 | Additionally if an object responds to #matches? then the receiver 19 | will be passed to this method to determine if the assertion passes. 20 | 21 | palindrome = Object.new 22 | 23 | def palindrome.matches?(word) 24 | word == word.reverse 25 | end 26 | 27 | "abracarba".assert palindrome 28 | 29 | ## RSpec, Shoulda and other 3rd-Party Matchers 30 | 31 | With tha addition of #matches?, AE supports the same interface for matchers 32 | as RSpec. Any matcher library designed for use with RSpec should therefore 33 | be usable with AE as well. This includes RSpecs own matchers and Shoulda's 34 | excellent Rails matchers. 35 | -------------------------------------------------------------------------------- /work/trash/verify.rb: -------------------------------------------------------------------------------- 1 | require 'ae/assertion' 2 | 3 | module AE 4 | 5 | # = Verify 6 | # 7 | # "You will find it a very good practice always to verify 8 | # your references sir." 9 | # --Martin Routh 10 | # 11 | module Verify 12 | 13 | # Same as #expect but only as a functor. 14 | # 15 | # 4.verify == 3 #=> Expectation Error 16 | # 17 | def verify(msg=nil, &block) 18 | return Assertor.new(self, :message=>msg, :backtrace=>caller) if !block 19 | raise Assertion.new(msg, :backtrace=>caller) if !block.call 20 | end 21 | 22 | # Designate a negated expectation via a *functor*. 23 | # Read this as "must not". 24 | # 25 | # 4.verify! == 4 #=> Expectation Error 26 | # 27 | def verify!(msg=nil, &block) 28 | return Assertor.new(self, :message=>msg, :negate=>true, :backtrace=>caller) if !block 29 | raise Assertion.new(msg, :backtrace=>caller) if block.call 30 | end 31 | 32 | # See #verify! method. 33 | # 34 | alias_method :refute , :verify! 35 | 36 | end 37 | 38 | end 39 | 40 | class ::Object #:nodoc: 41 | include AE::Verify 42 | end 43 | 44 | -------------------------------------------------------------------------------- /work/trash/subjunctive/will.rb: -------------------------------------------------------------------------------- 1 | require 'ae/assertion' 2 | 3 | module AE 4 | 5 | # = Will 6 | # 7 | # "After an access cover has been secured by 16 hold-down screws, 8 | # it will be discovered that the gasket has been omitted." 9 | # -- De la Lastra's Corollary 10 | # 11 | module Will 12 | 13 | # The #will methid is the same as #should. 14 | # 15 | # 4.will == 3 #=> Expectation Error 16 | # 17 | def will(msg=nil, &block) 18 | return Assertor.new(self, :message=>msg, :backtrace=>caller) if !block 19 | raise Assertion.new(msg, :backtrace=>caller) if !block.call 20 | end 21 | 22 | # Designate a negated expectation via a *functor*. 23 | # Read this as "will not". 24 | # 25 | # 4.will! == 4 #=> Expectation Error 26 | # 27 | def will!(msg=nil, &block) 28 | return Assertor.new(self, :message=>msg, :negate=>true, :backtrace=>caller) if !block 29 | raise Assertion.new(msg, :backtrace=>caller) if block.call 30 | end 31 | 32 | # Alias for #will! method. 33 | alias_method :wont, :will! 34 | 35 | end 36 | 37 | end 38 | 39 | class ::Object #:nodoc: 40 | include AE::Will 41 | end 42 | -------------------------------------------------------------------------------- /work/consider/detest.rb: -------------------------------------------------------------------------------- 1 | require 'ae/assertion' 2 | 3 | class AE 4 | # Given that x is "tom" then we can assert it 5 | # is using an asseretion pipe. 6 | # 7 | # x = "tom" 8 | # 9 | # T x == "tom" 10 | # 11 | # We can assert the opposite using F. 12 | # 13 | # F x == "tom" 14 | # 15 | # These can be used at any point of return. 16 | # 17 | # T case x 18 | # when 'tom' then true 19 | # else false 20 | # end 21 | # 22 | module Detest 23 | 24 | # Test for true. 25 | # 26 | # T 1 == 1 27 | # 28 | def T(x=nil, &b) 29 | Assertion.test(x || b.call, :backtrace=>caller) 30 | end 31 | 32 | # Test for not. 33 | # 34 | # F 1 == 2 35 | # 36 | def F(x=nil, &b) 37 | Assertion.test(!(x || b.call), :backtrace=>caller) 38 | end 39 | 40 | # Test for nil?. 41 | # 42 | # N nil 43 | # 44 | def N(x=nil,&b) 45 | Assertion.test(nil == (x || b.call), :backtrace=>caller) 46 | end 47 | 48 | # Expect and error. 49 | # 50 | # E { raise } 51 | # 52 | # Unless #T, #F and #N, the #E method only supports block notation. 53 | def E(&b) 54 | expect(Exception, &b) 55 | end 56 | 57 | # Catch a symbol. 58 | #def C 59 | #end 60 | end 61 | 62 | module World 63 | include AE::Detest 64 | end 65 | 66 | end 67 | 68 | 69 | -------------------------------------------------------------------------------- /.index: -------------------------------------------------------------------------------- 1 | --- 2 | revision: 2013 3 | type: ruby 4 | sources: 5 | - var 6 | authors: 7 | - name: Trans 8 | email: transfire@gmail.com 9 | organizations: [] 10 | requirements: 11 | - name: ansi 12 | - groups: 13 | - build 14 | development: true 15 | name: detroit 16 | - groups: 17 | - test 18 | development: true 19 | name: qed 20 | conflicts: [] 21 | alternatives: [] 22 | resources: 23 | - type: home 24 | uri: http://rubyworks.github.com/ae 25 | label: Homepage 26 | - type: code 27 | uri: http://github.com/rubyworks/ae 28 | label: Source Code 29 | - type: docs 30 | uri: http://rubydoc.info/gems/ae 31 | label: Documentation 32 | - type: wiki 33 | uri: http://wiki.github.com/rubyworks/ae 34 | label: User Guide 35 | - type: bugs 36 | uri: http://github.com/rubyworks/ae/issues 37 | label: Issue Tracker 38 | - type: mail 39 | uri: http://groups.google.com/group/rubyworks-mailinglist 40 | label: Mailing List 41 | repositories: 42 | - name: upstream 43 | scm: git 44 | uri: git://github.com/rubyworks/ae.git 45 | categories: [] 46 | copyrights: 47 | - holder: Rubyworks 48 | year: '2008' 49 | license: BSD-2-Clause 50 | customs: [] 51 | paths: 52 | lib: 53 | - lib 54 | created: '2008-08-17' 55 | summary: Assertive Expressive 56 | title: AE 57 | version: 1.8.2 58 | name: ae 59 | description: ! "Assertive Expressive is an assertions library specifically designed 60 | \nfor reuse by other test frameworks." 61 | date: '2013-03-09' 62 | -------------------------------------------------------------------------------- /work/trash/does.rb: -------------------------------------------------------------------------------- 1 | require 'ae/subjunctive/assertor' 2 | 3 | module AE 4 | module Subjunctive 5 | 6 | # = IsDoes 7 | # 8 | # An **optional** library. The provide #is and #does as Object methods. 9 | # 10 | module IsDoes 11 | # Designate a expectation via a *functor*. 12 | # 13 | # 4.is == 3 #=> Assertion Error 14 | # 15 | # 4.is 4 16 | # 17 | # 4.is do 18 | # 4 19 | # end 20 | # 21 | def is(*args, &block) 22 | Subjunctive::Assertor.new(self, :backtrace=>caller).is(*args, &block) 23 | end 24 | 25 | # Designate a expectation via a *functor* called #does. 26 | # 27 | # 4.does == 5 #=> Assertion Error 28 | # 29 | # 4.does do 30 | # self == 4 31 | # end 32 | # 33 | alias_method :is, :does 34 | 35 | # Designate a negated expectation via a *functor*. 36 | # Read this as "is not". 37 | # 38 | # 4.is! == 4 #=> Assertion Error 39 | # 40 | def is!(*args, &block) 41 | Subjunctive::Assertor.new(self, :backtrace=>caller).not(*args, &block) 42 | end 43 | 44 | # Designate a negated expectation via a *functor*. 45 | # Read this as "does not". 46 | # 47 | # 4.does! == 4 #=> Assertion Error 48 | # 49 | alias_method :is!, :does! 50 | end 51 | 52 | end 53 | end 54 | 55 | class ::Object #:nodoc: 56 | include AE::Subjunctive::IsDoes 57 | end 58 | 59 | # Copyright (c) 2008,2009 Thomas Sawyer 60 | -------------------------------------------------------------------------------- /lib/ae/adapters/minitest.rb: -------------------------------------------------------------------------------- 1 | require 'ae' 2 | 3 | AE.assertion_error = ::MiniTest::Assertion 4 | 5 | module MiniTest #:nodoc: 6 | class Unit #:nodoc: 7 | 8 | # MiniTest tracks assertion counts internally in it's Unit class via the 9 | # +assertion_count+ attribute. To work with AE we need add in AE's assertion 10 | # total by overriding the +assertion_count+ method. 11 | # 12 | # @return [Integer] Number of assertions made. 13 | def assertion_count 14 | @assertion_count + AE::Assertor.counts[:total] 15 | end 16 | 17 | # To teach MiniTest to recognize AE's expanded concept of assertions 18 | # we add in an extra capture clause to it's #puke method. 19 | # 20 | # @return [String] Status code is `S`, `F`, or `E`. 21 | def puke k, m, e 22 | case e 23 | when MiniTest::Skip 24 | @skips += 1 25 | return "S" unless @verbose 26 | e = "Skipped:\n#{m}(#{k}) [#{location e}]:\n#{e.message}\n" 27 | when MiniTest::Assertion 28 | @failures += 1 29 | e = "Failure:\n#{m}(#{k}) [#{location e}]:\n#{e.message}\n" 30 | else 31 | if e.respond_to?(:assertion?) && e.assertion? 32 | @failures += 1 33 | e = "Failure:\n#{m}(#{c}) [#{location e}]:\n#{e.message}\n" 34 | else 35 | @errors += 1 36 | b = MiniTest::filter_backtrace(e.backtrace).join "\n " 37 | e = "Error:\n#{m}(#{k}):\n#{e.class}: #{e.message}\n #{b}\n" 38 | end 39 | end 40 | @report << e 41 | e[0, 1] 42 | end 43 | 44 | end 45 | end 46 | 47 | -------------------------------------------------------------------------------- /work/trash/subjunctive/shall.rb: -------------------------------------------------------------------------------- 1 | require 'ae/assertion' 2 | 3 | module AE 4 | 5 | # = Shall 6 | # 7 | # "If a man will begin with certainties, he shall end in doubts; but if 8 | # he will be content to begin with doubts he shall end in certainties." 9 | # --Sir Francis Bacon (1561 - 1626) 10 | # 11 | module Shall 12 | # The #shall method can act as a functor, like #assert or #expect, 13 | # or as a described verification block. 14 | # 15 | # 4.shall == 3 #=> Expectation Error 16 | # 17 | # 4.shall "be three" do 18 | # 3 19 | # end #=> Expectation Error 20 | # 21 | def shall(msg=nil, &block) 22 | return Assertor.new(self, :message=>msg, :backtrace=>caller) if !block 23 | raise Assertion.new(msg, :backtrace=>caller) if !block.call 24 | end 25 | 26 | # Designate a negated expectation via a *functor*. 27 | # Read this as "shall not". 28 | # 29 | # 4.shall! == 4 #=> Expectation Error 30 | # 31 | # 4.shall! "be three" do 32 | # 4 33 | # end #=> Expectation Error 34 | # 35 | def shall!(msg=nil, &block) 36 | return Assertor.new(self, :message=>msg, :negate=>true, :backtrace=>caller) if !block 37 | raise Assertion.new(msg, :backtrace=>caller) if block.call 38 | end 39 | 40 | # Alias for #shall! method. 41 | # 42 | alias_method :shall_not, :shall! 43 | alias_method :shant , :shall! 44 | end 45 | 46 | end 47 | 48 | class ::Object #:nodoc: 49 | include AE::Shall 50 | end 51 | -------------------------------------------------------------------------------- /lib/ae/should.rb: -------------------------------------------------------------------------------- 1 | require 'ae/subjunctive' 2 | 3 | module AE 4 | 5 | # Should 6 | # 7 | # "Always and never are two words you should always 8 | # remember never to use." 9 | # --Wendell Johnson 10 | # 11 | # @note THIS IS AN OPTIONAL LIBRARY. 12 | module Should 13 | # Make an assertion in subjunctive tense. 14 | # 15 | # 4.should == 3 #=> Assertion Error 16 | # 17 | # 4.should do 18 | # self == 4 19 | # end 20 | # 21 | # @return [Assertor] Assertion functor. 22 | def should(*args, &block) 23 | Assertor.new(self, :backtrace=>caller).be(*args, &block) 24 | end 25 | 26 | # Same as 'object.should == other'. 27 | # 28 | # @return [Assertor] Assertion functor. 29 | def should=(cmp) 30 | Assertor.new(self, :backtrace=>caller).assert == cmp 31 | end 32 | 33 | # Designate a negated expectation via a *functor*. 34 | # Read this as "should not". 35 | # 36 | # 4.should! = 4 #=> Assertion Error 37 | # 38 | # @return [Assertor] Assertion functor. 39 | def should!(*args, &block) 40 | Assertor.new(self, :backtrace=>caller).not.be(*args, &block) 41 | end 42 | 43 | # NOTE: It would be nice if their were a single term that 44 | # meant the opposite of should, rather than a two word compound. 45 | 46 | # Alias for #should! method. 47 | alias_method :should_not, :should! 48 | 49 | # Alias for #should! method. 50 | alias_method :shouldnt, :should! 51 | end 52 | 53 | end 54 | 55 | class ::Object #:nodoc: 56 | include AE::Should 57 | end 58 | 59 | # Copyright (c) 2008 Thomas Sawyer, Rubyworks 60 | -------------------------------------------------------------------------------- /demo/08_check.md: -------------------------------------------------------------------------------- 1 | ## Check Ok/No 2 | 3 | The Check library is an optional library that can be used 4 | to conveniently speed-up construction of reptitive assertions. 5 | 6 | To use it, first require the library, then include the mixin 7 | into the namespace in which you will utilize it. 8 | 9 | require 'ae/check' 10 | 11 | include AE::Check 12 | 13 | Now we can define ok/no check procedures. A one-off procedure is 14 | defined with a block only. 15 | 16 | check do |x, y| 17 | x == y 18 | end 19 | 20 | ok 1,1 21 | no 1,2 22 | 23 | To define reusable check procedures, give the procedure a name. 24 | 25 | check :palindrome do |x| 26 | x.reverse == x 27 | end 28 | 29 | This will also cause the current check method to be set. 30 | Later in the code, the check procedure can be reset to this 31 | by just passing the name. 32 | 33 | check :palindrome 34 | 35 | ok 'abracarba' 36 | no 'foolishness' 37 | 38 | The Check mixin comes preloaded with a few standard checks. 39 | By default the `:equality` procedure is used. 40 | 41 | check :equality 42 | 43 | ok 1=>1.0 44 | 45 | Notice the use of the hash argument here. This is a useful construct for 46 | many check procedures becuase it it akin to the `#=>` pattern we often 47 | see in code examples and it also allows for multiple assertions in one call. 48 | For instance, in the case of `:equality`, multiple entries convey a 49 | meaning of logical-or. 50 | 51 | ok 1=>2, 1=>1 52 | 53 | This would pass becuase the second assertion of equality is true. 54 | 55 | Another built in check is `:case_equality` which uses `#===` instead of `#==` 56 | to make the comparison. 57 | 58 | check :case_equality 59 | 60 | ok 1=>Integer 61 | 62 | -------------------------------------------------------------------------------- /lib/ae/assertion.rb: -------------------------------------------------------------------------------- 1 | require 'ae/core_ext' 2 | 3 | module AE 4 | 5 | # The Assertion class is simply a subclass of Exception that is used 6 | # by AE as the default error raised when an assertion fails. 7 | # 8 | # "The reserve of modern assertions is sometimes pushed to extremes, 9 | # in which the fear of being contradicted leads the writer to strip 10 | # himself of almost all sense and meaning." 11 | # -- Sir Winston Churchill (1874 - 1965) 12 | # 13 | # 14 | class Assertion < Exception 15 | 16 | # @deprecated 17 | # This will be removed in favor of `AE::Assertor.counts`. 18 | def self.counts 19 | AE::Assertor.counts 20 | end 21 | 22 | # New assertion (failure). 23 | # 24 | # @param message [String] the failure message 25 | # @param options [Hash] options such as :backtrace 26 | # 27 | def initialize(message=nil, options={}) 28 | super(message) 29 | backtrace = options[:backtrace] 30 | set_backtrace(backtrace) if backtrace 31 | set_assertion(true) 32 | end 33 | 34 | # Technically any object that affirmatively responds to #assertion? 35 | # can be taken to be an Assertion. This makes it easier for various 36 | # libraries to work together without having to depend upon a common 37 | # Assertion base class. 38 | def assertion? 39 | true 40 | end 41 | 42 | # Parents error message prefixed with "(assertion)". 43 | # 44 | # @return [String] error message 45 | def to_s 46 | '(assertion) ' + super 47 | end 48 | 49 | end 50 | 51 | end 52 | 53 | # Set top-level Assertion to AE::Assertion if not already present. 54 | Assertion = AE::Assertion unless defined?(Assertion) 55 | -------------------------------------------------------------------------------- /lib/ae/must.rb: -------------------------------------------------------------------------------- 1 | module AE 2 | require 'ae/subjunctive' 3 | 4 | # Must 5 | # 6 | # "It is not enough to succeed. Others must fail." 7 | # --Gore Vidal (1925 - ) 8 | # 9 | # @note THIS IS AN OPTIONAL LIBRARY. 10 | module Must 11 | # The #must method is functionaly the same as #should. 12 | # 13 | # @example 14 | # 4.must == 3 #=> Assertion Error 15 | # 16 | # @example 17 | # 4.must do 18 | # self == 4 19 | # end 20 | # 21 | # @return [Assertor] Assertion functor. 22 | def must(*args, &block) 23 | Assertor.new(self, :backtrace=>caller).be(*args, &block) 24 | end 25 | 26 | # Same as 'object.must == other'. 27 | # 28 | # @return [Assertor] Assertion functor. 29 | def must=(cmp) 30 | Assertor.new(self, :backtrace=>caller) == cmp 31 | end 32 | 33 | # Designate a negated expectation via a *functor*. 34 | # Read this as "must not". 35 | # 36 | # @example 37 | # 4.must! == 4 #=> Assertion Error 38 | # 39 | # @return [Assertor] Assertion functor. 40 | def must!(*args, &block) 41 | Assertor.new(self, :backtrace=>caller).not.be(*args, &block) 42 | end 43 | 44 | # TODO: Are these negation methods needed now, since Ruby 1.9 allows for 45 | # redefining `!` as a method? 46 | 47 | # Perhaps not literally the counter-term to *must* (rather *will*), 48 | # but close enough for our purposes, and conveys the appropriate 49 | # semantics. 50 | alias_method :wont, :must! 51 | 52 | # Alias for #must! method. 53 | alias_method :must_not, :must! 54 | 55 | # Alias for #must! method. 56 | alias_method :mustnt, :must! 57 | end 58 | 59 | end 60 | 61 | class ::Object #:nodoc: 62 | include AE::Must 63 | end 64 | 65 | # Copyright (c) 2008 Thomas Sawyer 66 | -------------------------------------------------------------------------------- /lib/ae/expect.rb: -------------------------------------------------------------------------------- 1 | module AE 2 | require 'ae/assertor' 3 | 4 | # = Expect 5 | # 6 | # "When love and skill work together, expect a masterpiece." 7 | # --John Ruskin (1819 - 1900) 8 | # 9 | module Expect 10 | 11 | # The #expect method is a convenient tool for defining 12 | # certain sets of expectations in your specifications. 13 | # 14 | # Expect is used to expect a result from a block of code. 15 | # If the argument to expect is a subclass of Exception 16 | # or instance thereof, then the block is monitored for 17 | # the raising of such an exception. 18 | # 19 | # expect StandardError do 20 | # raise ArgumentError 21 | # end 22 | # 23 | # All other expectations are compared using case equality (#===). 24 | # This allows one to verify matching Regexp. 25 | # 26 | # expect /x/ do 27 | # "x" 28 | # end 29 | # 30 | # As well as checking that an object is an instance of a given Class. 31 | # 32 | # expect String do 33 | # "x" 34 | # end 35 | # 36 | # Like #assert it can be used to designate an expectation 37 | # via a *functor*. 38 | # 39 | # 4.expect == 3 40 | # 41 | def expect(*args, &block) 42 | Assertor.new(self, :backtrace=>caller).expect(*args, &block) 43 | end 44 | 45 | # Designate a negated expectation. Read this as "expect not". 46 | # 47 | # See #expect. 48 | # 49 | def expect!(*args, &block) 50 | Assertor.new(self, :backtrace=>caller).not.expect(*args, &block) 51 | end 52 | 53 | # Alias for #expect! method. 54 | alias_method :forbid, :expect! 55 | 56 | # Like #expect but uses the reciever as the object 57 | # of expectation. 58 | # 59 | # @example 60 | # /x/.expected do 61 | # "oooxooo" 62 | # end 63 | # 64 | def expected(*args, &block) 65 | expect(self, *args, &block) 66 | end 67 | 68 | end 69 | 70 | end 71 | 72 | # Copyright (c) 2008 Thomas Sawyer 73 | -------------------------------------------------------------------------------- /lib/ae/subjunctive.rb: -------------------------------------------------------------------------------- 1 | require 'ae/assertor' 2 | 3 | module AE 4 | 5 | # Subjunctive 6 | # 7 | # Mixin for Assertor that provides additional English-eque verbage 8 | # such as 'be' and 'an'. This makes it easier to create assertor 9 | # methods of subjunctive terms like 'should'. 10 | # 11 | # @note THIS IS AN OPTIONAL LIBRARY. 12 | module Subjunctive 13 | 14 | # Like #assert, except if an argument is provided and no block, 15 | # uses #equate? to compare the argument to the receiver. This 16 | # allows for statements of the form: 17 | # 18 | # 5.should.be Numeric 19 | # 20 | def be(*args, &block) 21 | return self if args.empty? && !block 22 | block = args.shift if !block && ::Proc === args.first 23 | if block 24 | pass = block.arity > 0 ? block.call(@delegate) : block.call #@delegate.instance_eval(&block) 25 | msg = args.shift || @message || block.inspect 26 | else 27 | pass = args.shift.equate?(@delegate) 28 | msg = args.shift 29 | end 30 | __assert__(pass, msg) 31 | end 32 | 33 | # Alias of #be. 34 | # 35 | # 5.assert.is Numeric 36 | # 37 | alias_method :is , :be 38 | alias_method :does, :be 39 | 40 | # The indefinite article is like #be, except that it compares a lone argument 41 | # with #case?, rather than #equate? 42 | # 43 | def a(*args, &block) 44 | return self if args.empty? && !block 45 | block = args.shift if !block && ::Proc === args.first 46 | if block 47 | pass = block.arity > 0 ? block.call(@delegate) : block.call #@delegate.instance_eval(&block) 48 | msg = args.shift || @message || block.inspect 49 | else 50 | pass = (args.shift === @delegate) # case equality 51 | msg = args.shift 52 | end 53 | __assert__(pass, msg) 54 | end 55 | 56 | alias_method :an, :a 57 | end 58 | 59 | end#module AE 60 | 61 | class AE::Assertor 62 | include ::AE::Subjunctive 63 | end 64 | 65 | # Copyright (c) 2008 Thomas Sawyer 66 | -------------------------------------------------------------------------------- /NOTICE.md: -------------------------------------------------------------------------------- 1 | # COPYRIGHT NOTICES 2 | 3 | ## AE 4 | 5 | Copyright:: (c) 2008 Thomas Sawyer 6 | License: BSD-2-Clause 7 | 8 | Copyright 2008 Thomas Sawyer. All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without modification, are 11 | permitted provided that the following conditions are met: 12 | 13 | 1. Redistributions of source code must retain the above copyright notice, this list of 14 | conditions and the following disclaimer. 15 | 16 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 17 | of conditions and the following disclaimer in the documentation and/or other materials 18 | provided with the distribution. 19 | 20 | THIS SOFTWARE IS PROVIDED BY Thomas Sawyer ``AS IS'' AND ANY EXPRESS OR IMPLIED 21 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 22 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Thomas Sawyer OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 28 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | The views and conclusions contained in the software and documentation are those of the 31 | authors and should not be interpreted as representing official policies, either expressed 32 | or implied, of Thoams Sawyer. 33 | 34 | 35 | ## BlankSlate 36 | 37 | Copyright:: (c) 2004,2006 Jim Weirich 38 | License:: Custom 39 | 40 | AE::BasicObject is based on Jim Weirich's BlankSlate class. 41 | 42 | Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org). 43 | All rights reserved. 44 | 45 | Permission is granted for use, copying, modification, distribution, 46 | and distribution of modified versions of this work as long as the 47 | above copyright notice is included. 48 | 49 | -------------------------------------------------------------------------------- /lib/ae/adapters/testunit.rb: -------------------------------------------------------------------------------- 1 | require 'ae' 2 | 3 | AE.assertion_error = ::Test::Unit::AssertionFailedError 4 | 5 | # TestUnit uses #add_assertion on it's +result+ object to track counts. 6 | # We capture the result object by overriding the TestCase#run method, 7 | # store it in a global variable and then use it when AE increments 8 | # assertion counts. 9 | # 10 | # In addition we teach #run to recognize any Exception class that 11 | # responds to #assertion? in the affirmative as an assertion 12 | # rather than an error. 13 | # 14 | module Test #:nodoc: 15 | module Unit #:nodoc: 16 | class TestCase #:nodoc: 17 | # These exceptions are not caught by #run. 18 | PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, Interrupt, SystemExit] 19 | # Runs the individual test method represented by this 20 | # instance of the fixture, collecting statistics, failures 21 | # and errors in result. 22 | def run(result) 23 | $_test_unit_result = result 24 | yield(STARTED, name) 25 | @_result = result 26 | begin 27 | setup 28 | __send__(@method_name) 29 | rescue AssertionFailedError => e 30 | add_failure(e.message, e.backtrace) 31 | rescue Exception => e 32 | if e.respond_to?(:assertion?) && e.assertion? 33 | add_failure(e.message, e.backtrace) 34 | else 35 | raise if PASSTHROUGH_EXCEPTIONS.include? $!.class 36 | add_error($!) 37 | end 38 | ensure 39 | begin 40 | teardown 41 | rescue AssertionFailedError => e 42 | add_failure(e.message, e.backtrace) 43 | rescue Exception => e 44 | if e.respond_to?(:assertion?) && e.assertion? 45 | add_failure(e.message, e.backtrace) 46 | else 47 | raise if PASSTHROUGH_EXCEPTIONS.include? $!.class 48 | add_error($!) 49 | end 50 | end 51 | end 52 | result.add_run 53 | yield(FINISHED, name) 54 | end 55 | end 56 | end 57 | end 58 | 59 | class AE::Assertor #:nodoc: 60 | def self.increment_counts(pass) 61 | $_test_unit_result.add_assertion if $_test_unit_result 62 | counts[:total] += 1 63 | if pass 64 | counts[:pass] += 1 65 | else 66 | counts[:fail] += 1 67 | end 68 | return counts 69 | end 70 | end 71 | 72 | -------------------------------------------------------------------------------- /demo/05_expect.md: -------------------------------------------------------------------------------- 1 | # Expect Method 2 | 3 | Expect is another assertion nomenclature available for use in your 4 | tests or specifications. Inspired by Jay Fields' Expectations library, 5 | it provides convenient syntax for creating exception and case equality 6 | assertions. 7 | 8 | require 'ae/expect' 9 | 10 | 11 | ## Underlying Comparison 12 | 13 | Expect uses `#===` for comparison. So providing an argument and a block 14 | to #expect we can test for a somewhat broader range of compassion 15 | than #assert. For example we can test for a subclass. 16 | 17 | expect Numeric do 18 | 3 19 | end 20 | 21 | Assertion.assert.raised? do 22 | expect Numeric do 23 | "3" 24 | end 25 | end 26 | 27 | 28 | ## Exception Expectation 29 | 30 | If the comparator is an Exception class or a instance of an Exception class, 31 | then #expect will check to see if the block raises that kind of exception. 32 | 33 | expect StandardError do 34 | some_undefined_method 35 | end 36 | 37 | expect Assertion do 38 | expect(nil) 39 | end 40 | 41 | This is an important distinction to note because it means `#expect` can not be used 42 | if verify instances of Exception classes. 43 | 44 | Assertion.assert.raised? do 45 | expect Exception do 46 | Exception.new 47 | end 48 | end 49 | 50 | 51 | ## Regex Expectations 52 | 53 | That #expect entails `#===` also means we can check for Regexp matches. 54 | 55 | expect /x/ do 56 | "oooxooo" 57 | end 58 | 59 | 60 | ## Expected Method 61 | 62 | We can use #expected to make the receiver the object of expectation. 63 | 64 | x = "dummy" 65 | 66 | /x/.expected do 67 | "x" 68 | end 69 | 70 | 71 | ## Without Block 72 | 73 | Without a block, the receiver is compared to the argument. 74 | 75 | x.expect String 76 | 77 | 78 | ## Negative Forms 79 | 80 | Like #assert, `#expect` has a negated form called #expect! 81 | 82 | expect! /x/ do 83 | "o" 84 | end 85 | 86 | The pure word form for those who do not like the clever use of the 87 | explimation mark is #forbid. 88 | 89 | forbid /x/ do 90 | "o" 91 | end 92 | 93 | 94 | ## Functor, or Higher Order Function 95 | 96 | Like #assert, #expect can be used used as a *fluid* notation. 97 | 98 | 10.expect == 10 99 | 100 | In which case it works just like `#assert`, including negative forms. 101 | 102 | 10.expect! == 11 103 | 10.forbid == 11 104 | 105 | -------------------------------------------------------------------------------- /lib/ae/assert.rb: -------------------------------------------------------------------------------- 1 | require 'ae/assertor' 2 | 3 | module AE 4 | 5 | # The Assert module is simple a conatiner module for the core 6 | # extension methods: #assert, #expect, etc. 7 | # 8 | # This module is included directory into the Object class. 9 | module Assert 10 | 11 | # Assert a operational relationship. 12 | # 13 | # 4.assert == 3 14 | # 15 | # If only a single test argument is given then #assert 16 | # simply validates that it evalutate to true. An optional 17 | # message argument can be given in this case which will 18 | # be used instead of the deafult message. 19 | # 20 | # assert(4==3, "not the same thing") 21 | # 22 | # In block form, #assert ensures the block evalutes 23 | # truthfully, i.e. not as nil or false. 24 | # 25 | # assert{ 4==3 } 26 | # 27 | # @return [Assertor] Assertion functor. 28 | def assert(*args, &block) 29 | Assertor.new(self, :backtrace=>caller).assert(*args, &block) 30 | end 31 | 32 | # Same as 'object.assert == other'. 33 | # 34 | # @return [Assertor] Assertion functor. 35 | def assert=(cmp) 36 | Assertor.new(self, :backtrace=>caller).assert == cmp 37 | end 38 | 39 | # Opposite of assert. 40 | # 41 | # 4.refute == 4 #=> Assertion Error 42 | # 43 | # @return [Assertor] Assertion functor. 44 | def refute(*args, &block) 45 | Assertor.new(self, :backtrace=>caller).not.assert(*args, &block) 46 | end 47 | 48 | # Same as 'object.refute == other'. 49 | # 50 | # @return [Assertor] Assertion functor. 51 | def refute=(cmp) 52 | Assertor.new(self, :backtrace=>caller).not.assert == cmp 53 | end 54 | 55 | # Alias for #refute. Read it as "assert not". 56 | # 57 | # 4.assert! == 4 58 | # 59 | # NOTE: This method would not be necessary if Ruby would allow 60 | # +!=+ to be define as a method, or at least +!+ as a unary method. 61 | # Looks like this is possible in Ruby 1.9, but we will wait until 62 | # Ruby 1.9 is the norm. 63 | alias_method :assert!, :refute 64 | 65 | # Directly raise an Assertion failure. 66 | # 67 | # @param message [String] 68 | # Error message. 69 | # 70 | # @param backtrace [String] 71 | # Backtrace, used to pass up an error from lower in the stack. 72 | # 73 | # @raise [Assertion] 74 | # Assertion error with given `message`. 75 | def flunk(message=nil, backtrace=nil) 76 | #Assertor.new(self, :backtrace=>caller).assert(false, message) 77 | Assertor.assert(false, message, backtrace || caller) 78 | end 79 | 80 | end 81 | 82 | end 83 | 84 | # Copyright (c) 2008 Thomas Sawyer 85 | -------------------------------------------------------------------------------- /work/notes/matrix.rdoc: -------------------------------------------------------------------------------- 1 | == Assett 2 | 3 | r.assert == x 4 | 5 | 1.assert == 2 6 | 7 | [1,2,3].assert.include? 2 8 | 9 | ArgumentError.assert.raised? do 10 | raise 11 | end 12 | 13 | assert.instance_eval do 14 | @a == 1 15 | end 16 | 17 | 4.assert == 3 18 | 19 | 5.assert == 9 20 | 21 | /x/.assert =~ "x" 22 | 23 | Numeric.assert === 3 24 | 25 | 3.assert.kind_of?(Numeric) 26 | 27 | lambda{}.assert.raise?(ArgumentError) 28 | 29 | 30 | == Is and Does (?) 31 | 32 | 1.is == 2 33 | 34 | [1,2,3].does.include? 2 35 | 36 | ArgumentError.is.raised? do 37 | raise 38 | end 39 | 40 | does.instance_eval do 41 | @a == 1 42 | end 43 | 44 | 4.is == 3 45 | 46 | 5.is == 9 47 | 48 | /x/.does =~ "x" 49 | 50 | Numeric.is === 3 51 | 52 | 3.is.kind_of?(Numeric) 53 | 54 | lambda{}.does.raise?(ArgumentError) 55 | 56 | 57 | === Must or Should 58 | 59 | 1.must == 2 60 | 61 | [1,2,3].must.include? 2 62 | 63 | ArgumentError.must.be.raised? do 64 | raise 65 | end 66 | 67 | must.instance_eval do 68 | @a == 1 69 | end 70 | 71 | 4.must == 3 72 | 73 | 5.must == 9 74 | 75 | /x/.must =~ "x" 76 | 77 | Numeric.must === 3 78 | 79 | 3.must.be.kind_of?(Numeric) 80 | 81 | lambda{}.must.raise?(ArgumentError) 82 | 83 | 84 | 85 | 86 | == Assert 87 | 88 | Assert as truth makes sense most possiblities, 89 | but allows for a equality testing when blocks 90 | are used. 91 | 92 | r.assert a, &b 93 | 94 | r a b 95 | 0 0 0 -> Assertor 96 | 0 0 b -> if b 97 | 0 a 0 -> if a 98 | 0 a b -> a = b 99 | r 0 0 -> Assertor 100 | r 0 b -> r = b 101 | r a 0 [same as 0 a 0] 102 | r a b -> 103 | 104 | 105 | == Expect 106 | 107 | The problem with expect is what type of equality to use. 108 | eql?, ==, === or some combination? It must be specific. 109 | If it is not specific, or at least nearly so, then it 110 | runs too great a chance of false positives. 111 | 112 | r.expect a, &b 113 | 114 | r a b 115 | ------- 116 | 0 0 0 -> Assertor 117 | 0 0 b -> nothing raised? 118 | 0 a 0 [same as r a 0] 119 | 0 a b -> a = b 120 | r 0 0 -> Assertor 121 | r 0 b -> r = b 122 | r a 0 -> r = a 123 | r a b -> 124 | 125 | 126 | == Must 127 | 128 | Very limited in its ability to be consitant since 129 | only one form makes sense for an assert using 130 | instance_eval. The rest must be like expect using 131 | instance_eval. 132 | 133 | r.must a, &b 134 | 135 | r a b 136 | ------- 137 | 0 0 0 -> Assertor 138 | 0 0 b -> if b* 139 | 0 a 0 [same as r a 0] 140 | 0 a b -> a = b* 141 | r 0 0 -> Assertor 142 | r 0 b -> r = b* 143 | r a 0 [makes no sense] 144 | r a b -> 145 | 146 | * using instance_eval 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /demo/01_overview.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | AE is an assertions framework for Ruby. It's designed 4 | around the concept of an Assertor. The Assertor is an 5 | Assertion Functor, or Higher-Order Function, which 6 | reroutes method calls while monitoring them for failing 7 | conditions. 8 | 9 | 10 | ## What AE Provides 11 | 12 | Requiring the AE library. 13 | 14 | require 'ae' 15 | 16 | Loads two classes, +Assertion+ and +Assertor+, the Kernel 17 | method +assert+ and it's antonyms +assert!+ and +refute+ 18 | and a set of core extensions that make writing certain types 19 | of assertions easier. 20 | 21 | 22 | ## Assertion and Assertor Classes 23 | 24 | The +Assertion+ class is at the heart of AE. All other AE 25 | methods depend on it. The +Assertion+ class is a subclass 26 | of Exception. When an assertion is made and fails, it is 27 | an instance of Assertion that is raised. 28 | 29 | expect Assertion do 30 | msg = "my failure message" 31 | assert false, msg 32 | end 33 | 34 | Like any raised exception, the last Assertion message is available 35 | via `$!`. 36 | 37 | (FYI, in Test::Unit the equivalent class was called +AssertionFailedError+.) 38 | 39 | Assertions themselves are not generally used in creating tests or 40 | behavior specifications. Rather they are used to create additional 41 | types of assertion methods. 42 | 43 | As mentioned above the +Assertor+ class is a type of Higher-Order 44 | function, or Functor, which intercedes with a normal message 45 | invocation to monitor for failed conditions, upon which is raises 46 | Assertion exceptions. 47 | 48 | 49 | ## Assertion Methods 50 | 51 | The three methods, +assert+, assert! and +refute+ all 52 | return an Assertor instance when used fluidly, i.e. magic-dot 53 | notation, higher-order notation, functor notation, whatever you 54 | prefer to call it. 55 | 56 | assert(AE::Assertor === assert) 57 | 58 | Through the use of +method_missing+, the Assertor allows us to write 59 | statements like: 60 | 61 | 1.assert == 1 62 | 63 | If the operation evaluates to false or nil, then an Assertion error 64 | is raised. 65 | 66 | expect Assertion do 67 | 1.assert == 2 68 | end 69 | 70 | The methods assert! and +refute+ are just like +assert+ 71 | expect they purport the negative condition. Patterned after Ruby's 72 | own use of "!" as meaning +not+, assert! should be 73 | read "assert not". While +refute+ exists for the sake of those who 74 | find the use of a bang method for this purpose unsuited to them. 75 | 76 | 77 | ## How It Works 78 | 79 | An Assertor essentially sits in wait for a method call (via 80 | method_missing). When that happens it applies the method to the 81 | original receiver, but wrapped in a clause that raises an 82 | Assertion should the statement fail. If we wanted to be 83 | pedantic, we could write our assertions like: 84 | 85 | raise Assertion.new("1 != 1") unless 1 == 1 86 | 87 | Instead of 88 | 89 | 1.assert == 1 90 | 91 | Obviously using Assertor methods are whole lot more concise. 92 | 93 | -------------------------------------------------------------------------------- /demo/04_subjunctive.md: -------------------------------------------------------------------------------- 1 | # Subjunctives 2 | 3 | Okay. I can hear the BDDers rumbling, "where's the *should?*" 4 | AE has nothing against "should", but there are different 5 | approaches for utilizing should nomenclature in specifications, 6 | and AE wants to be open to these techniques. One of which 7 | is how Shoulda (http://shoulda.rubyforge.org) utilizes 8 | `should` in a way analogous to RSpec's use of `it`. 9 | 10 | Even so, AE provides an optional mixin called `Subjunctive` which 11 | can be used to create assertor methods with English subjunctive 12 | terms, such as `should`, or `must`, `shall` and `will`. 13 | To load this library use: 14 | 15 | require 'ae/subjunctive' 16 | 17 | Then all that is required it to define a subjunctive method for all 18 | objects. For example: 19 | 20 | def will(*args, &block) 21 | Assertor.new(self, :backtrace=>caller).be(*args,&block) 22 | end 23 | 24 | It's that easy. Because of their commonality AE provides two such terms, 25 | `should` and +must+ as optional add-ons out-of-the-box. 26 | 27 | require 'ae/should' 28 | require 'ae/must' 29 | 30 | We will use these two methods interchangeable for the rest of this 31 | demonstration, but to be clear they both work exactly the same way, 32 | and almost exactly like `assert`. 33 | 34 | Keep in mind, AE "conical" functionality does not entail the subjunctive 35 | forms. These are simply options you can load via your `test_helper.rb`, 36 | or similar script, if you prefer these nomenclatures. 37 | 38 | 39 | ## Fluent Notation and Antonyms 40 | 41 | Like `assert`, `should` and `must` can be used as higher order functions. 42 | 43 | 4.should == 4 44 | 4.must == 4 45 | 46 | Antonyms provided for +should+ as `should!` (read "should not") and `shouldnt`. 47 | For `must`, they are `must!` and +wont+. 48 | 49 | 4.should! == 5 50 | 4.shouldnt == 5 51 | 52 | 4.must! == 5 53 | 4.wont == 5 54 | 55 | 56 | ## To Be 57 | 58 | On occasions where the English readability of a specification is hindered, 59 | `be` can be used. 60 | 61 | StandardError.must.be.raised? do 62 | unknown_method 63 | end 64 | 65 | The `be` method is the same as `assert` with the single exception 66 | that it will compare a lone argument to the receiver using `equate?`, 67 | unlike `assert` which simply checks to see that the argument evaluates 68 | as true. 69 | 70 | 10.should.be 10 71 | 10.should.be 10.0 72 | 10.should.be Numeric 73 | 74 | Assertion.assert.raised? do 75 | 10.should.be "40" 76 | end 77 | 78 | 79 | ## Indefinite Articles 80 | 81 | Additional English forms are `a` and `an`, equivalent to `be` except 82 | that they use `case?` (same as `#===`) instead of `equate?` when 83 | acting on a single argument. 84 | 85 | "hi".must.be.a String 86 | 87 | Assertion.assert.raised? do 88 | /x/.must.be.a /x/ 89 | end 90 | 91 | Otherwise they are interchangeable. 92 | 93 | "hi".must.be.an.instance_of?(String) 94 | 95 | The indefinite articles work well when a noun follows as an arguments. 96 | 97 | palindrome = lambda{ |x| x == x.reverse } 98 | 99 | "abracarba".must.be.a palindrome 100 | 101 | -------------------------------------------------------------------------------- /lib/ae/basic_object.rb: -------------------------------------------------------------------------------- 1 | if RUBY_VERSION >= '1.9' 2 | 3 | module AE 4 | BasicObject = ::BasicObject 5 | end 6 | 7 | else 8 | 9 | module AE 10 | # BasicObject provides an abstract base class with no predefined 11 | # methods (except for \_\_send__ and \_\_id__). 12 | # BasicObject is useful as a base class when writing classes that 13 | # depend upon method_missing (e.g. dynamic proxies). 14 | # 15 | # BasicObject is based on BlankSlate by Jim Weirich. 16 | # 17 | # Copyright 2004, 2006 by Jim Weirich (jim@weirichhouse.org). 18 | # All rights reserved. 19 | class BasicObject #:nodoc: 20 | 21 | # Hide the method named +name+ in the BlankSlate class. Don't 22 | # hide +instance_eval+ or any method beginning with "__". 23 | def self.hide(name) 24 | name = name.to_s 25 | if instance_methods.include?(name) and 26 | name !~ /^(__|instance_eval|instance_exec)/ 27 | @hidden_methods ||= {} 28 | @hidden_methods[name.to_sym] = instance_method(name) 29 | undef_method name 30 | end 31 | end 32 | 33 | def self.find_hidden_method(name) 34 | @hidden_methods ||= {} 35 | @hidden_methods[name.to_sym] || superclass.find_hidden_method(name) 36 | end 37 | 38 | # Redefine a previously hidden method so that it may be called on a blank 39 | # slate object. 40 | def self.reveal(name) 41 | hidden_method = find_hidden_method(name) 42 | fail "Don't know how to reveal method '#{name}'" unless hidden_method 43 | define_method(name, hidden_method) 44 | end 45 | 46 | # 47 | instance_methods.each { |m| hide(m) } 48 | end 49 | end 50 | 51 | # Since Ruby is very dynamic, methods added to the ancestors of 52 | # BlankSlate after BlankSlate is defined will show up in the 53 | # list of available BlankSlate methods. We handle this by defining a 54 | # hook in the Object and Kernel classes that will hide any method 55 | # defined after BlankSlate has been loaded. 56 | # 57 | module Kernel 58 | class << self 59 | alias_method :basic_object_method_added, :method_added 60 | 61 | # Detect method additions to Kernel and remove them in the 62 | # BasicObject class. 63 | def method_added(name) 64 | result = basic_object_method_added(name) 65 | return result if self != Kernel 66 | AE::BasicObject.hide(name) 67 | result 68 | end 69 | end 70 | end 71 | 72 | # Same as above, except in Object. 73 | # 74 | class Object 75 | class << self 76 | alias_method :basic_object_method_added, :method_added 77 | 78 | # Detect method additions to Object and remove them in the 79 | # BlankSlate class. 80 | def method_added(name) 81 | result = basic_object_method_added(name) 82 | return result if self != Object 83 | AE::BasicObject.hide(name) 84 | result 85 | end 86 | 87 | def find_hidden_method(name) 88 | nil 89 | end 90 | end 91 | end 92 | 93 | # Also, modules included into Object need to be scanned and have their 94 | # instance methods removed from blank slate. In theory, modules 95 | # included into Kernel would have to be removed as well, but a 96 | # "feature" of Ruby prevents late includes into modules from being 97 | # exposed in the first place. 98 | # 99 | class Module #:nodoc: 100 | alias basic_object_original_append_features append_features 101 | def append_features(mod) 102 | result = basic_object_original_append_features(mod) 103 | return result if mod != Object 104 | instance_methods.each do |name| 105 | AE::BasicObject.hide(name) 106 | end 107 | result 108 | end 109 | end 110 | 111 | end 112 | -------------------------------------------------------------------------------- /lib/ae/check.rb: -------------------------------------------------------------------------------- 1 | module AE 2 | 3 | # The Ok mixin is a reusable assertion helper that 4 | # makes it easy to construct parameterized assertions 5 | # with an elegant syntax. 6 | # 7 | module Check 8 | 9 | # The Check::Proc class encapsulates a labeled procedure 10 | # for making assertions using the `ok`/`no` methods. 11 | # 12 | class Proc 13 | # Setup new check procedure. 14 | def initialize(options={}, &check) 15 | @name = options[:name] 16 | @message = options[:message] || @name 17 | @check = check 18 | end 19 | 20 | # 21 | def message(&block) 22 | if block 23 | @message = message 24 | end 25 | @message 26 | end 27 | 28 | # 29 | def message=(msg) 30 | @message = msg 31 | end 32 | 33 | # Call check procedure. 34 | def call(*args) 35 | @check.call(*args) 36 | end 37 | 38 | # 39 | def to_s(*args) 40 | case @message 41 | when nil 42 | @name.to_s 43 | when ::Proc 44 | @message.call(*args) 45 | else 46 | # TODO: count %\S and apply `% args.map{|a|a.inspect}[0,count]` 47 | @message.to_s 48 | end 49 | end 50 | 51 | # 52 | def ok!(*args) 53 | assert(call(*args), to_s(*args)) 54 | end 55 | 56 | # 57 | def no!(*args) 58 | refute(call(*args), to_s(*args)) 59 | end 60 | end 61 | 62 | # TODO: Better way to customize error message so it can have 63 | # arguments in the messages ? 64 | 65 | # Built-in check procedures. 66 | TABLE = { 67 | :equality => Check::Proc.new(:message=>"should be equal"){|h| h.any?{|a,b| b==a}}, 68 | :case_equality => Check::Proc.new(:message=>"should be equal"){|h| h.any?{|a,b| b===a}} 69 | } 70 | 71 | # 72 | def self.table 73 | @table ||= TABLE.dup 74 | end 75 | 76 | # Define a univerally available ok/no check. 77 | # 78 | # AE::Check.define(:palindrome) do |x| 79 | # x.reverse == x 80 | # end 81 | # 82 | def self.define(name, &block) 83 | table[name] = Check::Proc.new(name, &block) 84 | end 85 | 86 | # 87 | def check_table 88 | Check.table 89 | end 90 | 91 | # Define an ok/no check procedure. A one-off procedure is defined 92 | # with a block. 93 | # 94 | # check do |x, y| 95 | # x == y 96 | # end 97 | # 98 | # ok 1,1 99 | # no 1,2 100 | # 101 | # The check method can also be used to define reusable checks. 102 | # 103 | # check(:palindrome) do |x| 104 | # x.reverse == x 105 | # end 106 | # 107 | # This will also cause the current check to be set. 108 | # Later in the code, the check procedure can be restored 109 | # by just passing the symbolic name. 110 | # 111 | # check :palindrome 112 | # 113 | # ok 'abracarba' 114 | # no 'foolishness' 115 | # 116 | def check(name=nil, &block) 117 | if name 118 | if block 119 | check_table[name] = Check::Proc.new(:name=>name, &block) 120 | end 121 | @__check__ = check_table[name] 122 | else 123 | #raise ArgumentError if block.arity == 0 124 | @__check__ = Check::Proc.new(&block) 125 | end 126 | end 127 | 128 | # 129 | def ok(*args) 130 | __check__.ok!(*args) 131 | end 132 | 133 | # 134 | def no(*args) 135 | __check__.no!(*args) 136 | end 137 | 138 | # Returns the current check. 139 | def __check__ 140 | @__check__ || check_table[:equality] 141 | end 142 | 143 | end 144 | 145 | end 146 | 147 | module AE::World 148 | # It's upto the test framework to include where needed. 149 | include AE::Check 150 | end 151 | 152 | -------------------------------------------------------------------------------- /lib/ae/core_ext/helpers.rb: -------------------------------------------------------------------------------- 1 | # Nearly all, if not all, of these core extension are available from Ruby Facets. 2 | 3 | # hack 4 | NoArgument = Object.new 5 | 6 | module Kernel 7 | # Is literally true. 8 | def true? 9 | TrueClass === self 10 | end 11 | 12 | # Is literally false. 13 | def false? 14 | FalseClass === self 15 | end 16 | 17 | # Are identical, eg. object_id's are equal. 18 | def identical?(exp) 19 | exp.object_id == object_id 20 | end 21 | 22 | # Alias for #identical? 23 | alias_method :identical_to?, :identical? 24 | 25 | # Word form of #==. Also can take a block. 26 | def eq?(value=NoArgument) #:yield: 27 | if block_given? 28 | self == yield 29 | else 30 | self == value 31 | end 32 | end 33 | 34 | # Word form of #===. Also can take a block. 35 | def case?(value=NoArgument) #:yield: 36 | if block_given? 37 | self === yield 38 | else 39 | self === value 40 | end 41 | end 42 | 43 | # Word form for #=~. Also can take a block. 44 | def match?(value=NoArgument) 45 | if block_given? 46 | self =~ yield 47 | else 48 | self =~ value 49 | end 50 | end 51 | 52 | # Broad equality. 53 | def equate?(x) 54 | equal?(x) || eql?(x) || self == x || self === x 55 | end 56 | 57 | # Can a message be sent to the receiver successfully? 58 | def send?(method, *args, &block) 59 | begin 60 | __send__(method, *args, &block) 61 | true 62 | rescue NoMethodError 63 | false 64 | end 65 | end 66 | 67 | # 68 | #def returns?(value) #:yield: 69 | # value == yield 70 | #end 71 | 72 | unless method_defined?(:public_send) 73 | # 74 | def public_send(m,*a,&b) 75 | raise NoMethodError unless respond_to?(m) 76 | __send__(m,*a,&b) 77 | end 78 | end 79 | end 80 | 81 | 82 | class Object 83 | # Allows equal? to take a block. 84 | def equal?(value=NoArgument) #:yield: 85 | if block_given? 86 | super(yield) 87 | else 88 | super 89 | end 90 | end 91 | 92 | # Allows eql? to take a block. 93 | def eql?(value=NoArgument) #:yield: 94 | if block_given? 95 | super(yield) 96 | else 97 | super 98 | end 99 | end 100 | end 101 | 102 | class Numeric 103 | # Is self and given number within delta tolerance. 104 | # 105 | # 0.05.in_delta?(50000.0 / 10**6, 0.00001) 106 | # 107 | def in_delta?(orig, delta=0.001) 108 | #(num.to_f - to_f).abs <= delta.to_f 109 | delta >= (orig - self).abs 110 | end 111 | 112 | # Alias for #in_delta. 113 | alias_method :close?, :in_delta? 114 | 115 | # Verify epsilon tolerance. 116 | def in_epsilon?(orig, epsilon=0.001) 117 | in_delta?(orig, [orig, self].min * epsilon) 118 | end 119 | end 120 | 121 | 122 | class Module 123 | # Is a given class or module an ancestor of this 124 | # class or module? 125 | # 126 | # class X ; end 127 | # class Y < X ; end 128 | # 129 | # Y.is?(X) #=> true 130 | # 131 | def is?(base) 132 | Module===base && ancestors.slice(1..-1).include?(base) 133 | end 134 | end 135 | 136 | class Proc 137 | # 138 | def raises?(exception=Exception, *args) 139 | begin 140 | call(*args) 141 | false 142 | rescue exception => error 143 | exception === error 144 | end 145 | end 146 | 147 | # 148 | def throws?(sym, *args) 149 | catch(sym) do 150 | begin 151 | call(*args) 152 | rescue ArgumentError # 1.9 exception 153 | rescue NameError # 1.8 exception 154 | end 155 | return false 156 | end 157 | return true 158 | end 159 | 160 | # TODO: Put in facets? 161 | # TODO: wrong place, change yield? 162 | def change? 163 | pre_result = yield 164 | call 165 | post_result = yield 166 | pre_result != post_result 167 | end 168 | end 169 | 170 | class Symbol 171 | # Does the block throw this symbol? 172 | # 173 | def thrown?(*args) 174 | catch(self) do 175 | begin 176 | yield(*args) 177 | rescue ArgumentError # 1.9 exception 178 | rescue NameError # 1.8 exception 179 | end 180 | return false 181 | end 182 | return true 183 | end 184 | end 185 | 186 | class Exception 187 | # 188 | def self.raised? #:yeild: 189 | begin 190 | yield 191 | false 192 | rescue self 193 | true 194 | end 195 | end 196 | end 197 | 198 | # Copyright (c) 2008,2009 Thomas Sawyer 199 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Assertive Expressive 2 | 3 | [Website](http://rubyworks.github.com/ae) / 4 | [API](http://rubydoc.info/gems/ae) / 5 | [User Guide](http://wiki.github.com/rubyworks/ae) / 6 | [Report Issue](http://github.com/rubyworks/ae/issues) / 7 | [Source Code](http://github.com/rubyworks/ae) 8 | 9 | [![Gem Version](https://badge.fury.io/rb/ae.png)](http://badge.fury.io/rb/ae) 10 | [![Build Status](https://secure.travis-ci.org/rubyworks/ae.png)](http://travis-ci.org/rubyworks/ae)     11 | [![Flattr Me](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/324911/Rubyworks-Ruby-Development-Fund) 12 | 13 | 14 | ## About 15 | 16 | Assertive Expressive (AE) is an assertions framework 17 | intended for reuse by any TDD, BDD or similar system. 18 | 19 | 20 | ## Features 21 | 22 | * Clear, simple and concise syntax. 23 | * Uses higher-order functions and fluid notation. 24 | * Reusable core extensions ease assertion construction. 25 | * Core extensions are standardized around Ruby Facets. 26 | * But Facets is not a dependency; the extensions are built-in. 27 | * Easily extensible allowing for alternate notations. 28 | * Eats it's own dog food. 29 | 30 | 31 | ## Synopsis 32 | 33 | AE defines the method `assert`. It's is compatible with the method 34 | as defined by Test::Unit and MiniTest, which verifies truth of a 35 | single argument (and can accept an optional failure message). 36 | 37 | assert(true) 38 | 39 | In addition AE's `assert` method has been extended to accept a block, 40 | the result of which is likewise verified. 41 | 42 | assert{true} 43 | 44 | But the real power the AE's +assert+ method lies in it's use 45 | without argument or block. In that case it returns an instance of 46 | `Assertor`. An `Assertor` is an *Assertions Functor*, or 47 | *Higher-Order Function*. It is a function that operates on 48 | another function. With it, we can make assertions like so: 49 | 50 | x.assert == y 51 | 52 | a.assert.include? e 53 | 54 | StandardError.assert.raised? do 55 | ... 56 | end 57 | 58 | And so forth. Any method can be used in conjunction with +assert+ 59 | to make an assertion. Eg. 60 | 61 | class String 62 | def daffy? 63 | /daffy/i =~ self 64 | end 65 | end 66 | 67 | "Daffy Duck".assert.daffy? 68 | 69 | When an assertion fails an Assertion exception is raised. Any test 70 | framework can catch this exception and process it accordingly. 71 | Technically the framework should check to see that the exception 72 | object responds affirmatively to the #assertion? method. This way any 73 | type of exception can be used as a means of assertion, not just AE's 74 | Assertion class. 75 | 76 | Please have a look at the QED and API documentation to learn more. 77 | 78 | 79 | ## Integration 80 | 81 | Generally speaking, AE can be used with any test framework simply by putting 82 | `require 'ae'` in a test helper script. However to fully 83 | integrate with a test framework and ensure the test framework recognizes 84 | AE assertions (as more than just exceptions) and to ensure assertion 85 | counts are correct, a little extra interfacing code may be necessary. 86 | 87 | Lucky for you AE has already done the leg work for the most common 88 | test frameworks: 89 | 90 | require 'ae/adapters/testunit' 91 | require 'ae/adapters/minitest' 92 | require 'ae/adapters/rspec' 93 | 94 | (Note that Cucumber does not need an adapter.) 95 | 96 | AE also includes a script that will automatically detect the current 97 | test framework by checking for the existence of their respective 98 | namespace modules. 99 | 100 | require 'ae/adapter' 101 | 102 | 103 | ## Nomenclature 104 | 105 | With AE, defining assertions centers around the #assert method. So 106 | *assert* can be thought of as AE's primary _nomenclature_. However, variant 107 | nomenclatures have been popularized by other test frameworks, in particular 108 | *should* and *must*. If you prefer one of them terms, AE provides optional 109 | libraries that can loaded for utilizing them. 110 | 111 | require 'ae/should' 112 | require 'ae/must' 113 | 114 | By loading one of these scripts (or both) into your test system (e.g. via a test 115 | helper script) you gain access to subjunctive terminology. See the API documentation 116 | for the Subjunctive module for details. 117 | 118 | 119 | ## Legacy 120 | 121 | To ease transition from TestUnit style assertion methods, AE provides 122 | a TestUnit legacy module. 123 | 124 | require 'ae/legacy' 125 | 126 | This provides a module `AE::Legacy::Assertions` which is included in AE::World 127 | and can be mixed into your test environment to provide old-school assertion 128 | methods, e.g. 129 | 130 | assert_equal(foo, bar, "it failed") 131 | 132 | 133 | ## Installation 134 | 135 | ### Gem Installs 136 | 137 | Install AE in the usual fashion: 138 | 139 | $ gem install ae 140 | 141 | ### Site Installs 142 | 143 | Local installation requires Setup.rb. 144 | 145 | $ gem install setup 146 | 147 | Then download the tarball package from GitHub 148 | and do: 149 | 150 | $ tar -xvzf ae-1.0.0.tgz 151 | $ cd ae-1.0.0.tgz 152 | $ sudo setup.rb all 153 | 154 | Windows users use 'ruby setup.rb all'. 155 | 156 | 157 | ## Contributing 158 | 159 | If you would like to contribute code to the AE project, for the upstream 160 | repository and create a branch for you changes. When your changes are ready 161 | for review (and no, they do not have to 100% perfect if you still have some issues 162 | you need help working out). 163 | 164 | It you need to personally discuss some ideas or issue you try to get up with us 165 | via the mailing list or the IRC channel. 166 | 167 | * [Source Code](http://github.com/rubyworks/ae) / 168 | * [IRC Channel](irc://irc.freenode.net/rubyworks) / 169 | * [Mailing List](http://googlegroups.com/group/rubyworks-mailinglist) 170 | 171 | 172 | ## Copyrights & License 173 | 174 | Copyright (c) 2008 Rubyworks. All rights reserved. 175 | 176 | Unless otherwise provided for by the originating author, this 177 | program is distributed under the terms of the *BSD-2-Clause* license. 178 | Portions of this program may be copyrighted by others. 179 | 180 | See the NOTICE.rdoc file for details. 181 | 182 | AE is a [Rubyworks](http://rubyworks.github.com) project. 183 | 184 | -------------------------------------------------------------------------------- /demo/03_assert.md: -------------------------------------------------------------------------------- 1 | # Assert Method 2 | 3 | ## Compatible with Test::Unit 4 | 5 | The `assert` method is designed to be backward compatible 6 | with the same method in `Test::Unit`. 7 | 8 | Using an argument, `assert` will check that an argument evaluates 9 | to true. Optionally one can send along a meaningful message should 10 | the assertion fail. 11 | 12 | assert(true, "Not true!") 13 | 14 | expect Assertion do 15 | assert(false, "Not true!") 16 | end 17 | 18 | 19 | ## Assert with a Block 20 | 21 | In addition +assert+ has been extended to accept a block. Like the case of the 22 | argument, the block is expected to return something that evaluates as true. 23 | 24 | assert do 25 | true 26 | end 27 | 28 | Assertion.assert.raised? do 29 | assert do 30 | false 31 | end 32 | end 33 | 34 | We should also mention that, while probably not very useful, since 35 | the arity of a block can be checked, one can also pass the receiver 36 | into the block as a block argument. 37 | 38 | "hi".assert do |s| 39 | /h/ =~ s 40 | end 41 | 42 | 43 | ## Antonyms for Assert 44 | 45 | We can state the opposite assertion using `assert!`. 46 | 47 | 10.assert! == 9 48 | 49 | Or, because some people do not like the use of a bang method, +refute+. 50 | 51 | 10.refute == 9 52 | 53 | These terms can be used just as +assert+ is used in all examples, 54 | but with the opposite inference. 55 | 56 | Another way to get the opposite inference, is to use +not+. 57 | 58 | 10.assert.not == 9 59 | 60 | ## Lambda Assertions 61 | 62 | Passing +assert+ a `Proc` object, or any object that responds to `#call`, 63 | will be used as if it were a block. This allows for a simple way to quickly 64 | create reusable assertions. 65 | 66 | palindrome = lambda{ |word| word == word.reverse } 67 | 68 | "abracarba".assert palindrome 69 | 70 | The message for a failed assertion will come from calling `#to_s` on the 71 | object. 72 | 73 | ## RSpec-style Assertion Matchers 74 | 75 | If an object passed to assert responds to `#matches?` then AE will handle 76 | the object as an RSpec-style mather, the receiver will be passed to the 77 | `#matches?` method to determine if the assertion passes and RSpec matcher 78 | message methods will be used if they are defined. 79 | 80 | palindrome = Object.new 81 | 82 | def palindrome.matches?(word) 83 | word == word.reverse 84 | end 85 | 86 | "abracarba".assert palindrome 87 | 88 | 89 | ## Identity Assertions 90 | 91 | Rather then the general form. 92 | 93 | x = 10 94 | x.assert.object_id == x.object_id 95 | 96 | We can use Ruby's own `equal?`> method. 97 | 98 | x.assert.equal?(x) 99 | 100 | AE provides `identical?`> method as an alternative 101 | to make it a bit more clear. 102 | 103 | x.assert.identical?(x) 104 | 105 | 106 | ## Equality Assertions 107 | 108 | The most common assertion is that of value equality (`==`), 109 | as we have seen throughout this document. But other forms of 110 | equality can be verified as easily. We have already mentioned 111 | identity. In addition there is *type equality*. 112 | 113 | 17.assert.eql? 17 114 | 115 | Assertion.assert.raised? do 116 | 17.assert.eql? 17.0 117 | end 118 | 119 | And there is *case equality*. 120 | 121 | Numeric.assert === 3 122 | 123 | 124 | ## Checking Equality with a Block 125 | 126 | Because operators can not take blocks, and at times blocks can 127 | be convenient means of supplying a value to an assertion, 128 | AE has defined alternate renditions of the equality methods. 129 | For equal? and eql?, the method names are the same, they simply 130 | can take a block in place of an argument if need be. 131 | 132 | For *value equality* (`==`), the method is called *eq?*. 133 | 134 | 10.assert.eq? do 135 | 10.0 136 | end 137 | 138 | And should it fail, 139 | 140 | Assertion.assert.raised? do 141 | 10.assert.eq? do 142 | 20 143 | end 144 | end 145 | 146 | 147 | ## Case Equality 148 | 149 | For *case equality* (`===`), it is `case?`. 150 | 151 | Numeric.assert.case? do 152 | "3".to_i 153 | end 154 | 155 | Assertion.assert.raised? do 156 | Numeric.assert.case? do 157 | "3" 158 | end 159 | end 160 | 161 | 162 | ## Regular Expressions 163 | 164 | Regular Expressions can be used to make assertions in much the same way as equality. 165 | 166 | /i/.assert =~ "i" 167 | 168 | Assertion.assert.raised? do 169 | /i/.assert =~ "g" 170 | end 171 | 172 | Conversely the String class recognizes the #=~ method as well. 173 | 174 | "i".assert =~ /i/ 175 | 176 | Assertion.assert.raised? do 177 | "i".assert =~ /g/ 178 | end 179 | 180 | 181 | ## Exception Assertions 182 | 183 | Validating errors is easy too, as has already been shown 184 | in the document to verify assertion failures. 185 | 186 | StandardError.assert.raised? do 187 | unknown_method 188 | end 189 | 190 | 191 | ## Assertions on Object State 192 | 193 | While testing or specifying the internal state of an object is 194 | generally considered poor form, there are times when it is 195 | necessary. Assert combined with +instance_eval+ makes it easy too. 196 | 197 | class X 198 | attr :a 199 | def initialize(a); @a = a; end 200 | end 201 | 202 | x = X.new(1) 203 | 204 | x.assert.instance_eval do 205 | @a == 1 206 | end 207 | 208 | 209 | ## Catch/Try Assertions 210 | 211 | Catch/Try throws can be tested via `Symbol#thrown?`. 212 | 213 | :hookme.assert.thrown? do 214 | throw :hookme 215 | end 216 | 217 | Alternatively, a lambda containing the potential throw 218 | can be the receiver using `throws?`. 219 | 220 | hook = lambda{ throw :hookme } 221 | 222 | hook.assert.throws?(:hookme) 223 | 224 | 225 | ## Assertions on Proc Changes 226 | 227 | I have to admit I'm not sure how this is useful, 228 | but I found it in the Bacon API and ported it over 229 | just for sake of thoroughness. 230 | 231 | a = 0 232 | 233 | l = lambda{ a } 234 | 235 | l.assert.change?{ a +=1 } 236 | 237 | 238 | ## Assertion on literal True, False and Nil 239 | 240 | Ruby already provides the #nil? method. 241 | 242 | nil.assert.nil? 243 | 244 | AE adds `true?` and `false?` which acts accordingly. 245 | 246 | true.assert.true? 247 | false.assert.false? 248 | 249 | 250 | ## Send Assertions 251 | 252 | Assert that a method can be successfully called. 253 | 254 | "STRING".assert.send?(:upcase) 255 | 256 | 257 | ## Numeric Delta and Epsilon 258 | 259 | You may wish to assert that a numeric value is with some 260 | range. 261 | 262 | 3.in_delta?(1,5) 263 | 264 | Or minimum range. 265 | 266 | 3.in_epsilon?(3,5) 267 | 268 | 269 | ## Verifying Object State 270 | 271 | Not surprisingly if underlying object state needs to be verified, +instance_eval+ 272 | can be used in conjunction with +assert+. 273 | 274 | class X 275 | attr :a 276 | def initialize(a); @a = a; end 277 | end 278 | 279 | x = X.new(4) 280 | 281 | x.instance_eval do 282 | @a.assert == 4 283 | end 284 | 285 | However #instance_eval is a reserved method for the underlying Assertor class, 286 | so it cannot be used on #assert, e.g. 287 | 288 | x.assert.instance_eval do 289 | @a == "obvisouly wrong" 290 | end 291 | 292 | AE offers an optional helper method for times when testing underlying private 293 | or protected methods is important, called #pry. See the QED on pry for more 294 | information. 295 | 296 | For some testing underlying implementation might be considered poor 297 | form. You will get no argument here. It should be used thoughtfully, 298 | but I would not bet against there being occasions when such validations 299 | might be needed. 300 | 301 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # RELEASE HISTORY 2 | 3 | ## 1.8.2 / 2013-02-18 4 | 5 | This release primarily fixes one bug --the assertions count 6 | global variable wasn't all caps, which prevented assertions 7 | from being counted correctly. 8 | 9 | Changes: 10 | 11 | * Fix $ASSERTION_COUNTS letter case. 12 | 13 | 14 | ## 1.8.1 / 2011-12-04 15 | 16 | Fixed missing ae/ansi.rb file from distribution. 17 | 18 | Changes: 19 | 20 | * Update manifest, missing ae/ansi.rb 21 | 22 | 23 | ## 1.8.0 / 2011-12-03 / Checkered Flag 24 | 25 | This new release improves support for Proc-based assertions and 26 | RSpec-style matchers. In addition, this release sees the optional 27 | Check Ok/No library in a usable state. And, lastly, note the license 28 | has been changed to BSD-2-Clause. 29 | 30 | Changes: 31 | 32 | * Finalize the Check mixin API. 33 | * Improve Proc and RSpec-style matchers. 34 | * Modernize the build configuration. 35 | * Change licenses to BSD-2-Clause. 36 | 37 | 38 | ## 1.7.4 / 2011-06-08 39 | 40 | Quick release fixes an issue due to Ruby 1.9+'s constant look-up 41 | system. Yes, BasicObject has no clue. This is fixed using the 42 | `const_missing` hook. 43 | 44 | Changes: 45 | 46 | * Add const_missing hook to Assertor class to redirect to 47 | toplevel methods. 48 | 49 | ## 1.7.3 / 2011-06-06 / D-Day 50 | 51 | This release simply adds a new optional help library, `ok.rb`. 52 | The API is still in it's infancy, so it probably will change 53 | but this early release can help refine it's development. 54 | 55 | Changes: 56 | 57 | * Add option `ok` helper library. 58 | 59 | 60 | ## 1.7.2 / 2011-06-02 61 | 62 | Minor release adds color diffs for failed equality comparisons to 63 | the error message, and it fixes an issue in which class references 64 | needed the toplevel prefix (`::`) to ensure they can be found in 65 | all cases. Note that ANSI color diffs can be deactivated via 66 | AE.ansi = false or using the master switch an upcoming release 67 | of the ANSI gem. 68 | 69 | Changes: 70 | 71 | * Add toplevel prefix (`::`) to class references in assertor.rb. 72 | * Provide ANSI colored diffs for failed equality comparisons. 73 | 74 | 75 | ## 1.7.1 / 2011-05-06 76 | 77 | This release adds a specialized message for certain comparison 78 | operators to allow them have a more forensic output. This is done 79 | via ANSI::Diff library. This release also deprecates the optional 80 | dot.rb and detest.rb emulation scripts. 81 | 82 | Changes: 83 | 84 | * Add special message for comparison assertions. 85 | * Remove optional dot.rb and detest.rb emulation scripts. 86 | 87 | 88 | ## 1.7.0 / 2011-04-28 89 | 90 | AE now uses proper namespace for all classes. In particular, the `Assertor` 91 | class has become `AE::Assertor`. Only the `Assertion` class remains outside 92 | the `AE` namespace, as it is now used to map to the current exception class 93 | for raising assertions as defined by current test framework. In addition, 94 | AE's Kernel extensions, which are used to adapt AE for use with any given 95 | test framework have been moved to the AE and AE::Assertor classes as 96 | class methods along with AE's assertion count methods. 97 | 98 | Changes: 99 | 100 | * All AE classes use proper namespace. 101 | * Framework methods moved to AE::Assertor class. 102 | * Assertion count tracking methods moved to AE::Assertor class. 103 | * AE::Assertion class simplified to a simple subclass of Exception. 104 | 105 | 106 | ## 1.6.1 / 2010-11-05 107 | 108 | This release has test passing for Ruby 1.9.2. Ruby 1.9.2 doesn't appear 109 | to like &block and block_given? to be used in same method scope. It 110 | may be a Ruby bug, nonetheless AE has been adjusted to circumvent the 111 | problem. 112 | 113 | Changes: 114 | 115 | * Use `&block` and not `block_given?`. 116 | 117 | 118 | ## 1.6.0 / 2010-11-04 119 | 120 | Support libraries defining toplevel methods, such as `legacy.rb`, now place 121 | their methods in AE::World module instead of Object. AE::World needs to 122 | to be included in the context desired for the testing framework used. This 123 | is important to prevent pollution of the Object namespace. 124 | 125 | Changes: 126 | 127 | * Toplevel extras are defined in AE::World instead of Object. 128 | * In dot.rb #true/#false methods renamed to `#true!`/`#false!`. 129 | * In dot.rb `#true!`/`#false!` methods can take an error or error message. 130 | 131 | 132 | ## 1.5.0 / 2010-09-06 133 | 134 | This release adds adapters for TestUnit, MiniTest and RSpec. AE worked with 135 | them previously but AE assertions were seen as errors rather than nice 136 | assertions. Likewise assertion counts were off in the final tally. These 137 | adapters insert AE's counts so the tally are correct. 138 | 139 | In addition to this the Assertion class itself now acts as the final end 140 | point for all assertions, which makes for a very clean interface. 141 | 142 | Changes: 143 | 144 | * Add adapters for TestUnit, MiniTest and RSpec. 145 | * Move final assertion call to Assertion#test. 146 | 147 | 148 | ## 1.4.0 / 2010-09-02 149 | 150 | Version 1.4 brings Ruby 1.9 compatibility. The Assertor class is now a 151 | subclass of BasicObject. This fixes an issues Assertor would had 152 | applying to methods defined both in a class and Kernel. 153 | 154 | Changes: 155 | 156 | * Assertor is a subclass of BasicObject. 157 | * Use custom BasicObject when using Ruby 1.8. 158 | * Add #assert= which works like `assert ==`. 159 | * Add #refute= which works like `refute ==`. 160 | 161 | 162 | ## 1.3.0 / 2010-06-17 163 | 164 | New release of AE adds support for RSpec-style matchers. This means 165 | it should be usable with Shoulda 3.0 and any other matchers library. 166 | This release also cleans up the underlying code, which is now 167 | extremely clean. Lastly a small API change allows #assert to compare 168 | it's argument to the return of it's block using #==, just as #expect 169 | does using #===. 170 | 171 | Changes: 172 | 173 | * Add RSpec-style matchers support. 174 | * Move #expect code to Assertor. 175 | * #assert method can do equality comparison. 176 | 177 | 178 | ## 1.2.3 / 2010-06-07 179 | 180 | This release is a quick fix, which adds a missing `require 'yaml'`. 181 | 182 | Changes: 183 | 184 | * Add missing require 'yaml'. 185 | 186 | 187 | ## 1.2.2 / 2010-06-06 188 | 189 | Version 1.2.2 simply add one new feature --the ability to 190 | use 'object.assert = other' instead of 'object.assert == other'. 191 | This was added simply because I found I often made the mistake 192 | of a missing '=', and since #assert= has no definition, there 193 | was no reason not to have behave accordingly. 194 | 195 | Also note that I switched the license from LGPL to MIT. 196 | With regards to reusable libraries and I moving all my 197 | work, such that I am able, to MIT to maximize free usage. 198 | 199 | Changes: 200 | 201 | * Add `#assert=` method as a shortcut for `#assert ==`. 202 | * Now distributed under MIT license. 203 | 204 | 205 | ## 1.2.0 / 2010-01-27 206 | 207 | This release fixes '=~' assertions and now requires the 208 | ae/expect library by default. 209 | 210 | Changes: 211 | 212 | * Expect method is now loaded by default when requiring 'ae'. 213 | * Fixed bug where #=~ did not work correctly against Assertor. 214 | 215 | 216 | ## 1.1.0 / 2009-09-06 217 | 218 | This release provided two major improvements. The first is 219 | the #expect method which is similar to #assert, but uses 220 | case equality (#===) for comparison. And second, an optional 221 | library `ae/legacy.rb`, is has been added that provides 222 | backward compatibility with Test::Unit assertions, should it 223 | be needed. 224 | 225 | Changes: 226 | 227 | * New #expect method. 228 | * Proved legacy assertion in optional ae/legacy.rb library. 229 | * Added backtrace parameter to flunk calls. 230 | 231 | 232 | ## 1.0.0 / 2009-09-03 233 | 234 | This is the initial release of AE. 235 | 236 | Changes: 237 | 238 | * Happy Birthday! 239 | 240 | -------------------------------------------------------------------------------- /.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'yaml' 4 | require 'pathname' 5 | 6 | module Indexer 7 | 8 | # Convert index data into a gemspec. 9 | # 10 | # Notes: 11 | # * Assumes all executables are in bin/. 12 | # * Does not yet handle default_executable setting. 13 | # * Does not yet handle platform setting. 14 | # * Does not yet handle required_ruby_version. 15 | # * Support for rdoc entries is weak. 16 | # 17 | class GemspecExporter 18 | 19 | # File globs to include in package (unless manifest file exists). 20 | FILES = ".index .ruby .yardopts alt bin ext lib man spec test [A-Z]*.*" unless defined?(FILES) 21 | 22 | # File globs to omit. 23 | OMIT = "Config.rb" unless defined?(OMIT) 24 | 25 | # Standard file patterns. 26 | PATTERNS = { 27 | :root => '{.index,Gemfile}', 28 | :bin => 'bin/*', 29 | :lib => 'lib/{**/}*', #.rb', 30 | :ext => 'ext/{**/}extconf.rb', 31 | :doc => '*.{txt,rdoc,md,markdown,tt,textile}', 32 | :test => '{test,spec}/{**/}*.rb' 33 | } unless defined?(PATTERNS) 34 | 35 | # For which revision of indexer spec is this converter intended? 36 | REVISION = 2013 unless defined?(REVISION) 37 | 38 | # 39 | def self.gemspec 40 | new.to_gemspec 41 | end 42 | 43 | # 44 | attr :metadata 45 | 46 | # 47 | def initialize(metadata=nil) 48 | @root_check = false 49 | 50 | if metadata 51 | root_dir = metadata.delete(:root) 52 | if root_dir 53 | @root = root_dir 54 | @root_check = true 55 | end 56 | metadata = nil if metadata.empty? 57 | end 58 | 59 | @metadata = metadata || YAML.load_file(root + '.index') 60 | 61 | if @metadata['revision'].to_i != REVISION 62 | warn "This gemspec exporter was not designed for this revision of index metadata." 63 | end 64 | end 65 | 66 | # 67 | def has_root? 68 | root ? true : false 69 | end 70 | 71 | # 72 | def root 73 | return @root if @root || @root_check 74 | @root_check = true 75 | @root = find_root 76 | end 77 | 78 | # 79 | def manifest 80 | return nil unless root 81 | @manifest ||= Dir.glob(root + 'manifest{,.txt}', File::FNM_CASEFOLD).first 82 | end 83 | 84 | # 85 | def scm 86 | return nil unless root 87 | @scm ||= %w{git hg}.find{ |m| (root + ".#{m}").directory? }.to_sym 88 | end 89 | 90 | # 91 | def files 92 | return [] unless root 93 | @files ||= \ 94 | if manifest 95 | File.readlines(manifest). 96 | map{ |line| line.strip }. 97 | reject{ |line| line.empty? || line[0,1] == '#' } 98 | else 99 | list = [] 100 | Dir.chdir(root) do 101 | FILES.split(/\s+/).each do |pattern| 102 | list.concat(glob(pattern)) 103 | end 104 | OMIT.split(/\s+/).each do |pattern| 105 | list = list - glob(pattern) 106 | end 107 | end 108 | list 109 | end.select{ |path| File.file?(path) }.uniq 110 | end 111 | 112 | # 113 | def glob_files(pattern) 114 | return [] unless root 115 | Dir.chdir(root) do 116 | Dir.glob(pattern).select do |path| 117 | File.file?(path) && files.include?(path) 118 | end 119 | end 120 | end 121 | 122 | def patterns 123 | PATTERNS 124 | end 125 | 126 | def executables 127 | @executables ||= \ 128 | glob_files(patterns[:bin]).map do |path| 129 | File.basename(path) 130 | end 131 | end 132 | 133 | def extensions 134 | @extensions ||= \ 135 | glob_files(patterns[:ext]).map do |path| 136 | File.basename(path) 137 | end 138 | end 139 | 140 | def name 141 | metadata['name'] || metadata['title'].downcase.gsub(/\W+/,'_') 142 | end 143 | 144 | def homepage 145 | page = ( 146 | metadata['resources'].find{ |r| r['type'] =~ /^home/i } || 147 | metadata['resources'].find{ |r| r['name'] =~ /^home/i } || 148 | metadata['resources'].find{ |r| r['name'] =~ /^web/i } 149 | ) 150 | page ? page['uri'] : false 151 | end 152 | 153 | def licenses 154 | metadata['copyrights'].map{ |c| c['license'] }.compact 155 | end 156 | 157 | def require_paths 158 | metadata['load_path'] || ['lib'] 159 | end 160 | 161 | # 162 | # Convert to gemnspec. 163 | # 164 | def to_gemspec 165 | if has_root? 166 | Gem::Specification.new do |gemspec| 167 | to_gemspec_data(gemspec) 168 | to_gemspec_paths(gemspec) 169 | end 170 | else 171 | Gem::Specification.new do |gemspec| 172 | to_gemspec_data(gemspec) 173 | to_gemspec_paths(gemspec) 174 | end 175 | end 176 | end 177 | 178 | # 179 | # Convert pure data settings. 180 | # 181 | def to_gemspec_data(gemspec) 182 | gemspec.name = name 183 | gemspec.version = metadata['version'] 184 | gemspec.summary = metadata['summary'] 185 | gemspec.description = metadata['description'] 186 | 187 | metadata['authors'].each do |author| 188 | gemspec.authors << author['name'] 189 | 190 | if author.has_key?('email') 191 | if gemspec.email 192 | gemspec.email << author['email'] 193 | else 194 | gemspec.email = [author['email']] 195 | end 196 | end 197 | end 198 | 199 | gemspec.licenses = licenses 200 | 201 | requirements = metadata['requirements'] || [] 202 | requirements.each do |req| 203 | next if req['optional'] 204 | next if req['external'] 205 | 206 | name = req['name'] 207 | groups = req['groups'] || [] 208 | 209 | version = gemify_version(req['version']) 210 | 211 | if groups.empty? or groups.include?('runtime') 212 | # populate runtime dependencies 213 | if gemspec.respond_to?(:add_runtime_dependency) 214 | gemspec.add_runtime_dependency(name,*version) 215 | else 216 | gemspec.add_dependency(name,*version) 217 | end 218 | else 219 | # populate development dependencies 220 | if gemspec.respond_to?(:add_development_dependency) 221 | gemspec.add_development_dependency(name,*version) 222 | else 223 | gemspec.add_dependency(name,*version) 224 | end 225 | end 226 | end 227 | 228 | # convert external dependencies into gemspec requirements 229 | requirements.each do |req| 230 | next unless req['external'] 231 | gemspec.requirements << ("%s-%s" % req.values_at('name', 'version')) 232 | end 233 | 234 | gemspec.homepage = homepage 235 | gemspec.require_paths = require_paths 236 | gemspec.post_install_message = metadata['install_message'] 237 | end 238 | 239 | # 240 | # Set gemspec settings that require a root directory path. 241 | # 242 | def to_gemspec_paths(gemspec) 243 | gemspec.files = files 244 | gemspec.extensions = extensions 245 | gemspec.executables = executables 246 | 247 | if Gem::VERSION < '1.7.' 248 | gemspec.default_executable = gemspec.executables.first 249 | end 250 | 251 | gemspec.test_files = glob_files(patterns[:test]) 252 | 253 | unless gemspec.files.include?('.document') 254 | gemspec.extra_rdoc_files = glob_files(patterns[:doc]) 255 | end 256 | end 257 | 258 | # 259 | # Return a copy of this file. This is used to generate a local 260 | # .gemspec file that can automatically read the index file. 261 | # 262 | def self.source_code 263 | File.read(__FILE__) 264 | end 265 | 266 | private 267 | 268 | def find_root 269 | root_files = patterns[:root] 270 | if Dir.glob(root_files).first 271 | Pathname.new(Dir.pwd) 272 | elsif Dir.glob("../#{ROOT}").first 273 | Pathname.new(Dir.pwd).parent 274 | else 275 | #raise "Can't find root of project containing `#{root_files}'." 276 | warn "Can't find root of project containing `#{root_files}'." 277 | nil 278 | end 279 | end 280 | 281 | def glob(pattern) 282 | if File.directory?(pattern) 283 | Dir.glob(File.join(pattern, '**', '*')) 284 | else 285 | Dir.glob(pattern) 286 | end 287 | end 288 | 289 | def gemify_version(version) 290 | case version 291 | when /^(.*?)\+$/ 292 | ">= #{$1}" 293 | when /^(.*?)\-$/ 294 | "< #{$1}" 295 | when /^(.*?)\~$/ 296 | "~> #{$1}" 297 | else 298 | version 299 | end 300 | end 301 | 302 | end 303 | 304 | end 305 | 306 | Indexer::GemspecExporter.gemspec -------------------------------------------------------------------------------- /lib/ae/legacy.rb: -------------------------------------------------------------------------------- 1 | module AE 2 | 3 | module Legacy #:nodoc: 4 | 5 | # Test::Unit Legacy Assertions 6 | # 7 | # This module provides a compatibility layer for Test::Unit. 8 | # This is an optional module and is intended for providing 9 | # an easier transition from Test::Unit to AE assertions. 10 | # 11 | # Note that two methods are not provided, +#assert_nothing_raised+, 12 | # and +#assert_nothing_thrown+. 13 | # 14 | module Assertions 15 | 16 | # Private method upon which all of the legacy assertions are based 17 | # (except for #assert itself). 18 | # 19 | # @raise [Assertion] If test fails. 20 | # 21 | # @return nothing 22 | def __assert__(test, msg=nil) 23 | msg = "failed assertion (no message given)" unless msg 24 | raise Assertion.new(msg, :backtrace=>caller[1..-1]) unless test 25 | end 26 | 27 | private :__assert__ 28 | 29 | # The assertion upon which all other assertions are based. 30 | # 31 | # @example 32 | # assert [1, 2].include?(5) 33 | # 34 | # @return [Assertor] if `test` not given 35 | def assert(test=nil, msg=nil) 36 | if test 37 | msg = "failed assertion (no message given)" unless msg 38 | raise Assertion.new(msg, :backtrace=>caller) unless test 39 | else 40 | Assertor.new(self, :backtrace=>caller) # TODO: Probably remove this! 41 | end 42 | end 43 | 44 | # Passes if the block yields true. 45 | # 46 | # @example 47 | # assert_block "Couldn't do the thing" do 48 | # do_the_thing 49 | # end 50 | # 51 | # @raise [Assertion] if test fails 52 | # 53 | # @return nothing 54 | def assert_block(msg=nil) # :yields: 55 | test = ! yield 56 | msg = "assertion failed" unless msg 57 | __assert__(test, msg) 58 | end 59 | 60 | # Passes if expected == +actual. 61 | # 62 | # Note that the ordering of arguments is important, 63 | # since a helpful error message is generated when this 64 | # one fails that tells you the values of expected and actual. 65 | # 66 | # @example 67 | # assert_equal 'MY STRING', 'my string'.upcase 68 | # 69 | # @raise [Assertion] if test fails 70 | # 71 | # @return nothing 72 | def assert_equal(exp, act, msg=nil) 73 | test = (exp == act) 74 | msg = "Expected #{act.inspect} to be equal to #{exp.inspect}" unless msg 75 | __assert__(test, msg) 76 | end 77 | 78 | # Passes if expected_float and actual_float are equal within delta tolerance. 79 | # 80 | # @example 81 | # assert_in_delta 0.05, (50000.0 / 10**6), 0.00001 82 | # 83 | # @raise [Assertion] if test fails 84 | # 85 | # @return nothing 86 | def assert_in_delta(exp, act, delta, msg=nil) 87 | test = (exp.to_f - act.to_f).abs <= delta.to_f 88 | msg = "Expected #{exp} to be within #{delta} of #{act}" unless msg 89 | __assert__(test, msg) 90 | end 91 | 92 | # Passes if object .instance_of? klass 93 | # 94 | # @example 95 | # assert_instance_of String, 'foo' 96 | # 97 | # @raise [Assertion] if test fails 98 | # 99 | # @return nothing 100 | def assert_instance_of(cls, obj, msg=nil) 101 | test = (cls === obj) 102 | msg = "Expected #{obj} to be a #{cls}" unless msg 103 | __assert__(test, msg) 104 | end 105 | 106 | # Passes if object .kind_of? klass 107 | # 108 | # @example 109 | # assert_kind_of Object, 'foo' 110 | # 111 | # @raise [Assertion] if test fails 112 | # 113 | # @return nothing 114 | def assert_kind_of(cls, obj, msg=nil) 115 | test = obj.kind_of?(cls) 116 | msg = "Expected #{obj.inspect} to be a kind of #{cls}" unless msg 117 | __assert__(test, msg) 118 | end 119 | 120 | # Passes if string =~ pattern. 121 | # 122 | # @example 123 | # assert_match(/\d+/, 'five, 6, seven') 124 | # 125 | # @raise [Assertion] if test fails 126 | # 127 | # @return nothing 128 | def assert_match(exp, act, msg=nil) 129 | test = (act =~ exp) 130 | msg = "Expected #{act.inspect} to match #{exp.inspect}" unless msg 131 | __assert__(test, msg) 132 | end 133 | 134 | # Passes if object is nil. 135 | # 136 | # @example 137 | # assert_nil [1, 2].uniq! 138 | # 139 | # @raise [Assertion] if test fails 140 | # 141 | # @return nothing 142 | def assert_nil(obj, msg=nil) 143 | test = obj.nil? 144 | msg = "Expected #{obj.inspect} to be nil" unless msg 145 | __assert__(test, msg) 146 | end 147 | 148 | # Passes if regexp !~ string 149 | # 150 | # @example 151 | # assert_no_match(/two/, 'one 2 three') 152 | # 153 | # @raise [Assertion] if test fails 154 | # 155 | # @return nothing 156 | def assert_no_match(exp, act, msg=nil) 157 | test = (act !~ exp) 158 | msg = "Expected #{act.inspect} to match #{exp.inspect}" unless msg 159 | __assert__(test, msg) 160 | end 161 | 162 | # Passes if expected != actual 163 | # 164 | # @example 165 | # assert_not_equal 'some string', 5 166 | # 167 | # @raise [Assertion] if test fails 168 | # 169 | # @return nothing 170 | def assert_not_equal(exp, act, msg=nil) 171 | test = (exp != act) 172 | msg = "Expected #{act.inspect} to not be equal to #{exp.inspect}" unless msg 173 | __assert__(test, msg) 174 | end 175 | 176 | # Passes if ! object .nil? 177 | # 178 | # @example 179 | # assert_not_nil '1 two 3'.sub!(/two/, '2') 180 | # 181 | # @raise [Assertion] if test fails 182 | # 183 | # @return nothing 184 | def assert_not_nil(obj, msg=nil) 185 | test = ! obj.nil? 186 | msg = "Expected #{obj.inspect} to not be nil" unless msg 187 | __assert__(test, msg) 188 | end 189 | 190 | # Passes if ! actual .equal? expected 191 | # 192 | # @example 193 | # assert_not_same Object.new, Object.new 194 | # 195 | # @raise [Assertion] if test fails 196 | # 197 | # @return nothing 198 | def assert_not_same(exp, act, msg=nil) 199 | test = ! exp.equal?(act) 200 | msg = "Expected #{act.inspect} to not be the same as #{exp.inspect}" unless msg 201 | __assert__(test, msg) 202 | end 203 | 204 | # Compares the +object1+ with +object2+ using operator. 205 | # 206 | # Passes if object1.send(operator, object2) is true. 207 | # 208 | # @example 209 | # assert_operator 5, :>=, 4 210 | # 211 | # @raise [Assertion] if test fails 212 | # 213 | # @return nothing 214 | def assert_operator(o1, op, o2, msg="") 215 | test = o1.__send__(op, o2) 216 | msg = "Expected #{o1}.#{op}(#{o2}) to be true" unless msg 217 | __assert__(test, msg) 218 | end 219 | 220 | # Passes if the block raises one of the given exceptions. 221 | # 222 | # @example 223 | # assert_raise RuntimeError, LoadError do 224 | # raise 'Boom!!!' 225 | # end 226 | # 227 | # @raise [Assertion] if test fails 228 | # 229 | # @return nothing 230 | def assert_raises(*args) 231 | msg = (Module === args.last ? nil : args.pop) 232 | begin 233 | yield 234 | msg = "Expected #{exp} to be raised" unless msg 235 | __assert__(false, msg) 236 | rescue Exception => e 237 | test = (exp === e) 238 | msg = "Expected #{exp} to be raised, but got #{e.class}" unless msg 239 | __assert__(test, msg) 240 | return e 241 | end 242 | end 243 | 244 | alias_method :assert_raise, :assert_raises 245 | 246 | # Provides a way to assert that a procedure 247 | # does not raise an exception. 248 | # 249 | # @example 250 | # refute_raises(StandardError){ raise } 251 | # 252 | #def assert_raises!(exception, &block) 253 | # begin 254 | # block.call(*a) 255 | # rescue exception 256 | # raise Assertion 257 | # end 258 | #end 259 | #alias_method :refute_raises, :assert_raises! 260 | 261 | # Passes if +object+ respond_to? +method+. 262 | # 263 | # @example 264 | # assert_respond_to 'bugbear', :slice 265 | # 266 | # @raise [Assertion] if test fails 267 | # 268 | # @return nothing 269 | def assert_respond_to(obj, meth, msg=nil) 270 | msg = "Expected #{obj} (#{obj.class}) to respond to ##{meth}" unless msg 271 | #flip = (Symbol === obj) && ! (Symbol === meth) # HACK for specs 272 | #obj, meth = meth, obj if flip 273 | test = obj.respond_to?(meth) 274 | __assert__(test, msg) 275 | end 276 | 277 | # Passes if +actual+ .equal? +expected+ (i.e. they are the same instance). 278 | # 279 | # @example 280 | # o = Object.new 281 | # assert_same(o, o) 282 | # 283 | # @raise [Assertion] if test fails 284 | # 285 | # @return nothing 286 | def assert_same(exp, act, msg=nil) 287 | msg = "Expected #{act.inspect} to be the same as #{exp.inspect}" unless msg 288 | test = exp.equal?(act) 289 | __assert__(test, msg) 290 | end 291 | 292 | # Passes if the method send returns a true value. 293 | # The parameter +send_array+ is composed of: 294 | # 295 | # * A receiver 296 | # * A method 297 | # * Arguments to the method 298 | # 299 | # @example 300 | # assert_send [[1, 2], :include?, 4] 301 | # 302 | # @raise [Assertion] if test fails 303 | # 304 | # @return nothing 305 | def assert_send(send_array, msg=nil) 306 | r, m, *args = *send_array 307 | test = r.__send__(m, *args) 308 | msg = "Expected #{r}.#{m}(*#{args.inspect}) to return true" unless msg 309 | __assert__(test, msg) 310 | end 311 | 312 | # Passes if the block throws expected_symbol 313 | # 314 | # @example 315 | # assert_throws :done do 316 | # throw :done 317 | # end 318 | # 319 | # @raise [Assertion] if test fails 320 | # 321 | # @return nothing 322 | def assert_throws(sym, msg=nil) 323 | msg = "Expected #{sym} to have been thrown" unless msg 324 | test = true 325 | catch(sym) do 326 | begin 327 | yield 328 | rescue ArgumentError => e # 1.9 exception 329 | default += ", not #{e.message.split(/ /).last}" 330 | rescue NameError => e # 1.8 exception 331 | default += ", not #{e.name.inspect}" 332 | end 333 | test = false 334 | end 335 | __assert__(test, msg) 336 | end 337 | 338 | # Assert that an Array, or any other object the responds to #include? 339 | # thus contains the given element. 340 | # 341 | # @raise [Assertion] if test fails 342 | # 343 | # @return nothing 344 | def assert_includes(elem, array, msg=nil) 345 | test = array.include?(elem) 346 | msg = "Expected #{elem.inspect} is not found in #{array.inspect}" unless msg 347 | __assert__(test, msg) 348 | end 349 | 350 | # Flunk always fails. 351 | # 352 | # @example 353 | # flunk 'Not done testing yet.' 354 | # 355 | # @raise [Assertion] always 356 | # 357 | # @return nothing 358 | def flunk(msg=nil) 359 | __assert__(false, msg) 360 | end 361 | 362 | end #module Assertions 363 | 364 | end #module Legacy 365 | 366 | module World 367 | include AE::Legacy::Assertions 368 | end 369 | 370 | end 371 | 372 | 373 | -------------------------------------------------------------------------------- /lib/ae/assertor.rb: -------------------------------------------------------------------------------- 1 | require 'ae/assertion' 2 | require 'ae/basic_object' 3 | require 'ae/ansi' 4 | 5 | module AE 6 | 7 | # Assertor is the underlying class of the whole system. It implements 8 | # the flutent assertion notation. 9 | # 10 | # An Assertor is an Assertion Functor. A Functor is a succinct name for what 11 | # is also known as Higher Order Function. In other words, it is a function 12 | # that acts on a function. It is very similiar to a delegator in most 13 | # respects, but is conditioned on the operation applied, rather then simply 14 | # passing-off to an alternate reciever. 15 | # 16 | class Assertor < AE::BasicObject 17 | 18 | # Initial settings of assertion counts. 19 | ZERO_COUNTS = {:total=>0,:pass=>0,:fail=>0} 20 | 21 | # Initialize assertion counts global variable. 22 | $ASSERTION_COUNTS = ZERO_COUNTS.dup 23 | 24 | # Returns Hash used to track assertion counts. 25 | def self.counts 26 | $ASSERTION_COUNTS 27 | end 28 | 29 | # Reset assertion counts. 30 | # 31 | # reset - Hash which can be used to set counts manually (optional). 32 | # 33 | # Returns the Hash of previous counts. 34 | def self.recount(reset=nil) 35 | old_counts = counts.dup 36 | if reset 37 | reset.each do |type, value| 38 | counts[type.to_sym] = value 39 | end 40 | else 41 | counts.replace(ZERO_COUNTS.dup) 42 | end 43 | return old_counts 44 | end 45 | 46 | # Increment assertion counts. If +pass+ is +true+ then +:total+ 47 | # and +:pass+ are increased. If +pass+ if +false+ then +:total+ 48 | # and +:fail+ are incremented. 49 | def self.increment_counts(pass) 50 | counts[:total] += 1 51 | if pass 52 | counts[:pass] += 1 53 | else 54 | counts[:fail] += 1 55 | end 56 | return counts 57 | end 58 | 59 | # Basic assertion. This method by-passes all the Assertor fluent 60 | # constructs and performs the underlying assertion procedure. It 61 | # is used by Assertor as the end call of an assertion. 62 | def self.assert(pass, error=nil, negated=nil, backtrace=nil) 63 | pass = negated ^ !!pass 64 | increment_counts(pass) 65 | if !pass 66 | backtrace = backtrace || caller 67 | raise_assertion(error, negated, backtrace) 68 | end 69 | return pass 70 | end 71 | 72 | # The intent of the method is to raise an assertion failure 73 | # class that the test framework supports. 74 | def self.raise_assertion(error, negated, backtrace=nil) 75 | if not ::Exception === error 76 | error = assertion_error.new(error) 77 | end 78 | error.set_negative(negated) 79 | error.set_backtrace(backtrace || caller) 80 | error.set_assertion(true) 81 | fail error 82 | end 83 | 84 | # Returns the Exception class to be raised when an assertion fails. 85 | def self.assertion_error 86 | ::Assertion 87 | end 88 | 89 | # NOT HAPPENING 90 | ## Is ::Assay defined. This is used for integration of the Assay library. 91 | #def self.assay? 92 | # @_assay ||= defined?(::Assay) 93 | #end 94 | # 95 | #def self.message(sym, neg, *args, &blk) 96 | # if method = Message.lookup(sym) 97 | # method = "non_#{method}" if neg 98 | # Message.send(method, *args, &blk) 99 | # else 100 | # nil 101 | # end 102 | #end 103 | 104 | # 105 | if ::RUBY_VERSION >= '1.9' 106 | eval "private :==, :!, :!=" # using eval here b/c it's a syntax error in 1.8- 107 | end 108 | 109 | # New Assertor. 110 | # 111 | def initialize(delegate, opts={}) #, backtrace) 112 | @delegate = delegate 113 | @message = opts[:message] 114 | @backtrace = opts[:backtrace] || caller #[1..-1] 115 | @negated = !!opts[:negated] 116 | end 117 | 118 | # TODO: Should #not return a new Assertor instead of in place negation? 119 | 120 | # Negate the meaning of the assertion. 121 | # 122 | def not(msg=nil) 123 | @negated = !@negated 124 | @message = msg if msg 125 | self 126 | end 127 | 128 | # Internal assert, provides all functionality associated 129 | # with external #assert method. (See Assert#assert) 130 | # 131 | # NOTE: I'm calling YAGNI on using extra arguments to pass 132 | # to the block. The interface is much nicer if a macro is 133 | # created to handle any neccessry arguments. Eg. 134 | # 135 | # assert something(parameter) 136 | # 137 | # instead of 138 | # 139 | # assert something, parameter 140 | # 141 | # Returns +true+ or +false+ based on assertions success. 142 | # 143 | def assert(*args, &block) 144 | return self if !block && args.empty? 145 | 146 | target = args.shift unless block 147 | error = nil 148 | 149 | # Block 150 | if block 151 | match = args.shift 152 | result = block.arity > 0 ? block.call(@delegate) : block.call 153 | if match 154 | pass = (match == result) 155 | error = @message || "#{match.inspect} == #{result.inspect}" 156 | else 157 | pass = result 158 | error = @message || block.inspect # "#{result.inspect}" 159 | end 160 | 161 | # Proc-style 162 | elsif proc_assertion?(target) 163 | pass, error = proc_apply(target) 164 | 165 | # Assay-style assertions 166 | #elsif assay_assertion?(target) 167 | # pass, error = assay_assertion_apply(target) 168 | 169 | # RSpec-style matchers 170 | elsif rspec_matcher?(target) 171 | pass, error = rspec_matcher_apply(target) 172 | 173 | # Truthiness 174 | else 175 | pass = target # truthiness 176 | error = args.shift # optional message for TestUnit compatiability 177 | end 178 | 179 | __assert__(pass, error) 180 | end 181 | 182 | # TODO: Should we deprecate the receiver matches in favor of #expected ? 183 | # In other words, should || @delegate be dropped? 184 | 185 | # Internal expect, provides all functionality associated 186 | # with external #expect method. (See Expect#expect) 187 | # 188 | def expect(*args, &block) 189 | return self if !block && args.empty? # same as #assert 190 | 191 | pass = false 192 | error = nil 193 | 194 | if block 195 | match = args.shift || @delegate # TODO: see above 196 | if exception?(match) 197 | $DEBUG, debug = false, $DEBUG # b/c it always spits-out a NameError 198 | begin 199 | block.arity > 0 ? block.call(@delegate) : block.call 200 | pass = false 201 | error = "#{match} not raised" 202 | rescue match => error 203 | pass = true 204 | error = "#{match} raised" 205 | rescue ::Exception => error 206 | pass = false 207 | error = "#{match} expected but #{error.class} was raised" 208 | ensure 209 | $DEBUG = debug 210 | end 211 | else 212 | result = block.arity > 0 ? block.call(@delegte) : block.call 213 | pass = (match === result) 214 | error = @message || "#{match.inspect} === #{result.inspect}" 215 | end 216 | 217 | ## Matcher 218 | #elsif target.respond_to?(:matches?) 219 | # pass = target.matches?(@delegate) 220 | # error = @message || matcher_message(target) #|| target.inspect 221 | # if target.respond_to?(:exception) 222 | # #error_class = target.failure_class 223 | # error = target.exception #failure(:backtrace=>@backtrace, :negated=>@negated) 224 | # end 225 | 226 | # Case Equality 227 | else 228 | target = args.shift 229 | pass = (target === @delegate) 230 | error = @message || "#{target.inspect} === #{@delegate.inspect}" 231 | end 232 | 233 | __assert__(pass, error) 234 | end 235 | 236 | # 237 | def flunk(message=nil, backtrace=nil) 238 | __assert__(false, message || @message) 239 | end 240 | 241 | # Ruby seems to have a quark in it's implementation whereby 242 | # this must be defined explicitly, otherwise it somehow 243 | # skips #method_missing. 244 | def =~(match) 245 | method_missing(:"=~", match) 246 | end 247 | 248 | # 249 | def send(op, *a, &b) 250 | method_missing(op, *a, &b) 251 | end 252 | 253 | # 254 | def inspect 255 | @delegate.inspect 256 | end 257 | 258 | private 259 | 260 | # AE-STYLE ASSERTIONS 261 | 262 | # 263 | def proc_assertion?(target) 264 | ::Proc === target || target.respond_to?(:call) || target.respond_to?(:to_proc) 265 | end 266 | 267 | # 268 | def proc_apply(target) 269 | call = target.method(:call) rescue target.to_proc 270 | pass = call.arity != 0 ? call.call(@delegate) : call.call 271 | error = @message || ( 272 | to_s = target.method(:to_s) 273 | to_s.arity == 0 ? to_s.call : to_s.call(@negated) 274 | ) 275 | return pass, error 276 | end 277 | 278 | # ASSAY-STYLE ASSERTIONS 279 | # (not yet supported b/c api is not 100%) 280 | 281 | # Is the `assertion` object an assay-style assertion? 282 | def assay_assertion?(assertion) 283 | assertion.respond_to?(:exception) && assertion.respond_to?(:pass?) 284 | end 285 | 286 | # 287 | def assay_assertion_apply(assay) 288 | if @negated 289 | pass = assay.fail?(@delegate) 290 | error = assay #.exception(@message || ) 291 | else 292 | pass = assay.pass?(@delegate) 293 | error = assay #.exception(@message || ) 294 | end 295 | return pass, error 296 | end 297 | 298 | # RSPEC-STYLE MATCHERS 299 | 300 | # Is `target` an Rspec-style Matcher? 301 | def rspec_matcher?(target) 302 | target.respond_to?(:matches?) 303 | end 304 | 305 | # 306 | def rspec_matcher_apply(matcher) 307 | pass = matcher.matches?(@delegate) 308 | error = @message || rspec_matcher_message(matcher) 309 | return pass, error 310 | end 311 | 312 | # TODO: Is there anything to be done with matcher.description? 313 | 314 | # 315 | def rspec_matcher_message(matcher) 316 | if @negated 317 | if matcher.respond_to?(:failure_message_for_should_not) 318 | return matcher.failure_message_for_should_not 319 | end 320 | if matcher.respond_to?(:negative_failure_message) 321 | return matcher.negative_failure_message 322 | end 323 | end 324 | 325 | if matcher.respond_to?(:failure_message_for_should) 326 | return matcher.failure_message_for_should 327 | end 328 | if matcher.respond_to?(:failure_message) 329 | return matcher.failure_message 330 | end 331 | 332 | return matcher.to_s # TODO: or just `nil` ? 333 | end 334 | 335 | # TODO: Should we use a more libreral determination of exception. 336 | # e.g. respond_to?(:exception). 337 | 338 | # Is the +object+ an Exception or an instance of one? 339 | def exception?(object) 340 | ::Exception === object or ::Class === object and object.ancestors.include?(::Exception) 341 | end 342 | 343 | # TODO: In future should probably be `@delegate.public_send(sym, *a, &b)`. 344 | 345 | # Converts a missing method into an Assertion. 346 | def method_missing(sym, *args, &block) 347 | error = @message || compare_message(sym, *args, &block) || generic_message(sym, *args, &block) 348 | 349 | pass = @delegate.__send__(sym, *args, &block) 350 | 351 | __assert__(pass, error) 352 | end 353 | 354 | # TODO: Can the handling of the message be simplified/improved? 355 | 356 | # Simple assert. 357 | def __assert__(pass, error=nil) 358 | Assertor.assert(pass, error, @negated, @backtrace) 359 | end 360 | 361 | # 362 | COMPARISON_OPERATORS = { :"==" => :"!=" } 363 | 364 | # Message to use when making a comparion assertion. 365 | # 366 | # NOTE: This message utilizes the ANSI gem to produce colorized 367 | # comparisons. If you need to remove color output (for non-ANSI 368 | # terminals) you can either set `AE.ansi = false` or use the 369 | # ANSI library's master switch to deactive all ANSI codes, 370 | # which can be set in your test helper. 371 | # 372 | # @param operator [Symbol] operator/method 373 | # 374 | # @see http://rubyworks.github.com/ansi 375 | def compare_message(operator, *args, &blk) 376 | return nil unless COMPARISON_OPERATORS.key?(operator) 377 | prefix = "" 378 | a, b = @delegate.inspect, args.first.inspect 379 | if @negated 380 | op = COMPARISON_OPERATORS[operator] 381 | if op 382 | operator = op 383 | else 384 | prefix = "NOT " 385 | end 386 | end 387 | if AE.ansi? 388 | diff = ::ANSI::Diff.new(a,b) 389 | a = diff.diff1 390 | b = diff.diff2 391 | end 392 | if a.size > 13 or b.size > 13 393 | prefix + "a #{operator} b\na) " + a + "\nb) " + b 394 | else 395 | prefix + "#{a} #{operator} #{b}" 396 | end 397 | end 398 | 399 | # Puts together a suitable error message. 400 | # 401 | # @param op [Symbol] operator/method 402 | # 403 | # @return [String] message 404 | def generic_message(op, *a, &b) 405 | inspection = @delegate.send(:inspect) 406 | if @negated 407 | "! #{inspection} #{op} #{a.collect{|x| x.inspect}.join(',')}" 408 | else 409 | "#{inspection} #{op} #{a.collect{|x| x.inspect}.join(',')}" 410 | end 411 | #self.class.message(m)[@delegate, *a] ) 412 | end 413 | 414 | # @see http://redmine.ruby-lang.org/issues/3768 415 | def self.const_missing(const) 416 | ::Object.const_get(const) 417 | end 418 | end 419 | 420 | end 421 | 422 | # DO WE MAKE THESE EXCEPTIONS? 423 | #class BasicObject 424 | # def assert 425 | # end 426 | #end 427 | 428 | # Copyright (c) 2008 Thomas Sawyer 429 | -------------------------------------------------------------------------------- /DEMO.rdoc: -------------------------------------------------------------------------------- 1 | = Introduction 2 | 3 | AE is an assertions framework for Ruby. It's designed 4 | around the concept of an Assertor. The Assertor is an 5 | Assertion Functor, or Higher-Order Function, which 6 | reroutes method calls while monitoring them for failing 7 | conditions. 8 | 9 | 10 | == What AE Provides 11 | 12 | Requiring the AE library. 13 | 14 | require 'ae' 15 | 16 | Loads two classes, +Assertion+ and +Assertor+, the Kernel 17 | method +assert+ and it's antonyms +assert!+ and +refute+ 18 | and a set of core extensions that make writing certain types 19 | of assertions easier. 20 | 21 | 22 | == Assertion and Assertor Classes 23 | 24 | The +Assertion+ class is at the heart of AE. All other AE 25 | methods depend on it. The +Assertion+ class is a subclass 26 | of Exception. When an assertion is made and fails, it is 27 | an instance of Assertion that is raised. 28 | 29 | expect Assertion do 30 | msg = "my failure message" 31 | assert false, msg 32 | end 33 | 34 | Like any raised exception, the last Assertion message is available 35 | via $!. 36 | 37 | (FYI, in Test::Unit the equivalent class was called +AssertionFailedError+.) 38 | 39 | Assertions themselves are not generally used in creating tests or 40 | behavior specifications. Rather they are used to create additional 41 | types of assertion methods. 42 | 43 | As mentioned above the +Assertor+ class is a type of Higher-Order 44 | function, or Functor, which intercedes with a normal message 45 | invocation to monitor for failed conditions, upon which is raises 46 | Assertion exceptions. 47 | 48 | 49 | == Assertion Methods 50 | 51 | The three methods, +assert+, assert! and +refute+ all 52 | return an Assertor instance when used fluidly, i.e. magic-dot 53 | notation, higher-order notation, functor notation, whatever you 54 | prefer to call it. 55 | 56 | assert(AE::Assertor === assert) 57 | 58 | Through the use of +method_missing+, the Assertor allows us to write 59 | statements like: 60 | 61 | 1.assert == 1 62 | 63 | If the operation evaluates to false or nil, then an Assertion error 64 | is raised. 65 | 66 | expect Assertion do 67 | 1.assert == 2 68 | end 69 | 70 | The methods assert! and +refute+ are just like +assert+ 71 | expect they purport the negative condition. Patterned after Ruby's 72 | own use of "!" as meaning +not+, assert! should be 73 | read "assert not". While +refute+ exists for the sake of those who 74 | find the use of a bang method for this purpose unsuited to them. 75 | 76 | 77 | == How It Works 78 | 79 | An Assertor essentially sits in wait for a method call (via 80 | method_missing). When that happens it applies the method to the 81 | original receiver, but wrapped in a clause that raises an 82 | Assertion should the statement fail. If we wanted to be 83 | pedantic, we could write our assertions like: 84 | 85 | raise Assertion.new("1 != 1") unless 1 == 1 86 | 87 | Instead of 88 | 89 | 1.assert == 1 90 | 91 | Obviously using Assertor methods are whole lot more concise. 92 | 93 | 94 | = Assertion Class 95 | 96 | The Assertion class is a subclass of Exception and is the error raised when 97 | and assertion fails. 98 | 99 | = Assert Method 100 | 101 | == Compatible with Test::Unit 102 | 103 | The +assert+ method is designed to be backward compatible 104 | with the same method in Test::Unit. 105 | 106 | Using an argument, +assert+ will check that an argument evaluates 107 | to true. Optionally one can send along a meaningful message should 108 | the assertion fail. 109 | 110 | assert(true, "Not true!") 111 | 112 | expect Assertion do 113 | assert(false, "Not true!") 114 | end 115 | 116 | 117 | == Assert with a Block 118 | 119 | In addition +assert+ has been extended to accept a block. Like the case of the 120 | argument, the block is expected to return something that evaluates as true. 121 | 122 | assert do 123 | true 124 | end 125 | 126 | Assertion.assert.raised? do 127 | assert do 128 | false 129 | end 130 | end 131 | 132 | We should also mention that, while probably not very useful, since 133 | the arity of a block can be checked, one can also pass the receiver 134 | into the block as a block argument. 135 | 136 | "hi".assert do |s| 137 | /h/ =~ s 138 | end 139 | 140 | 141 | == Antonyms for Assert 142 | 143 | We can state the opposite assertion using assert!. 144 | 145 | 10.assert! == 9 146 | 147 | Or, because some people do not like the use of a bang method, +refute+. 148 | 149 | 10.refute == 9 150 | 151 | These terms can be used just as +assert+ is used in all examples, 152 | but with the opposite inference. 153 | 154 | Another way to get the opposite inference, is to use +not+. 155 | 156 | 10.assert.not == 9 157 | 158 | == Lambda Assertions 159 | 160 | Passing +assert+ a `Proc` object, or any object that responds to `#call`, 161 | will be used as if it were a block. This allows for a simple way to quickly 162 | create reusable assertions. 163 | 164 | palindrome = lambda{ |word| word == word.reverse } 165 | 166 | "abracarba".assert palindrome 167 | 168 | The message for a failed assertion will come from calling `#to_s` on the 169 | object. 170 | 171 | == RSpec-style Assertion Matchers 172 | 173 | If an object passed to assert responds to `#matches?` then AE will handle 174 | the object as an RSpec-style mather, the receiver will be passed to the 175 | `#matches?` method to determine if the assertion passes and RSpec matcher 176 | message methods will be used if they are defined. 177 | 178 | palindrome = Object.new 179 | 180 | def palindrome.matches?(word) 181 | word == word.reverse 182 | end 183 | 184 | "abracarba".assert palindrome 185 | 186 | 187 | == Identity Assertions 188 | 189 | Rather then the general form. 190 | 191 | x = 10 192 | x.assert.object_id == x.object_id 193 | 194 | We can use Ruby's own equal? method. 195 | 196 | x.assert.equal?(x) 197 | 198 | AE provides identical? method as an alternative 199 | to make it a bit more clear. 200 | 201 | x.assert.identical?(x) 202 | 203 | 204 | == Equality Assertions 205 | 206 | The most common assertion is that of value equality (==), 207 | as we have seen throughout this document. But other forms of 208 | equality can be verified as easily. We have already mentioned 209 | identity. In addition there is type equality. 210 | 211 | 17.assert.eql? 17 212 | 213 | Assertion.assert.raised? do 214 | 17.assert.eql? 17.0 215 | end 216 | 217 | And there is case equality. 218 | 219 | Numeric.assert === 3 220 | 221 | 222 | == Checking Equality with a Block 223 | 224 | Because operators can not take blocks, and at times blocks can 225 | be convenient means of supplying a value to an assertion, 226 | AE has defined alternate renditions of the equality methods. 227 | For equal? and eql?, the method names are the same, they simply 228 | can take a block in place of an argument if need be. 229 | 230 | For value equality (==), the method is called eq?. 231 | 232 | 10.assert.eq? do 233 | 10.0 234 | end 235 | 236 | And should it fail... 237 | 238 | Assertion.assert.raised? do 239 | 10.assert.eq? do 240 | 20 241 | end 242 | end 243 | 244 | == Case Equality 245 | 246 | For case equality (===), it is case?. 247 | 248 | Numeric.assert.case? do 249 | "3".to_i 250 | end 251 | 252 | Assertion.assert.raised? do 253 | Numeric.assert.case? do 254 | "3" 255 | end 256 | end 257 | 258 | 259 | == Regular Expressions 260 | 261 | Regular Expressions can be used to make assertions in much the same way as equality. 262 | 263 | /i/.assert =~ "i" 264 | 265 | Assertion.assert.raised? do 266 | /i/.assert =~ "g" 267 | end 268 | 269 | Conversely the String class recognizes the #=~ method as well. 270 | 271 | "i".assert =~ /i/ 272 | 273 | Assertion.assert.raised? do 274 | "i".assert =~ /g/ 275 | end 276 | 277 | 278 | == Exception Assertions 279 | 280 | Validating errors is easy too, as has already been shown 281 | in the document to verify assertion failures. 282 | 283 | StandardError.assert.raised? do 284 | unknown_method 285 | end 286 | 287 | 288 | == Assertions on Object State 289 | 290 | While testing or specifying the internal state of an object is 291 | generally considered poor form, there are times when it is 292 | necessary. Assert combined with +instance_eval+ makes it easy too. 293 | 294 | class X 295 | attr :a 296 | def initialize(a); @a = a; end 297 | end 298 | 299 | x = X.new(1) 300 | 301 | x.assert.instance_eval do 302 | @a == 1 303 | end 304 | 305 | 306 | == Catch/Try Assertions 307 | 308 | Catch/Try throws can be tested via Symbol#thrown?. 309 | 310 | :hookme.assert.thrown? do 311 | throw :hookme 312 | end 313 | 314 | Alternatively, a lambda containing the potential throw 315 | can be the receiver using throws?. 316 | 317 | hook = lambda{ throw :hookme } 318 | 319 | hook.assert.throws?(:hookme) 320 | 321 | 322 | == Assertions on Proc Changes 323 | 324 | I have to admit I'm not sure how this is useful, 325 | but I found it in the Bacon API and ported it over 326 | just for sake of thoroughness. 327 | 328 | a = 0 329 | 330 | l = lambda{ a } 331 | 332 | l.assert.change?{ a +=1 } 333 | 334 | 335 | == Assertion on literal True, False and Nil 336 | 337 | Ruby already provides the #nil? method. 338 | 339 | nil.assert.nil? 340 | 341 | AE adds true? and false? which acts accordingly. 342 | 343 | true.assert.true? 344 | false.assert.false? 345 | 346 | 347 | == Send Assertions 348 | 349 | Assert that a method can be successfully called. 350 | 351 | "STRING".assert.send?(:upcase) 352 | 353 | 354 | == Numeric Delta and Epsilon 355 | 356 | You may wish to assert that a numeric value is with some 357 | range. 358 | 359 | 3.in_delta?(1,5) 360 | 361 | Or minimum range. 362 | 363 | 3.in_epsilon?(3,5) 364 | 365 | 366 | == Verifying Object State 367 | 368 | Not surprisingly if underlying object state needs to be verified, +instance_eval+ 369 | can be used in conjunction with +assert+. 370 | 371 | class X 372 | attr :a 373 | def initialize(a); @a = a; end 374 | end 375 | 376 | x = X.new(4) 377 | 378 | x.instance_eval do 379 | @a.assert == 4 380 | end 381 | 382 | However #instance_eval is a reserved method for the underlying Assertor class, 383 | so it cannot be used on #assert, e.g. 384 | 385 | x.assert.instance_eval do 386 | @a == "obvisouly wrong" 387 | end 388 | 389 | AE offers an optional helper method for times when testing underlying private 390 | or protected methods is important, called #pry. See the QED on pry for more 391 | information. 392 | 393 | For some testing underlying implementation might be considered poor 394 | form. You will get no argument here. It should be used thoughtfully, 395 | but I would not bet against there being occasions when such validations 396 | might be needed. 397 | 398 | = Subjunctives 399 | 400 | Okay. I can hear the BDDers rumbling, "where's the *should?*" 401 | AE has nothing against "should", but there are different 402 | approaches for utilizing should nomenclature in specifications, 403 | and AE wants to be open to these techniques. One of which 404 | is how Shoulda (http://shoulda.rubyforge.org) utilizes 405 | +should+ in a way analogous to RSpec's use of +it+. 406 | 407 | Even so, AE provides an optional mixin called +Subjunctive+ which 408 | can be used to create assertor methods with English subjunctive 409 | terms, such as +should+, or +must+, +shall+ and +will+. 410 | To load this library use: 411 | 412 | require 'ae/subjunctive' 413 | 414 | Then all that is required it to define a subjunctive method for all 415 | objects. For example: 416 | 417 | def will(*args, &block) 418 | Assertor.new(self, :backtrace=>caller).be(*args,&block) 419 | end 420 | 421 | It's that easy. Because of their commonality AE provides two such terms, 422 | +should+ and +must+ as optional add-ons out-of-the-box. 423 | 424 | require 'ae/should' 425 | require 'ae/must' 426 | 427 | We will use these two methods interchangeable for the rest of this 428 | demonstration, but to be clear they both work exactly the same way, 429 | and almost exactly like +assert+. 430 | 431 | Keep in mind, AE "conical" functionality does not entail the subjunctive 432 | forms. These are simply options you can load via your test_helper.rb, 433 | or similar script, if you prefer these nomenclatures. 434 | 435 | 436 | == Fluent Notation and Antonyms 437 | 438 | Like +assert+, +should+ and +must+ can be used as higher order functions. 439 | 440 | 4.should == 4 441 | 4.must == 4 442 | 443 | Antonyms provided for +should+ as should! (read "should not") and +shouldnt+. 444 | For +must+ +must+, they are must! and +wont+. 445 | 446 | 4.should! == 5 447 | 4.shouldnt == 5 448 | 449 | 4.must! == 5 450 | 4.wont == 5 451 | 452 | 453 | == To Be 454 | 455 | On occasions where the English readability of a specification is hindered, 456 | +be+ can be used. 457 | 458 | StandardError.must.be.raised? do 459 | unknown_method 460 | end 461 | 462 | The +be+ method is the same as +assert+ with the single exception 463 | that it will compare a lone argument to the receiver using +equate?+, 464 | unlike +assert+ which simply checks to see that the argument evaluates 465 | as true. 466 | 467 | 10.should.be 10 468 | 10.should.be 10.0 469 | 10.should.be Numeric 470 | 471 | Assertion.assert.raised? do 472 | 10.should.be "40" 473 | end 474 | 475 | 476 | == Indefinite Articles 477 | 478 | Additional English forms are +a+ and +an+, equivalent to +be+ except 479 | that they use case? (same as #===) instead of 480 | equate? when acting on a single argument. 481 | 482 | "hi".must.be.a String 483 | 484 | Assertion.assert.raised? do 485 | /x/.must.be.a /x/ 486 | end 487 | 488 | Otherwise they are interchangeable. 489 | 490 | "hi".must.be.an.instance_of?(String) 491 | 492 | The indefinite articles work well when a noun follows as an arguments. 493 | 494 | palindrome = lambda{ |x| x == x.reverse } 495 | 496 | "abracarba".must.be.a palindrome 497 | 498 | 499 | = Expect Method 500 | 501 | Expect is another assertion nomenclature available for use in your 502 | tests or specifications. Inspired by Jay Fields' Expectations library, 503 | it provides convenient syntax for creating exception and case equality 504 | assertions. 505 | 506 | require 'ae/expect' 507 | 508 | 509 | == Underlying Comparison 510 | 511 | Expect uses #=== for comparison. So providing an argument and a block to 512 | #expect we can test for a somewhat broader range of compassion than #assert. 513 | For example we can test for a subclass. 514 | 515 | expect Numeric do 516 | 3 517 | end 518 | 519 | Assertion.assert.raised? do 520 | expect Numeric do 521 | "3" 522 | end 523 | end 524 | 525 | 526 | == Exception Expectation 527 | 528 | If the comparator is an Exception class or a instance of an Exception class, 529 | then #expect will check to see if the block raises that kind of exception. 530 | 531 | expect StandardError do 532 | some_undefined_method 533 | end 534 | 535 | expect Assertion do 536 | expect(nil) 537 | end 538 | 539 | This is an important distinction to note because it means #expect can not be used 540 | if verify instances of Exception classes. 541 | 542 | Assertion.assert.raised? do 543 | expect Exception do 544 | Exception.new 545 | end 546 | end 547 | 548 | 549 | == Regex Expectations 550 | 551 | That #expect entails #=== also means we can check for Regexp matches. 552 | 553 | expect /x/ do 554 | "oooxooo" 555 | end 556 | 557 | 558 | == Expected Method 559 | 560 | We can use #expected to make the receiver the object of expectation. 561 | 562 | x = "dummy" 563 | 564 | /x/.expected do 565 | "x" 566 | end 567 | 568 | 569 | == Without Block 570 | 571 | Without a block, the receiver is compared to the argument. 572 | 573 | x.expect String 574 | 575 | 576 | == Negative Forms 577 | 578 | Like #assert, #expect has a negated form called #expect! 579 | 580 | expect! /x/ do 581 | "o" 582 | end 583 | 584 | The pure word form for those who do not like the clever use of the 585 | explimation mark is #forbid. 586 | 587 | forbid /x/ do 588 | "o" 589 | end 590 | 591 | 592 | == Functor, or Higher Order Function 593 | 594 | Like #assert, #expect can be used used as a *fluid* notation. 595 | 596 | 10.expect == 10 597 | 598 | In which case it works just like #assert, including negative forms. 599 | 600 | 10.expect! == 11 601 | 10.forbid == 11 602 | 603 | 604 | = Assertion Counts 605 | 606 | AE tracks the number of assertions made and the number that failed to pass. 607 | We can reset the count using the +recount+ class method. 608 | 609 | old_counts = AE::Assertor.recount 610 | 611 | For example if we have one assertion pass and another fail. 612 | 613 | assert(true) 614 | 615 | expect Assertion do 616 | assert(false) 617 | end 618 | 619 | We will see that AE counted three assertions and one failure. 620 | 621 | counts = AE::Assertor.counts.dup 622 | 623 | counts[:total].assert == 3 624 | counts[:pass].assert == 2 625 | counts[:fail].assert == 1 626 | 627 | The #expect call is an assertion too, which is why the total count is 3 628 | rather than 2. 629 | 630 | Now that we are done checking counts we will restore them so that 631 | any other demos being run with this will tally correctly. 632 | 633 | AE::Assertor.recount(old_counts) 634 | AE::Assertor.counts[:total] += 3 635 | AE::Assertor.counts[:pass] += 3 636 | 637 | 638 | = Matchers 639 | 640 | Matchers are simply Procs or objects with the proper interface that can be 641 | passed to #assert or #refute (or other Assertor) as an ecapsulated test. 642 | 643 | == Proc or #to_proc 644 | 645 | Passing a Proc object or an object that responds to :to_proc, will use it 646 | as if it were a block of the method. This allows for a simple way to quickly 647 | create reusable assertions. 648 | 649 | palindrome = lambda{ |word| word == word.reverse } 650 | 651 | "abracarba".assert palindrome 652 | 653 | == #matches? 654 | 655 | Additionally if an object responds to #matches? then the receiver 656 | will be passed to this method to determine if the assertion passes. 657 | 658 | palindrome = Object.new 659 | 660 | def palindrome.matches?(word) 661 | word == word.reverse 662 | end 663 | 664 | "abracarba".assert palindrome 665 | 666 | == RSpec, Shoulda and other 3rd-Party Matchers 667 | 668 | With tha addition of #matches?, AE supports the same interface for matchers 669 | as RSpec. Any matcher library designed for use with RSpec should therefore 670 | be usable with AE as well. This includes RSpecs own matchers and Shoulda's 671 | excellent Rails matchers. 672 | 673 | == Check Ok/No 674 | 675 | The Check library is an optional library that can be used 676 | to conveniently speed-up construction of reptitive assertions. 677 | 678 | To use it, first require the library, then include the mixin 679 | into the namespace in which you will utilize it. 680 | 681 | require 'ae/check' 682 | 683 | include AE::Check 684 | 685 | Now we can define ok/no check procedures. A one-off procedure is 686 | defined with a block only. 687 | 688 | check do |x, y| 689 | x == y 690 | end 691 | 692 | ok 1,1 693 | 694 | no 1,2 695 | 696 | To define reusable check procedures, give the procedure a name. 697 | 698 | check :palindrome do |x| 699 | x.reverse == x 700 | end 701 | 702 | This will also cause the current check method to be set. 703 | Later in the code, the check procedure can be reset to this 704 | by just passing the name. 705 | 706 | check :palindrome 707 | 708 | ok 'abracarba' 709 | 710 | no 'foolishness' 711 | 712 | The Check mixin comes preloaded with a few standard checks. 713 | By default the `:equality` procedure is used. 714 | 715 | check :equality 716 | 717 | ok 1=>1.0 718 | 719 | Notice the use of the hash argument here. This is a useful construct for 720 | many check procedures becuase it it akin to the `#=>` pattern we often 721 | see in code examples and it also allows for multiple assertions in one call. 722 | For instance, in the case of `:equality`, multiple entries convey a 723 | meaning of logical-or. 724 | 725 | ok 1=>2, 1=>1 726 | 727 | This would pass becuase the second assertion of equality is true. 728 | 729 | Another built in check is `:case_equality` which uses `#===` instead of `#==` 730 | to make the comparison. 731 | 732 | check :case_equality 733 | 734 | ok 1=>Integer 735 | 736 | 737 | --------------------------------------------------------------------------------