rspec-core 2.8.0 → 2.9.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/.document +5 -0
  2. data/.yardopts +3 -0
  3. data/Changelog.md +492 -0
  4. data/README.md +1 -1
  5. data/features/mock_framework_integration/use_flexmock.feature +2 -2
  6. data/features/step_definitions/additional_cli_steps.rb +2 -2
  7. data/features/subject/attribute_of_subject.feature +4 -2
  8. data/features/subject/explicit_subject.feature +6 -2
  9. data/features/subject/implicit_receiver.feature +1 -1
  10. data/features/subject/implicit_subject.feature +40 -7
  11. data/lib/autotest/rspec2.rb +1 -1
  12. data/lib/rspec/core.rb +1 -1
  13. data/lib/rspec/core/configuration.rb +53 -5
  14. data/lib/rspec/core/configuration_options.rb +5 -2
  15. data/lib/rspec/core/drb_options.rb +3 -1
  16. data/lib/rspec/core/filter_manager.rb +1 -1
  17. data/lib/rspec/core/formatters/base_text_formatter.rb +1 -5
  18. data/lib/rspec/core/formatters/documentation_formatter.rb +4 -8
  19. data/lib/rspec/core/formatters/helpers.rb +15 -0
  20. data/lib/rspec/core/hooks.rb +3 -1
  21. data/lib/rspec/core/metadata.rb +6 -11
  22. data/lib/rspec/core/runner.rb +4 -14
  23. data/lib/rspec/core/version.rb +1 -1
  24. data/lib/rspec/core/world.rb +7 -7
  25. data/spec/autotest/rspec_spec.rb +1 -1
  26. data/spec/rspec/core/configuration_options_spec.rb +8 -5
  27. data/spec/rspec/core/configuration_spec.rb +38 -10
  28. data/spec/rspec/core/example_group_spec.rb +16 -0
  29. data/spec/rspec/core/formatters/documentation_formatter_spec.rb +23 -1
  30. data/spec/rspec/core/formatters/helpers_spec.rb +20 -0
  31. data/spec/rspec/core/metadata_spec.rb +3 -4
  32. data/spec/rspec/core/shared_example_group_spec.rb +2 -2
  33. data/spec/rspec/core/world_spec.rb +11 -1
  34. metadata +166 -19
  35. data/lib/rspec/monkey.rb +0 -1
  36. data/lib/rspec/monkey/spork/test_framework/rspec.rb +0 -10
data/README.md CHANGED
@@ -32,7 +32,7 @@ describe Order do
32
32
  end
