minitest-suite 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14315494c6d4e85d5f6a754070add34121f58dac9499820f1a3936496c71a38e
4
- data.tar.gz: 7c5e5da23030fb27f8f4b3a2db983f918ca62a8aae89527bb28b0c78cceafb77
3
+ metadata.gz: f4c6431daa7d510ea5146c3b8b417c77847f7f3f5d5a64b331248b8e3299e8e5
4
+ data.tar.gz: e8c90ad9b02cda9467c7eee661ce47d539e2b055be68219e09965b23890238ae
5
5
  SHA512:
6
- metadata.gz: a4ce626b86e3c40b8091078c9210f18a714d7c67cc1cdcbcbea77f240ca7dfa4014c3280d286cfaaf84cbb44679fa9c7527e4ebe2692a216fc9cbdaf81d468c6
7
- data.tar.gz: db057fcf019dbf786c7755b300df0c226cd6cf27c54c5572765ccdd3c0e6731558097efd34ed962a0c30fa248bce45064c6ae4d086dc99625274f08c612978a2
6
+ metadata.gz: 9554fb2541a0be0b30c454841a667640194ee5c0f1ce9e53f1c4a5c5bc0ea0c37fb6d39e9ccc9803c8161aed0651b4833ce23c26f39c1ca6916cf7a101f220ed
7
+ data.tar.gz: 419c7a9f46e85b059dffcefea7170527c274fdcf38533f0d41b197480284253390239ee9d7d076223bdb46f763281e70a25c9daa2e5331455b666726adf8ca12
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # 0.0.2
2
+
3
+ - Adds Minitest::Suite.order= for specifying initial suite order
4
+ - Adds MINITEST_SUITE_ONLY and MINITEST_SUITE_EXCEPT options
5
+
1
6
  # 0.0.1
2
7
 
3
8
  - initial release
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- minitest-suite (0.0.1)
4
+ minitest-suite (0.0.2)
5
5
  minitest
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -5,8 +5,8 @@ The minitest-suite gem modifies the way
5
5
  `Minitest::Test` subclasses are run by allowing you to organize them into
6
6
  sub-suites or groups.
7
7
 
8
- This can be handy when you want to (usually for performance reasons) want to run
9
- one logical grouping of tests at a time before moving onto the next.
8
+ This can be handy when you want to (usually for performance reasons) run
9
+ one logical grouping of tests at a time before moving onto the next group.
10
10
 
11
11
  ## Using minitest-suite
12
12
 
@@ -26,8 +26,8 @@ require "minitest/suite"
26
26
 
27
27
  ### Declaring suites
28
28
 
29
- To associate a test class, just pass a suite name to `suite` in the body of each
30
- test class:
29
+ Just pass a suite name to `suite` in the class body of each
30
+ test to group that test with others of the same named suite:
31
31
 
32
32
  ```ruby
33
33
  class SweetTest < Minitest::Test
@@ -47,9 +47,9 @@ of those suites will be shuffled.
47
47
  For example, suppose you have 4 test classes, two fruits and two vegetables:
48
48
 
49
49
  ```ruby
50
- class Pumpkin < Minitest::Test
50
+ class Broccoli < Minitest::Test
51
51
  suite :veggie
52
- def test_it() = puts("🎃")
52
+ def test_it() = puts("🥦")
53
53
  end
54
54
 
55
55
  class Pear < Minitest::Test
@@ -77,17 +77,71 @@ the order will still be randomized):
77
77
 
78
78
  🍐
79
79
  .🍎
80
- .🎃
80
+ .🥦
81
81
  .🌶
82
82
  .
