test-prof 0.1.0.beta3 → 0.1.0.beta4
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.
- checksums.yaml +4 -4
- data/README.md +6 -0
- data/guides/factory_default.md +109 -0
- data/guides/tests_sampling.md +24 -0
- data/lib/test_prof/factory_default.rb +58 -0
- data/lib/test_prof/factory_default/factory_girl_patch.rb +22 -0
- data/lib/test_prof/recipes/minitest/sample.rb +29 -0
- data/lib/test_prof/recipes/rspec/factory_default.rb +9 -0
- data/lib/test_prof/recipes/rspec/sample.rb +13 -0
- data/lib/test_prof/stack_prof.rb +10 -0
- data/lib/test_prof/version.rb +1 -1
- metadata +23 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4bebeb0abae778ec18d0ec11538784b0255bd26d
|
4
|
+
data.tar.gz: '098fa4a951eba03ac7fcda3e6565ca01936dd182'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1099d6a1805f71697cccbb555025bffba6589e87016e33d715a046368e20dda1d5d1b2df259792b144c2ba0e7f816f64e6dd745f14114b1bb4ee6446e2edbba2
|
7
|
+
data.tar.gz: 4212b16b0487b6a5c4853e855a07278778cc487f0b378148d4903e180d20a935e3110542bdaf553070f499ddce220a046366b2b18049ae598b6dc36e4a54e0dc
|
data/README.md
CHANGED
@@ -31,6 +31,8 @@ See [Table of Contents](#table-of-contents) for more.
|
|
31
31
|
|
32
32
|
- RubyConfBy, 2017, "Run Test Run" talk [[video](https://www.youtube.com/watch?v=q52n4p0wkIs), [slides](https://speakerdeck.com/palkan/rubyconfby-minsk-2017-run-test-run)]
|
33
33
|
|
34
|
+
- [Tips to improve speed of your test suite](https://medium.com/appaloosa-store-engineering/tips-to-improve-speed-of-your-test-suite-8418b485205c) by [Benoit Tigeot](https://github.com/benoittgt)
|
35
|
+
|
34
36
|
## Installation
|
35
37
|
|
36
38
|
Add `test-prof` gem to your application:
|
@@ -69,8 +71,12 @@ We also want to share some small code tricks which can help you to improve your
|
|
69
71
|
|
70
72
|
- [AnyFixture](https://github.com/palkan/test-prof/tree/master/guides/any_fixture.md)
|
71
73
|
|
74
|
+
- [FactoryDefault](https://github.com/palkan/test-prof/tree/master/guides/factory_default.md)
|
75
|
+
|
72
76
|
- [RSpec Stamp](https://github.com/palkan/test-prof/tree/master/guides/rspec_stamp.md)
|
73
77
|
|
78
|
+
- [Tests Sampling](https://github.com/palkan/test-prof/tree/master/guides/tests_sampling.md)
|
79
|
+
|
74
80
|
## Configuration
|
75
81
|
|
76
82
|
TestProf global configuration is used by most of the profilers:
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# FactoryDefault
|
2
|
+
|
3
|
+
_Factory Default_ aims to help you cope with _factory cascades_ (see [FactoryProf](https://github.com/palkan/test-prof/tree/master/guides/factory_prof.md)) by re-using associated records.
|
4
|
+
|
5
|
+
It can be very useful when you're working on a typical SaaS application (or other hierarchical data).
|
6
|
+
|
7
|
+
Consider an example. Assume we have the following factories:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
factory :account do
|
11
|
+
end
|
12
|
+
|
13
|
+
factory :user do
|
14
|
+
account
|
15
|
+
end
|
16
|
+
|
17
|
+
factory :project do
|
18
|
+
account
|
19
|
+
user
|
20
|
+
end
|
21
|
+
|
22
|
+
factory :task do
|
23
|
+
account
|
24
|
+
project
|
25
|
+
user
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
And we want to test the `Task` model:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
describe "PATCH #update" do
|
33
|
+
let(:task( { create(:task) }
|
34
|
+
|
35
|
+
it "works" do
|
36
|
+
patch :update, id: task.id, task: { completed: 't' }
|
37
|
+
expect(response).to be_success
|
38
|
+
end
|
39
|
+
|
40
|
+
...
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
How many users and accounts are created per example? Two and four respectively.
|
45
|
+
|
46
|
+
And it breaks our logic (every object should belong to the same account).
|
47
|
+
|
48
|
+
Typical workaround:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
describe "PATCH #update" do
|
52
|
+
let(:account) { create(:account) }
|
53
|
+
let(:project) { create(:project, account: account) }
|
54
|
+
let(:task( { create(:task, project: project, account: account) }
|
55
|
+
|
56
|
+
it "works" do
|
57
|
+
patch :update, id: task.id, task: { completed: 't' }
|
58
|
+
expect(response).to be_success
|
59
|
+
end
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
That works. And there are some cons: it's a little bit verbose and error-prone (easy to forget something).
|
64
|
+
|
65
|
+
Here is how we can deal with it using FactoryDefault:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
describe "PATCH #update" do
|
69
|
+
let(:account) { create_default(:account) }
|
70
|
+
let(:project) { create_default(:project) }
|
71
|
+
let(:task( { create(:task) }
|
72
|
+
|
73
|
+
# and if need more projects, users, tasks with the same parent record,
|
74
|
+
# we just write
|
75
|
+
let(:another_project) { create(:project) } # uses the same account
|
76
|
+
let(:another_task) { create(:task) } # uses the same account and the first project
|
77
|
+
|
78
|
+
it "works" do
|
79
|
+
patch :update, id: task.id, task: { completed: 't' }
|
80
|
+
expect(response).to be_success
|
81
|
+
end
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
*NOTE*. This feature introduces a bit of _magic_ to your tests, so use it with caution ('cause tests should be human-readable first). Good idea is to use defaults for top-level entities only (such as tenants in multi-tenancy apps).
|
86
|
+
|
87
|
+
## Instructions
|
88
|
+
|
89
|
+
In your `spec_helper.rb`:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
require "test_prof/recipes/rspec/factory_default"
|
93
|
+
```
|
94
|
+
|
95
|
+
This adds two new methods to FactoryGirl:
|
96
|
+
|
97
|
+
- `FactoryGirl#set_factory_default(factory, object)` – use the `object` as default for associations built with `factory`
|
98
|
+
|
99
|
+
Example:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
let(:user) { create(:user) }
|
103
|
+
|
104
|
+
before { FactoryGirl.set_factory_default(:user, user) }
|
105
|
+
```
|
106
|
+
|
107
|
+
- `FactoryGirl#create_default(factory, *args)` – is a shortcut for `create` + `set_factory_default`.
|
108
|
+
|
109
|
+
*NOTE*. Defaults are cleaned up after each example.
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Tests Sampling
|
2
|
+
|
3
|
+
Sometimes it's useful to run profilers against randomly chosen tests. Unfortunetaly, test frameworks don's support such functionality. That's why we've included small patches for RSpec and Minitest in TestProf.
|
4
|
+
|
5
|
+
|
6
|
+
## Instructions
|
7
|
+
|
8
|
+
Require the corresponding patch:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
# For RSpec in your spec_helper.rb
|
12
|
+
require "test_prof/recipes/rspec/sample"
|
13
|
+
|
14
|
+
# For Minitest in your test_helper.rb
|
15
|
+
require "test_prof/recipes/minitest/sample"
|
16
|
+
```
|
17
|
+
|
18
|
+
And then just add `SAMPLE` env variable with the number of example groups (or suites) you want to run:
|
19
|
+
|
20
|
+
```sh
|
21
|
+
SAMPLE=10 rspec
|
22
|
+
```
|
23
|
+
|
24
|
+
That's it. Enjoy!
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "test_prof/factory_default/factory_girl_patch"
|
4
|
+
|
5
|
+
module TestProf
|
6
|
+
# FactoryDefault allows use to re-use associated objects
|
7
|
+
# in factories implicilty
|
8
|
+
module FactoryDefault
|
9
|
+
module DefaultSyntax # :nodoc:
|
10
|
+
def create_default(name, *args, &block)
|
11
|
+
set_factory_default(
|
12
|
+
name,
|
13
|
+
FactoryGirl.create(name, *args, &block)
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_factory_default(name, obj)
|
18
|
+
FactoryDefault.register(name, obj)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def init
|
24
|
+
FactoryGirl::Syntax::Methods.include DefaultSyntax
|
25
|
+
FactoryGirl.extend DefaultSyntax
|
26
|
+
FactoryGirl::Strategy::Create.prepend StrategyExt
|
27
|
+
FactoryGirl::Strategy::Build.prepend StrategyExt
|
28
|
+
FactoryGirl::Strategy::Stub.prepend StrategyExt
|
29
|
+
|
30
|
+
@store = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def register(name, obj)
|
34
|
+
store[name] = obj
|
35
|
+
end
|
36
|
+
|
37
|
+
def get(name)
|
38
|
+
store[name]
|
39
|
+
end
|
40
|
+
|
41
|
+
def exists?(name)
|
42
|
+
store.key?(name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def remove(name)
|
46
|
+
store.delete(name)
|
47
|
+
end
|
48
|
+
|
49
|
+
def reset
|
50
|
+
@store.clear
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
attr_reader :store
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TestProf
|
4
|
+
module FactoryDefault # :nodoc: all
|
5
|
+
module RunnerExt
|
6
|
+
refine FactoryGirl::FactoryRunner do
|
7
|
+
def name
|
8
|
+
@name
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
using RunnerExt
|
14
|
+
|
15
|
+
module StrategyExt
|
16
|
+
def association(runner)
|
17
|
+
return super unless FactoryDefault.exists?(runner.name)
|
18
|
+
FactoryDefault.get(runner.name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TestProf
|
4
|
+
# Add ability to run only a specified number of example groups (randomly selected)
|
5
|
+
module MinitestSample
|
6
|
+
# Do not add these classes to resulted sample
|
7
|
+
CORE_RUNNABLES = [
|
8
|
+
Minitest::Test,
|
9
|
+
Minitest::Unit::TestCase,
|
10
|
+
Minitest::Spec
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
def run(*)
|
14
|
+
unless ENV['SAMPLE'].nil?
|
15
|
+
sample_size = ENV['SAMPLE'].to_i
|
16
|
+
# Make sure that sample contains only _real_ suites
|
17
|
+
runnables = Minitest::Runnable.runnables
|
18
|
+
.sample(sample_size + CORE_RUNNABLES.size)
|
19
|
+
.reject { |suite| CORE_RUNNABLES.include?(suite) }
|
20
|
+
.take(sample_size)
|
21
|
+
Minitest::Runnable.reset
|
22
|
+
runnables.each { |r| Minitest::Runnable.runnables << r }
|
23
|
+
end
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Minitest.singleton_class.prepend(TestProf::MinitestSample)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TestProf
|
4
|
+
# Add ability to run only a specified number of example groups (randomly selected)
|
5
|
+
module RspecSample
|
6
|
+
def ordered_example_groups
|
7
|
+
@example_groups = @example_groups.sample(ENV['SAMPLE'].to_i) unless ENV['SAMPLE'].nil?
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec::Core::World.prepend(TestProf::RspecSample)
|
data/lib/test_prof/stack_prof.rb
CHANGED
@@ -83,6 +83,16 @@ module TestProf
|
|
83
83
|
::StackProf.results(path)
|
84
84
|
|
85
85
|
log :info, "StackProf report generated: #{path}"
|
86
|
+
|
87
|
+
return unless config.raw
|
88
|
+
|
89
|
+
html_path = path.gsub(/\.dump$/, '.html')
|
90
|
+
|
91
|
+
log :info, <<~MSG
|
92
|
+
Run the following command to generate a flame graph report:
|
93
|
+
|
94
|
+
stackprof --flamegraph #{path} > #{html_path} && stackprof --flamegraph-viewer=#{html_path}
|
95
|
+
MSG
|
86
96
|
end
|
87
97
|
|
88
98
|
private
|
data/lib/test_prof/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: test-prof
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.
|
4
|
+
version: 0.1.0.beta4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.9'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.9'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: activerecord
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -161,6 +175,7 @@ files:
|
|
161
175
|
- guides/any_fixture.md
|
162
176
|
- guides/before_all.md
|
163
177
|
- guides/event_prof.md
|
178
|
+
- guides/factory_default.md
|
164
179
|
- guides/factory_doctor.md
|
165
180
|
- guides/factory_prof.md
|
166
181
|
- guides/rspec_stamp.md
|
@@ -168,6 +183,7 @@ files:
|
|
168
183
|
- guides/ruby_prof.md
|
169
184
|
- guides/stack_prof.md
|
170
185
|
- guides/tag_prof.md
|
186
|
+
- guides/tests_sampling.md
|
171
187
|
- lib/test-prof.rb
|
172
188
|
- lib/test_prof.rb
|
173
189
|
- lib/test_prof/any_fixture.rb
|
@@ -181,6 +197,8 @@ files:
|
|
181
197
|
- lib/test_prof/event_prof/minitest.rb
|
182
198
|
- lib/test_prof/event_prof/rspec.rb
|
183
199
|
- lib/test_prof/ext/float_duration.rb
|
200
|
+
- lib/test_prof/factory_default.rb
|
201
|
+
- lib/test_prof/factory_default/factory_girl_patch.rb
|
184
202
|
- lib/test_prof/factory_doctor.rb
|
185
203
|
- lib/test_prof/factory_doctor/factory_girl_patch.rb
|
186
204
|
- lib/test_prof/factory_doctor/minitest.rb
|
@@ -190,8 +208,11 @@ files:
|
|
190
208
|
- lib/test_prof/factory_prof/printers/flamegraph.rb
|
191
209
|
- lib/test_prof/factory_prof/printers/simple.rb
|
192
210
|
- lib/test_prof/logging.rb
|
211
|
+
- lib/test_prof/recipes/minitest/sample.rb
|
193
212
|
- lib/test_prof/recipes/rspec/any_fixture.rb
|
194
213
|
- lib/test_prof/recipes/rspec/before_all.rb
|
214
|
+
- lib/test_prof/recipes/rspec/factory_default.rb
|
215
|
+
- lib/test_prof/recipes/rspec/sample.rb
|
195
216
|
- lib/test_prof/rspec_stamp.rb
|
196
217
|
- lib/test_prof/rspec_stamp/parser.rb
|
197
218
|
- lib/test_prof/rspec_stamp/rspec.rb
|