33
33
  ```
34
34
 
35
- The `describe` method creates an [ExampleGroup](../RSpec/Core/ExampleGroup). Within the
35
+ The `describe` method creates an [ExampleGroup](http://rubydoc.info/gems/rspec-core/RSpec/Core/ExampleGroup). Within the
36
36
  block passed to `describe` you can declare examples using the `it` method.
37
37
 
38
38
  Under the hood, an example group is a class in which the block passed to
@@ -9,7 +9,7 @@ Feature: mock with flexmock
9
9
  config.mock_framework = :flexmock
10
10
  end
11
11
 
12
- describe "mocking with RSpec" do
12
+ describe "mocking with Flexmock" do
13
13
  it "passes when it should" do
14
14
  receiver = flexmock('receiver')
15
15
  receiver.should_receive(:message).once
@@ -27,7 +27,7 @@ Feature: mock with flexmock
27
27
  config.mock_framework = :flexmock
28
28
  end
29
29
 
30
- describe "mocking with RSpec" do
30
+ describe "mocking with Flexmock" do
31
31
  it "fails when it should" do
32
32
  receiver = flexmock('receiver')
33
33
  receiver.should_receive(:message).once
@@ -11,8 +11,8 @@ Then /^the output should not contain any of these:$/ do |table|
11
11
  end
12
12
 
13
13
  Then /^the example(?:s)? should(?: all)? pass$/ do
14
- Then %q{the output should contain "0 failures"}
15
- Then %q{the exit status should be 0}
14
+ step %q{the output should contain "0 failures"}
15
+ step %q{the exit status should be 0}
16
16
  end
17
17
 
18
18
  Then /^the file "([^"]*)" should contain:$/ do |file, partial_content|
@@ -1,10 +1,12 @@
1
1
  Feature: attribute of subject
2
2
 
3
- Use the its() method as a short-hand to generate a nested example group with
3
+ WARNING: `its` will be extracted from rspec-core-3.0 into its own gem.
4
+
5
+ Use the `its` method as a short-hand to generate a nested example group with
4
6
  a single example that specifies the expected value of an attribute of the
5
7
  subject. This can be used with an implicit or explicit subject.
6
8
 
7
- its() accepts a symbol or a string, and a block representing the example.
9
+ `its` accepts a symbol or a string, and a block representing the example.
8
10
 
9
11
  its(:size) { should eq(1) }
10
12
  its("length") { should eq(1) }
@@ -1,7 +1,11 @@
1
1
  Feature: explicit subject
2
2
 
3
- Use subject() in the group scope to explicitly define the value that is
4
- returned by the subject() method in the example scope.
3
+ Use `subject` in the group scope to explicitly define the value that is
4
+ returned by the `subject` method in the example scope.
5
+
6
+ Note that while the examples below demonstrate how `subject` can be used as a
7
+ user-facing concept, we recommend that you reserve it for support of custom
8
+ matchers and/or extension libraries that hide its use from examples.
5
9
 
6
10
  Scenario: subject in top level group
7
11
  Given a file named "top_level_subject_spec.rb" with:
@@ -1,6 +1,6 @@
1
1
  Feature: implicit receiver
2
2
 
3
- When should() is called in an example without an explicit receiver, it is
3
+ When `should` is called in an example without an explicit receiver, it is
4
4
  invoked against the subject (explicit or implicit).
5
5
 
6
6
  Scenario: implicit subject
@@ -1,14 +1,18 @@
1
- Feature: implicit subject
1
+ Feature: implicitly defined subject
2
2
 
3
3
  If the first argument to the outermost example group is a class, an instance
4
- of that class is exposed to each example via the subject() method.
4
+ of that class is exposed to each example via the `subject` method.
5
+
6
+ While the examples below demonstrate how `subject` can be used as a
7
+ user-facing concept, we recommend that you reserve it for support of custom
8
+ matchers and/or extension libraries that hide its use from examples.
5
9
 
6
- Scenario: subject in top level group
10
+ Scenario: subject exposed in top level group
7
11
  Given a file named "top_level_subject_spec.rb" with:
8
12
  """
9
- describe Array, "when first created" do
10
- it "should be empty" do
11
- subject.should eq([])
13
+ describe Array do
14
+ it "should be empty when first created" do
15
+ subject.should be_empty
12
16
  end
13
17
  end
14
18
  """
@@ -21,7 +25,36 @@ Feature: implicit subject
21
25
  describe Array do
22
26
  describe "when first created" do
23
27
  it "should be empty" do
24
- subject.should eq([])
28
+ subject.should be_empty
29
+ end
30
+ end
31
+ end
32
+ """
33
+ When I run `rspec nested_subject_spec.rb`
34
+ Then the examples should all pass
35
+
36
+ Scenario: subject in a nested group with a different class (outermost wins)
37
+ Given a file named "nested_subject_spec.rb" with:
38
+ """
39
+ class ArrayWithOneElement < Array
40
+ def initialize(*)
41
+ super
42
+ unshift "first element"
43
+ end
44
+ end
45
+
46
+ describe Array do
47
+ describe ArrayWithOneElement do
48
+ context "referenced as subject" do
49
+ it "should be empty (because it is the Array declared at the top)" do
50
+ subject.should be_empty
51
+ end
52
+ end
53
+
54
+ context "created in the example" do
55
+ it "should not be empty" do
56
+ ArrayWithOneElement.new.should_not be_empty
57
+ end
25
58
  end
26
59
  end
27
60
  end
@@ -47,7 +47,7 @@ class Autotest::Rspec2 < Autotest
47
47
  # Overrides Autotest's implementation to generate the rspec command to run
48
48
  def make_test_cmd(files_to_test)
49
49
  files_to_test.empty? ? '' :
50
- "#{prefix}#{ruby}#{suffix} -S #{RSPEC_EXECUTABLE} --tty #{normalize(files_to_test).keys.flatten.map { |f| "'#{f}'"}.join(' ')}"
50
+ "#{prefix}#{ruby}#{suffix} -S '#{RSPEC_EXECUTABLE}' --tty #{normalize(files_to_test).keys.flatten.map { |f| "'#{f}'"}.join(' ')}"
51
51
  end
52
52
 
53
53
  # Generates a map of filenames to Arrays for Autotest
@@ -10,6 +10,7 @@ else
10
10
  end
11
11
  end
12
12
 
13
+ require 'set'
13
14
  require_rspec 'core/filter_manager'
14
15
  require_rspec 'core/dsl'
15
16
  require_rspec 'core/extensions'
@@ -104,4 +105,3 @@ module RSpec
104
105
  end
105
106
 
106
107
  require_rspec 'core/backward_compatibility'
107
- require_rspec 'monkey'
@@ -534,10 +534,26 @@ EOM
534
534
  # or config files (e.g. `.rspec`).
535
535
  #
536
536
  # @example
537
- # filter_run_including :x => 'y'
537
+ # # given this declaration
538
+ # describe "something", :foo => 'bar' do
539
+ # # ...
540
+ # end
541
+ #
542
+ # # any of the following will include that group
543
+ # config.filter_run_including :foo => 'bar'
544
+ # config.filter_run_including :foo => /^ba/
545
+ # config.filter_run_including :foo => lambda {|v| v == 'bar'}
546
+ # config.filter_run_including :foo => lambda {|v,m| m[:foo] == 'bar'}
547
+ #
548
+ # # given a proc with an arity of 1, the lambda is passed the value related to the key, e.g.
549
+ # config.filter_run_including :foo => lambda {|v| v == 'bar'}
550
+ #
551
+ # # given a proc with an arity of 2, the lambda is passed the value related to the key,
552
+ # # and the metadata itself e.g.
553
+ # config.filter_run_including :foo => lambda {|v,m| m[:foo] == 'bar'}
538
554
  #
539
555
  # # with treat_symbols_as_metadata_keys_with_true_values = true
540
- # filter_run_including :foo # results in {:foo => true}
556
+ # filter_run_including :foo # same as filter_run_including :foo => true
541
557
  def filter_run_including(*args)
542
558
  filter_manager.include_with_low_priority build_metadata_hash_from(args)
543
559
  end
@@ -576,10 +592,26 @@ EOM
576
592
  # or config files (e.g. `.rspec`).
577
593
  #
578
594
  # @example
579
- # filter_run_excluding :x => 'y'
595
+ # # given this declaration
596
+ # describe "something", :foo => 'bar' do
597
+ # # ...
598
+ # end
599
+ #
600
+ # # any of the following will exclude that group
601
+ # config.filter_run_excluding :foo => 'bar'
602
+ # config.filter_run_excluding :foo => /^ba/
603
+ # config.filter_run_excluding :foo => lambda {|v| v == 'bar'}
604
+ # config.filter_run_excluding :foo => lambda {|v,m| m[:foo] == 'bar'}
605
+ #
606
+ # # given a proc with an arity of 1, the lambda is passed the value related to the key, e.g.
607
+ # config.filter_run_excluding :foo => lambda {|v| v == 'bar'}
608
+ #
609
+ # # given a proc with an arity of 2, the lambda is passed the value related to the key,
610
+ # # and the metadata itself e.g.
611
+ # config.filter_run_excluding :foo => lambda {|v,m| m[:foo] == 'bar'}
580
612
  #
581
613
  # # with treat_symbols_as_metadata_keys_with_true_values = true
582
- # filter_run_excluding :foo # results in {:foo => true}
614
+ # filter_run_excluding :foo # same as filter_run_excluding :foo => true
583
615
  def filter_run_excluding(*args)
584
616
  filter_manager.exclude_with_low_priority build_metadata_hash_from(args)
585
617
  end
@@ -677,7 +709,23 @@ EOM
677
709
  def configure_group(group)
678
710
  include_or_extend_modules.each do |include_or_extend, mod, filters|
679
711
  next unless filters.empty? || group.any_apply?(filters)
680
- group.send(include_or_extend, mod)
712
+ send("safe_#{include_or_extend}", mod, group)
713
+ end
714
+ end
715
+
716
+ # @private
717
+ def safe_include(mod, host)
718
+ host.send(:include,mod) unless host < mod
719
+ end
720
+
721
+ # @private
722
+ if RUBY_VERSION.to_f >= 1.9
723
+ def safe_extend(mod, host)
724
+ host.extend(mod) unless (class << host; self; end) < mod
725
+ end
726
+ else
727
+ def safe_extend(mod, host)
728
+ host.extend(mod) unless (class << host; self; end).included_modules.include?(mod)
681
729
  end
682
730
  end
683
731
 
@@ -33,12 +33,15 @@ module RSpec
33
33
  end
34
34
 
35
35
  def filter_manager
36
- @filter_manager ||= FilterManager.new
36
+ @filter_manager ||= RSpec::configuration.filter_manager
37
37
  end
38
38
 
39
39
  private
40
40
 
41
- NON_FORCED_OPTIONS = [:debug, :requires, :libs, :files_or_directories_to_run, :line_numbers, :full_description]
41
+ NON_FORCED_OPTIONS = [
42
+ :debug, :requires, :libs, :profile, :drb, :files_or_directories_to_run,
43
+ :line_numbers, :full_description, :full_backtrace, :tty
44
+ ].to_set
42
45
 
43
46
  def force?(key)
44
47
  !NON_FORCED_OPTIONS.include?(key)
@@ -52,9 +52,11 @@ module RSpec::Core
52
52
  end
53
53
  end
54
54
 
55
+ CONDITIONAL_FILTERS = [:if, :unless]
56
+
55
57
  def add_filter(argv, name, hash)
56
58
  hash.each_pair do |k, v|
57
- next if [:if,:unless].include?(k)
59
+ next if CONDITIONAL_FILTERS.include?(k)
58
60
  tag = name == :inclusion ? k.to_s : "~#{k}"
59
61
  tag << ":#{v}" if v.is_a?(String)
60
62
  argv << "--tag" << tag
@@ -67,7 +67,7 @@ module RSpec
67
67
  # @see Configuration#filter_run_excluding
68
68
  class FilterManager
69
69
  DEFAULT_EXCLUSIONS = {
70
- :if => lambda { |value, metadata| metadata.has_key?(:if) && !value },
70
+ :if => lambda { |value| !value },
71
71
  :unless => lambda { |value| value }
72
72
  }
73
73
 
@@ -35,7 +35,7 @@ module RSpec
35
35
  super(duration, example_count, failure_count, pending_count)
36
36
  # Don't print out profiled info if there are failures, it just clutters the output
37
37
  dump_profile if profile_examples? && failure_count == 0
38
- output.puts "\nFinished in #{format_seconds(duration)} seconds\n"
38
+ output.puts "\nFinished in #{format_duration(duration)}\n"
39
39
  output.puts colorise_summary(summary_line(example_count, failure_count, pending_count))
40
40
  dump_commands_to_rerun_failed_examples
41
41
  end
@@ -142,10 +142,6 @@ module RSpec
142
142
 
143
143
  private
144
144
 
145
- def pluralize(count, string)
146
- "#{count} #{string}#{'s' unless count == 1}"
147
- end
148
-
149
145
  def format_caller(caller_info)
150
146
  backtrace_line(caller_info.to_s.split(':in `block').first)