83
83
  ```
84
84
 
85
85
  To wit, the above strategy will ensure you'd never see this test order:
86
- 🍐,🌶,🍎,🎃.
86
+ 🍐,🌶,🍎,🥦.
87
87
 
88
88
  Looking for more? Check out this repo's [example
89
89
  test](/example/test/sweet_test.rb).
90
90
 
91
+ ### Configuration
92
+
93
+ Since you're going to the trouble of organizing your tests into logical suites,
94
+ you may as well have a little additional control over which suites run and in
95
+ what order. Below are a few handy things you can do once you're set up.
96
+
97
+ #### Fix the ordering that suites run in
98
+
99
+ A very sensible strategy is to optimize the speed of your feedback loop by run
100
+ your fastest tests first so your tests can fail fast.
101
+
102
+ As a typical example, suppose you want to run your pure Ruby unit tests, then
103
+ your Rails model tests, then your other (presumably slower) integration tests.
104
+ Near the top of your test helper, before your tests have started running, set
105
+ the order like this:
106
+
107
+ ```ruby
108
+ Minitest::Suite.order = [:unit, :model]
109
+ ```
110
+
111
+ With this set, any test classes that call `suite :unit` will be shuffled and run
112
+ first, then any tests with `suite :model`, and then the rest of your suites and
113
+ tests.
114
+
115
+ (Fail-fast behavior is available for Minitest via the [minitest-fail-fast
116
+ gem](https://github.com/teoljungberg/minitest-fail-fast) or the [Rails test
117
+ runner](https://guides.rubyonrails.org/testing.html#the-rails-test-runner)'s
118
+ `-f` flag.)
119
+
120
+ #### Filter to run only certain test suites
121
+
122
+ If you want to run only tests belonging to a certain suite or set of suites,
123
+ just set the environment variable `MINITEST_SUITE_ONLY` to a comma-delimited
124
+ string of suite names to run:
125
+
126
+ ```
127
+ $ MINITEST_SUITE_ONLY="unit,model" bin/rake test
128
+ ```
129
+
130
+ When using this option, note that test classes that _don't_ call `suite` **will
131
+ not be run**.
132
+
133
+ #### Run all tests except certain suites
134
+
135
+ If there's a suite of tests you don't want to run, set the environment variable
136
+ `MINITEST_SUITE_EXCEPT` to a comma-delimited string of suite names to skip:
137
+
138
+ ```
139
+ $ MINITEST_SUITE_EXCEPT="integration,browser" bin/rake test
140
+ ```
141
+
142
+ When using this option, note that test classes that _don't_ call `suite` **will
143
+ be run**.
144
+
91
145
  ## Code of Conduct
92
146
 
93
147
  This project follows Test Double's [code of
data/example/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- minitest-suite (0.0.1)
4
+ minitest-suite (0.0.2)
5
5
  minitest
6
6
 
7
7
  GEM
@@ -0,0 +1,65 @@
1
+ def assert_good_only_run(suites, bad)
2
+ unless bad.empty?
3
+ raise <<~MSG
4
+ Expected to only run #{suites.inspect}, but these suites ran and shouldn't have:
5
+
6
+ #{bad.join("\n")}
7
+ MSG
8
+ end
9
+ end
10
+
11
+ module Exe
12
+ def self.cution(exe)
13
+ @executions ||= []
14
+ @executions << exe
15
+ end
16
+
17
+ def self.cutions
18
+ @executions || []
19
+ end
20
+ end
21
+
22
+ at_exit do
23
+ if (setting = ENV["MINITEST_SUITE_ONLY"])
24
+ suites = setting.gsub(/\s+/, "").split(",").map(&:to_sym)
25
+ bad = Exe.cutions.reject { |exe| suites.include?(exe) }
26
+ assert_good_only_run(suites, bad)
27
+ elsif (setting = ENV["MINITEST_SUITE_EXCEPT"])
28
+ suites = setting.gsub(/\s+/, "").split(",").map(&:to_sym)
29
+ bad = Exe.cutions.select { |exe| suites.include?(exe) }
30
+ assert_good_only_run(suites, bad)
31
+ end
32
+ end
33
+
34
+ require "minitest/suite"
35
+ require "minitest/autorun"
36
+
37
+ class Test1 < Minitest::Test
38
+ suite :suite1
39
+
40
+ def test_it
41
+ Exe.cution :suite1
42
+ end
43
+ end
44
+
45
+ class Test2 < Minitest::Test
46
+ suite :suite2
47
+
48
+ def test_it
49
+ Exe.cution :suite2
50
+ end
51
+ end
52
+
53
+ class Test3 < Minitest::Test
54
+ suite :suite3
55
+
56
+ def test_it
57
+ Exe.cution :suite3
58
+ end
59
+ end
60
+
61
+ class Test4 < Minitest::Test
62
+ def test_it
63
+ Exe.cution :unsuitened
64
+ end
65
+ end
@@ -0,0 +1,113 @@
1
+ module Exe
2
+ def self.cution(exe)
3
+ @executions ||= []
4
+ @executions << exe
5
+ end
6
+
7
+ def self.cutions
8
+ @executions || []
9
+ end
10
+ end
11
+
12
+ at_exit do
13
+ expected = Minitest::Suite.order
14
+ expected_index = 0
15
+
16
+ Exe.cutions.each.with_index do |suite, actual_index|
17
+ finished_suites = expected_index == 0 ? [] : expected[0..(expected_index - 1)]
18
+
19
+ if finished_suites.include?(suite)
20
+ raise <<~MSG
21
+ Ensuring order #{expected.inspect} but #{suite.inspect} test was just run
22
+ out of order. Actual execution order:
23
+
24
+ #{Exe.cutions.map(&:inspect).join("\n")}
25
+ MSG
26
+ elsif suite == expected[expected_index]
27
+ # cool
28
+ else
29
+ expected_index += 1
30
+ end
31
+ end
32
+ end
33
+
34
+ require "minitest/suite"
35
+ require "minitest/autorun"
36
+
37
+ Minitest::Suite.order = ENV["ORDER"].split(",").map(&:to_sym)
38
+
39
+ class Test1 < Minitest::Test
40
+ suite :suite1
41
+
42
+ def test_it
43
+ Exe.cution :suite1
44
+ end
45
+ end
46
+
47
+ class Test2 < Minitest::Test
48
+ suite :suite2
49
+
50
+ def test_it
51
+ Exe.cution :suite2
52
+ end
53
+ end
54
+
55
+ class Test3 < Minitest::Test
56
+ suite :suite3
57
+
58
+ def test_it
59
+ Exe.cution :suite3
60
+ end
61
+ end
62
+
63
+ class Test4 < Minitest::Test
64
+ def test_it
65
+ Exe.cution :unsuitened
66
+ end
67
+ end
68
+
69
+ class Test5 < Minitest::Test
70
+ suite :suite1
71
+
72
+ def test_it
73
+ Exe.cution :suite1
74
+ end
75
+ end
76
+
77
+ class Test6 < Minitest::Test
78
+ suite :suite2
79
+
80
+ def test_it
81
+ Exe.cution :suite2
82
+ end
83
+ end
84
+
85
+ class Test7 < Minitest::Test
86
+ suite :suite3
87
+
88
+ def test_it
89
+ Exe.cution :suite3
90
+ end
91
+ end
92
+
93
+ class Test8 < Minitest::Test
94
+ def test_it
95
+ Exe.cution :unsuitened
96
+ end
97
+ end
98
+
99
+ class Test9 < Minitest::Test
100
+ suite :suite1
101
+
102
+ def test_it
103
+ Exe.cution :suite1
104
+ end
105
+ end
106
+
107
+ class Test10 < Minitest::Test
108
+ suite :suite2
109
+
110
+ def test_it
111
+ Exe.cution :suite2
112
+ end
113
+ end
@@ -15,10 +15,10 @@ at_exit do
15
15
  raise "Expected each suite to be run contiguously, but was not. Actual suite execution order was: #{suite_order}"
16
16
  end
17
17
 
18
- puts "Run order:"
19
- executions.each.with_index do |execution, i|
20
- puts "#{i + 1}. #{execution[:suite]}: #{execution[:class]}##{execution[:method]}"
21
- end
18
+ # puts "Run order:"
19
+ # executions.each.with_index do |execution, i|
20
+ # puts "#{i + 2}. #{execution[:suite]}: #{execution[:class]}##{execution[:method]}"
21
+ # end
22
22
  end
23
23
 
24
24
  require "minitest/suite"
@@ -52,23 +52,3 @@ CLASS_COUNT.times do |i|
52
52
  end
53
53
  })
54
54
  end
55
-
56
- class Pumpkin < Minitest::Test
57
- suite :veggie
58
- def test_it() = puts("🎃")
59
- end
60
-
61
- class Pear < Minitest::Test
62
- suite :fruit
63
- def test_it() = puts("🍐")
64
- end
65
-
66
- class Pepper < Minitest::Test
67
- suite :veggie
68
- def test_it() = puts("🌶")
69
- end
70
-
71
- class Apple < Minitest::Test
72
- suite :fruit
73
- def test_it() = puts("🍎")
74
- end
@@ -1,35 +1,69 @@
1
- require_relative "suite/version"
2
1
  require "minitest"
2
+ require_relative "suite/version"
3
3
 
4
4
  module Minitest
5
5
  module Suite
6
6
  class Error < StandardError; end
7
+ Registration = Struct.new(:suite, :test, keyword_init: true)
7
8
 
8
9
  def self.register(suite_name:, test_class:)
9
- raise Error.new("suite_name must be a Symbol") unless suite_name.is_a?(Symbol)
10
- raise Error.new("test_class must be a Minitest::Test") unless test_class.ancestors.include?(Minitest::Test)
11
- if (conflicting_suite_name = (suites.keys - [suite_name]).find { |suite_name| suites[suite_name].include?(test_class) })
12
- raise Error.new("#{test_class.name || "Class"} is already registered to the #{conflicting_suite_name.inspect} suite")
10
+ if !suite_name.is_a?(Symbol)
11
+ raise Error.new("suite_name must be a Symbol")
12
+ elsif !test_class.ancestors.include?(Minitest::Test)
13
+ raise Error.new("test_class must be a Minitest::Test")
14
+ elsif (conflict = registrations.find { |r| r.test == test_class && r.suite != suite_name })
15
+ raise Error.new("#{conflict.test.name || "Class"} is already registered to the #{conflict.suite.inspect} suite")
16
+ elsif registrations.none? { |r| r.test == test_class && r.suite == suite_name }
17
+ registrations << Registration.new(suite: suite_name, test: test_class)
18
+ end
19
+ end
20
+
21
+ def self.order=(suite_order)
22
+ if !suite_order.is_a?(Array) ||
23
+ suite_order.any? { |suite| !suite.is_a?(Symbol) }
24
+ raise Error.new("Minitest::Suite.order must be an array of Symbol suite names")
25
+ else
26
+ @@suite_order = suite_order.uniq
13
27
  end
14
- suites[suite_name] = (suites[suite_name] + [test_class]).uniq
15
28
  end
16
29
 
17
- def self.suites
18
- Thread.current[:minitest_suites] || reset
30
+ def self.order
31
+ @@suite_order ||= []
32
+ end
33
+
34
+ def self.registrations
35
+ @@registrations ||= reset
19
36
  end
20
37
 
21
38
  def self.reset
22
- Thread.current[:minitest_suites] = Hash.new { [] }
39
+ @@registrations = []
40
+ end
41
+
42
+ def self.filter_runnables(runnables)
43
+ if (only = ENV["MINITEST_SUITE_ONLY"])
44
+ suites = only.gsub(/\s+/, "").split(",").map(&:to_sym)
45
+ selected_tests = registrations.select { |r| suites.include?(r.suite) }
46
+ .map(&:test)
47
+ runnables.select { |r| selected_tests.include?(r) }
48
+ elsif (except = ENV["MINITEST_SUITE_EXCEPT"])
49
+ suites = except.gsub(/\s+/, "").split(",").map(&:to_sym)
50
+ excepted_tests = registrations.select { |r| suites.include?(r.suite) }
51
+ .map(&:test)
52
+ runnables.reject { |r| excepted_tests.include?(r) }
53
+ else
54
+ runnables
55
+ end
23
56
  end
24
57
 
25
58
  class PartialArrayProxy < Array
26
59
  def shuffle
27
- suites = (Suite.suites.keys + [:unsuitened]).shuffle
60
+ filtered = Suite.registrations.select { |r| include?(r.test) }
61
+ suites = Suite.order | (filtered.map(&:suite).uniq + [:__unsuitened]).shuffle
28
62
  suites.flat_map { |suite|
29
- if suite == :unsuitened
30
- (self - Suite.suites.values.flatten).shuffle
63
+ if suite == :__unsuitened
64
+ (self - filtered.map(&:test)).shuffle
31
65
  else
32
- Suite.suites[suite].shuffle
66
+ filtered.select { |r| r.suite == suite }.map(&:test).shuffle
33
67
  end
34
68
  }
35
69
  end
@@ -52,8 +86,13 @@ module Minitest
52
86
  class << self
53
87
  undef_method :runnables
54
88
  define_method :runnables do
55
- return @@runnables if @@runnables.is_a?(Minitest::Suite::PartialArrayProxy)
56
- @@runnables = Minitest::Suite::PartialArrayProxy.new(@@runnables)
89
+ filtered = Minitest::Suite.filter_runnables(@@runnables)
90
+ if @@runnables.is_a?(Minitest::Suite::PartialArrayProxy) &&
91
+ filtered == @runnables
92
+ @@runnables
93
+ else
94
+ @@runnables = Minitest::Suite::PartialArrayProxy.new(filtered)
95
+ end
57
96
  end
58
97
  end
59
98
  end
@@ -1,5 +1,5 @@
1
1
  module Minitest
2
2
  module Suite
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
data/script/test CHANGED
@@ -3,5 +3,23 @@
3
3
  set -ex
4
4
 
5
5
  bundle exec rake
6
+
6
7
  cd example
7
- bundle exec rake
8
+
9
+ bundle exec ruby test/suite_shuffle_test.rb
10
+
11
+ bundle exec ruby test/suite_filter_test.rb
12
+ MINITEST_SUITE_ONLY="suite1" bundle exec ruby test/suite_filter_test.rb
13
+ MINITEST_SUITE_ONLY="suite1, suite2" bundle exec ruby test/suite_filter_test.rb
14
+ MINITEST_SUITE_ONLY="suite1,suite2,suite3" bundle exec ruby test/suite_filter_test.rb
15
+ MINITEST_SUITE_ONLY="nonsense" bundle exec ruby test/suite_filter_test.rb
16
+ MINITEST_SUITE_EXCEPT="suite1" bundle exec ruby test/suite_filter_test.rb
17
+ MINITEST_SUITE_EXCEPT="suite1,suite3" bundle exec ruby test/suite_filter_test.rb
18
+ MINITEST_SUITE_EXCEPT="suite1,suite2,suite3" bundle exec ruby test/suite_filter_test.rb
19
+ MINITEST_SUITE_EXCEPT="nonsense" bundle exec ruby test/suite_filter_test.rb
20
+
21
+ ORDER="" bundle exec ruby test/suite_fixed_order_test.rb
22
+ ORDER="suite1" bundle exec ruby test/suite_fixed_order_test.rb
23
+ ORDER="suite2,suite3" bundle exec ruby test/suite_fixed_order_test.rb
24
+ ORDER="suite3,suite2,suite1" bundle exec ruby test/suite_fixed_order_test.rb
25
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minitest-suite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Searls
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-31 00:00:00.000000000 Z
11
+ date: 2021-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -44,7 +44,9 @@ files:
44
44
  - example/Gemfile
45
45
  - example/Gemfile.lock
46
46
  - example/Rakefile
47
- - example/test/sweet_test.rb
47
+ - example/test/suite_filter_test.rb
48
+ - example/test/suite_fixed_order_test.rb
49
+ - example/test/suite_shuffle_test.rb
48
50
  - lib/minitest/suite.rb
49
51
  - lib/minitest/suite/version.rb
50
52
  - minitest-suite.gemspec