File: macros.rb

package info (click to toggle)
libshoulda-ruby 2.10.3-1
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 880 kB
  • ctags: 726
  • sloc: ruby: 5,764; makefile: 6
file content (133 lines) | stat: -rw-r--r-- 5,016 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
require 'shoulda/private_helpers'

module Shoulda # :nodoc:
  module Macros
    # Macro that creates a test asserting a change between the return value
    # of a block that is run before and after the current setup block
    # is run. This is similar to Active Support's <tt>assert_difference</tt>
    # assertion, but supports more than just numeric values.  See also
    # should_not_change.
    #
    # The passed description will be used when generating the test name and failure messages.
    #
    # Example:
    #
    #   context "Creating a post" do
    #     setup { Post.create }
    #     should_change("the number of posts", :by => 1) { Post.count }
    #   end
    #
    # As shown in this example, the <tt>:by</tt> option expects a numeric
    # difference between the before and after values of the expression.  You
    # may also specify <tt>:from</tt> and <tt>:to</tt> options:
    #
    #   should_change("the number of posts", :from => 0, :to => 1) { Post.count }
    #   should_change("the post title", :from => "old", :to => "new") { @post.title }
    #
    # Combinations of <tt>:by</tt>, <tt>:from</tt>, and <tt>:to</tt> are allowed:
    #
    #   # Assert the value changed in some way:
    #   should_change("the post title") { @post.title }
    #
    #   # Assert the value changed to anything other than "old:"
    #   should_change("the post title", :from => "old") { @post.title }
    #
    #   # Assert the value changed to "new:"
    #   should_change("the post title", :to => "new") { @post.title }
    def should_change(description, options = {}, &block)
      by, from, to = get_options!([options], :by, :from, :to)
      stmt = "change #{description}"
      stmt << " from #{from.inspect}" if from
      stmt << " to #{to.inspect}" if to
      stmt << " by #{by.inspect}" if by

      if block_given?
        code = block
      else
        warn "[DEPRECATION] should_change(expression, options) is deprecated. " <<
             "Use should_change(description, options) { code } instead."
        code = lambda { eval(description) }
      end
      before = lambda { @_before_should_change = code.bind(self).call }
      should stmt, :before => before do
        old_value = @_before_should_change
        new_value = code.bind(self).call
        assert_operator from, :===, old_value, "#{description} did not originally match #{from.inspect}" if from
        assert_not_equal old_value, new_value, "#{description} did not change" unless by == 0
        assert_operator to, :===, new_value, "#{description} was not changed to match #{to.inspect}" if to
        assert_equal old_value + by, new_value if by
      end
    end

    # Macro that creates a test asserting no change between the return value
    # of a block that is run before and after the current setup block
    # is run. This is the logical opposite of should_change.
    #
    # The passed description will be used when generating the test name and failure message.
    #
    # Example:
    #
    #   context "Updating a post" do
    #     setup { @post.update_attributes(:title => "new") }
    #     should_not_change("the number of posts") { Post.count }
    #   end
    def should_not_change(description, &block)
      if block_given?
        code = block
      else
        warn "[DEPRECATION] should_not_change(expression) is deprecated. " <<
             "Use should_not_change(description) { code } instead."
        code = lambda { eval(description) }
      end
      before = lambda { @_before_should_not_change = code.bind(self).call }
      should "not change #{description}", :before => before do
        new_value = code.bind(self).call
        assert_equal @_before_should_not_change, new_value, "#{description} changed"
      end
    end

    # Macro that creates a test asserting that a record of the given class was
    # created.
    #
    # Example:
    #
    #   context "creating a post" do
    #     setup { Post.create(post_attributes) }
    #     should_create :post
    #   end
    def should_create(class_name)
      should_change_record_count_of(class_name, 1, 'create')
    end

    # Macro that creates a test asserting that a record of the given class was
    # destroyed.
    #
    # Example:
    #
    #   context "destroying a post" do
    #     setup { Post.first.destroy }
    #     should_destroy :post
    #   end
    def should_destroy(class_name)
      should_change_record_count_of(class_name, -1, 'destroy')
    end

    private

    def should_change_record_count_of(class_name, amount, action) # :nodoc:
      klass = class_name.to_s.camelize.constantize
      before = lambda do
        @_before_change_record_count = klass.count
      end
      human_name = class_name.to_s.humanize.downcase
      should "#{action} a #{human_name}", :before => before do
        assert_equal @_before_change_record_count + amount,
                     klass.count,
                     "Expected to #{action} a #{human_name}"
      end
    end

    include Shoulda::Private
  end
end