151
147
  end
@@ -3,9 +3,7 @@ require 'rspec/core/formatters/base_text_formatter'
3
3
  module RSpec
4
4
  module Core
5
5
  module Formatters
6
-
7
6
  class DocumentationFormatter < BaseTextFormatter
8
-
9
7
  def initialize(output)
10
8
  super(output)
11
9
  @group_level = 0
@@ -15,7 +13,7 @@ module RSpec
15
13
  super(example_group)
16
14
 
17
15
  output.puts if @group_level == 0
18
- output.puts "#{current_indentation}#{example_group.description}"
16
+ output.puts "#{current_indentation}#{example_group.description.strip}"
19
17
 
20
18
  @group_level += 1
21
19
  end
@@ -40,7 +38,7 @@ module RSpec
40
38
  end
41
39
 
42
40
  def failure_output(example, exception)
43
- red("#{current_indentation}#{example.description} (FAILED - #{next_failure_index})")
41
+ red("#{current_indentation}#{example.description.strip} (FAILED - #{next_failure_index})")
44
42
  end
45
43
 
46
44
  def next_failure_index
@@ -49,11 +47,11 @@ module RSpec
49
47
  end
50
48
 
51
49
  def passed_output(example)
52
- green("#{current_indentation}#{example.description}")
50
+ green("#{current_indentation}#{example.description.strip}")
53
51
  end
