rspec-core 2.8.0 → 2.9.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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