Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

implemented and tested

  • Loading branch information...
commit db45c7c74e5b35ef9970f218e41edc95d999fb1c 1 parent 4af2ca5
@dnagir authored
View
1  .gitignore
@@ -1,4 +1,5 @@
*.gem
.bundle
+*.swp
Gemfile.lock
pkg/*
View
2  .rspec
@@ -0,0 +1,2 @@
+--color
+--backtrace
View
6 its.gemspec
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
- # specify any dependencies here; for example:
- # s.add_development_dependency "rspec"
- # s.add_runtime_dependency "rest-client"
+ s.add_runtime_dependency "rspec-core", "~> 2.8"
+ s.add_development_dependency "rspec-mocks"
+ s.add_development_dependency "rspec-expectations"
end
View
99 lib/its.rb
@@ -1,5 +1,102 @@
require "its/version"
+require "rspec/core"
module Its
- # Your code goes here...
+ module ExampleGroupMethods
+
+ # Creates a nested example group named by the submitted +attribute+,
+ # and then generates an example using the submitted block.
+ #
+ # # This ...
+ # describe Array do
+ # its(:size) { should eq(0) }
+ # end
+ #
+ # # ... generates the same runtime structure as this:
+ # describe Array do
+ # describe "size" do
+ # it "should eq(0)" do
+ # subject.size.should eq(0)
+ # end
+ # end
+ # end
+ #
+ # The attribute can be a +Symbol+ or a +String+. Given a +String+
+ # with dots, the result is as though you concatenated that +String+
+ # onto the subject in an expression.
+ #
+ # describe Person do
+ # subject do
+ # Person.new.tap do |person|
+ # person.phone_numbers << "555-1212"
+ # end
+ # end
+ #
+ # its("phone_numbers.first") { should eq("555-1212") }
+ # end
+ #
+ # When the subject is a +Hash+, you can refer to the Hash keys by
+ # specifying a +Symbol+ or +String+ in an array.
+ #
+ # describe "a configuration Hash" do
+ # subject do
+ # { :max_users => 3,
+ # 'admin' => :all_permissions }
+ # end
+ #
+ # its([:max_users]) { should eq(3) }
+ # its(['admin']) { should eq(:all_permissions) }
+ #
+ # # You can still access to its regular methods this way:
+ # its(:keys) { should include(:max_users) }
+ # its(:count) { should eq(2) }
+ # end
+ #
+ # You can also pass any additional arguments the target method can accept:
+ #
+ # describe Person do
+ # subject do
+ # Person.new.tap do |person|
+ # person.phone_numbers << "123-123"
+ # person.phone_numbers << "234-234"
+ # person.phone_numbers << "xxx-xxx"
+ # end
+ # end
+ #
+ # its("phone_numbers.first", 2) { should == ["123-123", "234-234"]
+ # end
+ #
+ #
+ # This is extraction for the RSpec Core.
+ # For reference, see:
+ # - https://github.com/rspec/rspec-core/blob/7ce078e4948e8f0d1745a50bb83dd87a68b2e50e/lib/rspec/core/subject.rb#L120
+ # This modifies the following behaviour:
+ # - calls the target with arguments passed it
+ # - changes the description to include the args (if any)
+ def its(attribute, *args, &block)
+ desc = attribute.to_s
+ desc += "(#{args.map{|a| a.nil? ? 'nil' : a.to_s}.join(', ')})" unless args.empty?
+
+ describe(desc) do
+ example do
+ self.class.class_eval do
+ define_method(:subject) do
+ @_subject ||= if attribute.is_a?(Array)
+ super()[*attribute]
+ else
+ attribute.to_s.split('.').inject(super()) do |target, method|
+ target.send(method, *args)
+ end
+ end
+ end
+ end
+ instance_eval(&block)
+ end
+ end
+ end
+
+ end
+
end
+
+RSpec::Core::ExampleGroup.send :extend, Its::ExampleGroupMethods
View
97 spec/its_spec.rb
@@ -0,0 +1,97 @@
+require 'its'
+
+describe "#its" do
+ subject do
+ Class.new do
+ def initialize
+ @call_count = 0
+ end
+
+ def call_count
+ @call_count += 1
+ end
+ end.new
+ end
+
+ context "with a call counter" do
+ its(:call_count) { should eq(1) }
+ end
+
+ context "with nil value" do
+ subject do
+ Class.new do
+ def nil_value
+ nil
+ end
+ end.new
+ end
+ its(:nil_value) { should be_nil }
+ end
+
+ context "with nested attributes" do
+ subject do
+ Class.new do
+ def name
+ "John"
+ end
+ end.new
+ end
+ its("name") { should eq("John") }
+ its("name.size") { should eq(4) }
+ its("name.size.class") { should eq(Fixnum) }
+ end
+
+ context "when it responds to #[]" do
+ subject do
+ Class.new do
+ def [](*objects)
+ objects.map do |object|
+ "#{object.class}: #{object.to_s}"
+ end.join("; ")
+ end
+
+ def name
+ "George"
+ end
+ end.new
+ end
+ its([:a]) { should eq("Symbol: a") }
+ its(['a']) { should eq("String: a") }
+ its([:b, 'c', 4]) { should eq("Symbol: b; String: c; Fixnum: 4") }
+ its(:name) { should eq("George") }
+ context "when referring to an attribute without the proper array syntax" do
+ context "it raises an error" do
+ its(:age) do
+ expect do
+ should eq(64)
+ end.to raise_error(NoMethodError)
+ end
+ end
+ end
+ end
+
+ context "when it does not respond to #[]" do
+ subject { Object.new }
+
+ context "it raises an error" do
+ its([:a]) do
+ expect do
+ should eq("Symbol: a")
+ end.to raise_error(NoMethodError)
+ end
+ end
+ end
+
+ context "with arguments passed in" do
+ subject do
+ Class.new do
+ def currency(code)
+ "$#{code}"
+ end
+ end.new
+ end
+ its(:currency, :us) { should eq("$us") }
+ its(:currency, :uk) { should eq("$uk") }
+ its(:currency, :ua) { should eq("$ua") }
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.