54
52
 
55
53
  def pending_output(example, message)
56
- yellow("#{current_indentation}#{example.description} (PENDING: #{message})")
54
+ yellow("#{current_indentation}#{example.description.strip} (PENDING: #{message})")
57
55
  end
58
56
 
59
57
  def current_indentation
@@ -63,9 +61,7 @@ module RSpec
63
61
  def example_group_chain
64
62
  example_group.ancestors.reverse
65
63
  end
66
-
67
64
  end
68
-
69
65
  end
70
66
  end
71
67
  end
@@ -6,6 +6,17 @@ module RSpec
6
6
  SUB_SECOND_PRECISION = 5
7
7
  DEFAULT_PRECISION = 2
8
8
 
9
+ def format_duration(duration)
10
+ if duration > 60
11
+ minutes = duration.to_i / 60
12
+ seconds = duration - minutes * 60
13
+
14
+ "#{pluralize(minutes, 'minute')} #{format_seconds(seconds)} seconds"
15
+ else
16
+ "#{format_seconds(duration)} seconds"
17
+ end
18
+ end
19
+
9
20
  def format_seconds(float)
10
21
  precision ||= (float < 1) ? SUB_SECOND_PRECISION : DEFAULT_PRECISION
11
22
  formatted = sprintf("%.#{precision}f", float)
