Class: ASpec

Inherits:
Object
  • Object
show all
Defined in:
lib/aspeclib.rb

Overview

ASpec is a simple library that utilizes aspect oriented programming and meta programming to enable stubing, method call counting, Liskov Substition Principle testing and more

Constant Summary collapse

@@class_substitutions =
{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeASpec

Returns a new instance of ASpec.



14
15
16
17
18
19
20
21
22
23
24
# File 'lib/aspeclib.rb', line 14

def initialize
  @replaced_class = ''
  @replacing_classes = [nil]
  @method_stubs = {}
  @aspects = []
  @return_values = []
  @method_call_counts = {}
  @expected_method_call_counts = {}
  @method_call_arguments = {}
  @expected_method_call_arguments = {}
end

Instance Attribute Details

#expected_method_call_argumentsObject (readonly)

Returns the value of attribute expected_method_call_arguments.



8
9
10
# File 'lib/aspeclib.rb', line 8

def expected_method_call_arguments
  @expected_method_call_arguments
end

#expected_method_call_countsObject (readonly)

Returns the value of attribute expected_method_call_counts.



8
9
10
# File 'lib/aspeclib.rb', line 8

def expected_method_call_counts
  @expected_method_call_counts
end

#method_call_argumentsObject (readonly)

Returns the value of attribute method_call_arguments.



8
9
10
# File 'lib/aspeclib.rb', line 8

def method_call_arguments
  @method_call_arguments
end

#method_call_countsObject (readonly)

Returns the value of attribute method_call_counts.



8
9
10
# File 'lib/aspeclib.rb', line 8

def method_call_counts
  @method_call_counts
end

#return_valuesObject (readonly)

Returns the value of attribute return_values.



8
9
10
# File 'lib/aspeclib.rb', line 8

def return_values
  @return_values
end

Class Method Details

.class_substition(original_class) ⇒ Object

Get the name of the class that should be replacing the original class in LSP testing



27
28
29
# File 'lib/aspeclib.rb', line 27

def self.class_substition(original_class)
  return @@class_substitutions[original_class]
end

.define_new_method(replaced_class) ⇒ Object

Replaces the constructor of the given class with the one from the LSP class subsititutions list



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/aspeclib.rb', line 102

def self.define_new_method(replaced_class)
  def (eval(replaced_class)).new
    obj = nil
    if ASpec.class_substition(self.to_s).nil?
      the_ancestor = ''
      self.ancestors.each do |ancestor|
        unless ASpec.class_substition(ancestor.to_s).nil?
          the_ancestor = ancestor.to_s
          break
        end
      end
      ASpec.remove_new_method_from_class(the_ancestor)
      obj = self.new
      ASpec.define_new_method(the_ancestor)
    else
      ASpec.remove_new_method_from_class(self.to_s)
      obj = eval(ASpec.class_substition(self.to_s)).new
      ASpec.define_new_method(self.to_s)
    end
    obj
  end
end

.newObject



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/aspeclib.rb', line 103

def (eval(replaced_class)).new
  obj = nil
  if ASpec.class_substition(self.to_s).nil?
    the_ancestor = ''
    self.ancestors.each do |ancestor|
      unless ASpec.class_substition(ancestor.to_s).nil?
        the_ancestor = ancestor.to_s
        break
      end
    end
    ASpec.remove_new_method_from_class(the_ancestor)
    obj = self.new
    ASpec.define_new_method(the_ancestor)
  else
    ASpec.remove_new_method_from_class(self.to_s)
    obj = eval(ASpec.class_substition(self.to_s)).new
    ASpec.define_new_method(self.to_s)
  end
  obj
end

.remove_new_method_from_class(class_name) ⇒ Object

Removes the arbitrary new constructor from the given class



126
127
128
129
130
# File 'lib/aspeclib.rb', line 126

def self.remove_new_method_from_class(class_name)
  class <<(eval(class_name))
    remove_method :new
  end
end

Instance Method Details

#count_method_calls(type, method, expected_count) ⇒ Object

Defines the expected count of method calls for the method in the type



73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/aspeclib.rb', line 73

def count_method_calls(type, method, expected_count)
  type, method, expected_count = type.to_s, method.to_s, expected_count.to_i
  (@expected_method_call_counts[type] ||= {})[method] = expected_count
  (@method_call_counts[type] ||= {})[method] = 0

  aspect = Aspect.new :around, :calls_to => method, :for_type => eval(type) do |join_point, object, *args|
    @method_call_counts[object.class.to_s][join_point.method_name.to_s] += 1
  end

  @aspects << aspect
  self
end

#executeObject

Executes the given block If there are LSP substitutes defined, executes the block for each of them



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/aspeclib.rb', line 134

def execute
  @replacing_classes.each do |replacing_class|

    unless replacing_class.nil?
      @@class_substitutions[@replaced_class] = replacing_class
      ASpec.define_new_method(@replaced_class)
    end

    @return_values << yield

    ASpec.remove_new_method_from_class(@replaced_class) unless replacing_class.nil?
  end

  @aspects.each do |aspect|
    aspect.unadvise
  end
  @aspects.clear

  self
end

#expect_method_arguments(type, method, expected_arguments) ⇒ Object

Defines the expected method arguments for each of the method calls expected_arguments is a list of lists of arguments, e.g. [[call1_arg1, call1_arg2], [call2_arg1, call2_arg2]]



88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/aspeclib.rb', line 88

def expect_method_arguments(type, method, expected_arguments)
  type, method = type.to_s, method.to_s
  (@expected_method_call_arguments[type] ||= {})[method] = expected_arguments
  (@method_call_arguments[type] ||= {})[method] = []

  aspect = Aspect.new :around, :calls_to => method, :for_type => eval(type) do |join_point, object, *args|
    (@method_call_arguments[object.class.to_s][join_point.method_name.to_s] ||= []) << args
  end

  @aspects << aspect
  self
end

#lsp(replaced_class, replacing_classes) ⇒ Object

Test Liskov Substition Principle Replaces the replaced_class with each of the replacing_classes during execution



33
34
35
36
37
38
39
40
41
# File 'lib/aspeclib.rb', line 33

def lsp(replaced_class, replacing_classes)
  @replaced_class = replaced_class.to_s
  @replacing_classes += if replacing_classes.class == Array
    replacing_classes.map {|item| item.to_s}
  else
    [replacing_classes.to_s]
  end
  self
end

#raise_exception(type, method) ⇒ Object

Raises exception when the method of the type is called



62
63
64
65
66
67
68
69
70
# File 'lib/aspeclib.rb', line 62

def raise_exception(type, method)
  type, method = type.to_s, method.to_s
  aspect = Aspect.new :around, :calls_to => method, :for_type => eval(type) do |join_point, object, *args|
    raise "ASpec doesn't like this at all."
  end

  @aspects << aspect
  self
end

#return_valueObject

The first returned value from the execution



156
157
158
# File 'lib/aspeclib.rb', line 156

def return_value
  @return_values.first
end

#stub(type, method, &block) ⇒ Object

Replaces the method of the type with the given block



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/aspeclib.rb', line 44

def stub(type, method, &block)
  type, method = type.to_s, method.to_s
  @method_stubs[[type, method]] = block

  aspect = Aspect.new :around, :calls_to => method, :for_type => eval(type) do |join_point, object, *args|
    key = [object.class.to_s, join_point.method_name.to_s]
    if @method_stubs.has_key? key
      @method_stubs[key].call
    else
      jp.proceed
    end
  end

  @aspects << aspect
  self
end