@@ -17,6 +28,10 @@ module RSpec
17
28
  stripped.empty? ? "0" : stripped
18
29
  end
19
30
 
31
+ def pluralize(count, string)
32
+ "#{count} #{string}#{'s' unless count == 1}"
33
+ end
34
+
20
35
  end
21
36
 
22
37
  end
@@ -398,8 +398,10 @@ module RSpec
398
398
 
399
399
  private
400
400
 
401
+ SCOPES = [:each, :all, :suite]
402
+
401
403
  def scope_and_options_from(*args)
402
- scope = if [:each, :all, :suite].include?(args.first)
404
+ scope = if SCOPES.include?(args.first)
403
405
  args.shift
404
406
  elsif args.any? { |a| a.is_a?(Symbol) }
405
407
  raise ArgumentError.new("You must explicitly give a scope (:each, :all, or :suite) when using symbols as metadata for a hook.")
@@ -102,8 +102,8 @@ module RSpec
102
102
 
103
103
  def described_class
104
104
  container_stack.each do |g|
105
- return g[:describes] if g.has_key?(:describes)
106
105
  return g[:described_class] if g.has_key?(:described_class)
106
+ return g[:describes] if g.has_key?(:describes)
107
107
  end
108
108
 
109
109
  container_stack.reverse.each do |g|
@@ -174,20 +174,15 @@ module RSpec
174
174
  return metadata.location_filter_applies?(value) if key == :locations
175
175
  return metadata.filters_apply?(key, value) if Hash === value
176
176
 
177
+ return false unless metadata.has_key?(key)
178
+
177
179
  case value
178
180
  when Regexp
179
181
  metadata[key] =~ value
180
182
  when Proc
181
- if value.arity == 2
182
- # Pass the metadata hash to allow the proc to check if it even has the key.
183
- # This is necessary for the implicit :if exclusion filter:
184
- # { } # => run the example
185
- # { :if => nil } # => exclude the example
186
- # The value of metadata[:if] is the same in these two cases but
187
- # they need to be treated differently.
188
- value.call(metadata[key], metadata) rescue false
189
- else
190
- value.call(metadata[key]) rescue false
183
+ case value.arity
184
+ when 1 then value.call(metadata[key])
185
+ when 2 then value.call(metadata[key], metadata)
191
186
  end
192
187
  else
193
188
  metadata[key].to_s == value.to_s
@@ -7,7 +7,7 @@ module RSpec
7
7
  # Register an at_exit hook that runs the suite.
8
8
  def self.autorun
9
9
  return if autorun_disabled? || installed_at_exit? || running_in_drb?
10
- at_exit { exit run(ARGV, $stderr, $stdout).to_i }
10
+ at_exit { exit run(ARGV, $stderr, $stdout).to_i unless $! }
11
11
  @installed_at_exit = true
12
12
  end
13
13
  AT_EXIT_HOOK_BACKTRACE_LINE = "#{__FILE__}:#{__LINE__ - 2}:in `autorun'"
@@ -60,27 +60,17 @@ module RSpec
60
60
 
61
61
  if options.options[:drb]
62
62
  begin
63
- run_over_drb(options, err, out)
63
+ DRbCommandLine.new(options).run(err, out)
64
64
  rescue DRb::DRbConnError
65
65
  err.puts "No DRb server is running. Running in local process instead ..."
66
- run_in_process(options, err, out)
66
+ CommandLine.new(options).run(err, out)
67
67
  end
68
68
  else
69
- run_in_process(options, err, out)
69
+ CommandLine.new(options).run(err, out)
70
70
  end
71
71
  ensure
72
72
  RSpec.reset
73
73
  end
74
-
75
- def self.run_over_drb(options, err, out)
76
- DRbCommandLine.new(options).run(err, out)
77
- end
78
-
79
- def self.run_in_process(options, err, out)
80
- CommandLine.new(options, RSpec::configuration, RSpec::world).run(err, out)
81
- end
82
-
83
74
  end
84
-
85
75
  end
86
76
  end