mocktail 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +12 -12
- data/README.md +152 -75
- data/lib/mocktail/explains_thing.rb +34 -4
- data/lib/mocktail/raises_neato_no_method_error.rb +1 -1
- data/lib/mocktail/value/explanation.rb +3 -0
- data/lib/mocktail/value/fake_method_data.rb +9 -0
- data/lib/mocktail/value.rb +1 -0
- data/lib/mocktail/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6a9c74cd3ff5167b99c38892ee6411a1ac3f1379158c062076bfbe6d8479c7b7
|
4
|
+
data.tar.gz: 81dd2b80a78c162a9c4030fa8bc9be42741c3a8c053fa33c319dc13c404234a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd1fbc482135e89dea00de9249d63b5e851d64ecb2f4afc898b4bb77abf5afc96303cfed6b59665d0e51ab4b2c8dd4c438dc2a7ab93d1c69e1d12d723797cf9a
|
7
|
+
data.tar.gz: 3af137dd6d1488b1ef41ac0b47178a224e160f11dce0ed0cbeb24418ae4a33c86a6e4fa8e3cabb20c8421c8ac1ac8c166045051d996d4aa1dc709e4fcc97ec3a
|
data/.github/workflows/main.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# 1.1.0
|
2
|
+
|
3
|
+
* Feature: add support for passing methods to `Mocktail.explain()`
|
4
|
+
* Fix 3.1 support by bypassing highlight_error for custom NoMethodError objects
|
5
|
+
raised by Mocktail [error_highlight#20](https://github.com/ruby/error_highlight/issues/20)
|
6
|
+
|
1
7
|
# 1.0.0
|
2
8
|
|
3
9
|
* First breaking change! 🎉
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
mocktail (1.
|
4
|
+
mocktail (1.1.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -12,27 +12,27 @@ GEM
|
|
12
12
|
method_source (1.0.0)
|
13
13
|
minitest (5.15.0)
|
14
14
|
parallel (1.21.0)
|
15
|
-
parser (3.0.
|
15
|
+
parser (3.1.0.0)
|
16
16
|
ast (~> 2.4.1)
|
17
17
|
pry (0.14.1)
|
18
18
|
coderay (~> 1.1)
|
19
19
|
method_source (~> 1.0)
|
20
|
-
rainbow (3.
|
20
|
+
rainbow (3.1.1)
|
21
21
|
rake (13.0.6)
|
22
22
|
regexp_parser (2.2.0)
|
23
23
|
rexml (3.2.5)
|
24
|
-
rubocop (1.
|
24
|
+
rubocop (1.25.0)
|
25
25
|
parallel (~> 1.10)
|
26
|
-
parser (>= 3.
|
26
|
+
parser (>= 3.1.0.0)
|
27
27
|
rainbow (>= 2.2.2, < 4.0)
|
28
28
|
regexp_parser (>= 1.8, < 3.0)
|
29
29
|
rexml
|
30
|
-
rubocop-ast (>= 1.
|
30
|
+
rubocop-ast (>= 1.15.1, < 2.0)
|
31
31
|
ruby-progressbar (~> 1.7)
|
32
32
|
unicode-display_width (>= 1.4.0, < 3.0)
|
33
|
-
rubocop-ast (1.15.
|
33
|
+
rubocop-ast (1.15.1)
|
34
34
|
parser (>= 3.0.1.1)
|
35
|
-
rubocop-performance (1.
|
35
|
+
rubocop-performance (1.13.2)
|
36
36
|
rubocop (>= 1.7.0, < 2.0)
|
37
37
|
rubocop-ast (>= 0.4.0)
|
38
38
|
ruby-progressbar (1.11.0)
|
@@ -42,9 +42,9 @@ GEM
|
|
42
42
|
simplecov_json_formatter (~> 0.1)
|
43
43
|
simplecov-html (0.12.3)
|
44
44
|
simplecov_json_formatter (0.1.3)
|
45
|
-
standard (1.
|
46
|
-
rubocop (= 1.
|
47
|
-
rubocop-performance (= 1.
|
45
|
+
standard (1.7.0)
|
46
|
+
rubocop (= 1.25.0)
|
47
|
+
rubocop-performance (= 1.13.2)
|
48
48
|
unicode-display_width (2.1.0)
|
49
49
|
|
50
50
|
PLATFORMS
|
@@ -60,4 +60,4 @@ DEPENDENCIES
|
|
60
60
|
standard
|
61
61
|
|
62
62
|
BUNDLED WITH
|
63
|
-
2.
|
63
|
+
2.3.6
|
data/README.md
CHANGED
@@ -553,14 +553,160 @@ them!)]
|
|
553
553
|
### Mocktail.explain
|
554
554
|
|
555
555
|
Test debugging is hard enough when there _aren't_ fake objects flying every
|
556
|
-
which way, so Mocktail tries to make it a little easier
|
557
|
-
messages throughout the
|
556
|
+
which way, so Mocktail tries to make it a little easier on you. In addition to
|
557
|
+
returning useful messages throughout the API, the gem also includes an
|
558
|
+
introspection method `Mocktail.explain(thing)`, which returns a human-readable
|
559
|
+
`message` and a `reference` object with useful attributes (that vary depending
|
560
|
+
on the type of fake `thing` you pass in. Below are some things `explain()` can
|
561
|
+
do.
|
562
|
+
|
563
|
+
#### Fake instances created by Mocktail
|
564
|
+
|
565
|
+
Any instances created by `Mocktail.of` or `Mocktail.of_next` can be passed to
|
566
|
+
`Mocktail.explain`, and they will list out all the calls and stubbings made for
|
567
|
+
each of their fake methods.
|
568
|
+
|
569
|
+
Suppose these interactions have occurred:
|
570
|
+
|
571
|
+
```ruby
|
572
|
+
ice_tray = Mocktail.of(IceTray)
|
573
|
+
|
574
|
+
Mocktail.stubs { ice_tray.fill(:tap_water, 30) }.with { :some_ice }
|
575
|
+
|
576
|
+
ice_tray.fill(:tap_water, 50)
|
577
|
+
```
|
578
|
+
|
579
|
+
You can interrogate what's going on with the fake instance by passing it to
|
580
|
+
`explain`:
|
581
|
+
|
582
|
+
```ruby
|
583
|
+
explanation = Mocktail.explain(ice_tray)
|
584
|
+
|
585
|
+
explanation.reference.type #=> IceTray
|
586
|
+
explanation.reference.double #=> The ice_tray instance
|
587
|
+
explanation.reference.calls #=> details on each invocation of each method
|
588
|
+
explanation.reference.stubbings #=> all stubbings configured for each method
|
589
|
+
```
|
590
|
+
|
591
|
+
Calling `explanation.message` will return:
|
592
|
+
|
593
|
+
```
|
594
|
+
This is a fake `IceTray' instance.
|
595
|
+
|
596
|
+
It has these mocked methods:
|
597
|
+
- fill
|
598
|
+
|
599
|
+
`IceTray#fill' stubbings:
|
600
|
+
|
601
|
+
fill(:tap_water, 30)
|
602
|
+
|
603
|
+
`IceTray#fill' calls:
|
604
|
+
|
605
|
+
fill(:tap_water, 50)
|
606
|
+
|
607
|
+
```
|
608
|
+
|
609
|
+
#### Modules and classes with singleton methods replaced
|
610
|
+
|
611
|
+
If you've called `Mocktail.replace()` on a class or module, it can also be
|
612
|
+
passed to `Mocktail.explain()` for a summary of all the stubbing configurations
|
613
|
+
and calls made against its faked singleton methods for the currently running
|
614
|
+
thread.
|
615
|
+
|
616
|
+
Imagine a `Shop` class with `self.open!` and `self.close!` singleton methods:
|
617
|
+
|
618
|
+
```ruby
|
619
|
+
Mocktail.replace(Shop)
|
620
|
+
|
621
|
+
stubs { |m| Shop.open!(m.numeric) }.with { :a_bar }
|
622
|
+
|
623
|
+
Shop.open!(42)
|
624
|
+
|
625
|
+
Shop.close!(42)
|
626
|
+
|
627
|
+
explanation = Mocktail.explain(Shop)
|
628
|
+
|
629
|
+
explanation.reference.type #=> Shop
|
630
|
+
explanation.reference.replaced_method_names #=> [:close!, :open!]
|
631
|
+
explanation.reference.calls #=> details on each invocation of each method
|
632
|
+
explanation.reference.stubbings #=> all stubbings configured for each method
|
633
|
+
```
|
634
|
+
|
635
|
+
And `explanation.message` will return:
|
636
|
+
|
637
|
+
```ruby
|
638
|
+
`Shop' is a class that has had its singleton methods faked.
|
639
|
+
|
640
|
+
It has these mocked methods:
|
641
|
+
- close!
|
642
|
+
- open!
|
643
|
+
|
644
|
+
`Shop.close!' has no stubbings.
|
645
|
+
|
646
|
+
`Shop.close!' calls:
|
647
|
+
|
648
|
+
close!(42)
|
649
|
+
|
650
|
+
close!(42)
|
651
|
+
|
652
|
+
`Shop.open!' stubbings:
|
653
|
+
|
654
|
+
open!(numeric)
|
655
|
+
|
656
|
+
open!(numeric)
|
657
|
+
|
658
|
+
`Shop.open!' calls:
|
659
|
+
|
660
|
+
open!(42)
|
661
|
+
|
662
|
+
open!(42)
|
663
|
+
```
|
664
|
+
|
665
|
+
#### Methods on faked instances and replaced types
|
666
|
+
|
667
|
+
In addition to passing the test double, you can also pass a reference to any
|
668
|
+
fake method created by Mocktail to `Mocktail.explain`:
|
669
|
+
|
670
|
+
```ruby
|
671
|
+
ice_tray = Mocktail.of(IceTray)
|
672
|
+
|
673
|
+
ice_tray.fill(:chilled, 50)
|
674
|
+
|
675
|
+
explanation = Mocktail.explain(ice_tray.method(:fill))
|
676
|
+
|
677
|
+
explanation.reference.receiver #=> a reference to the `ice_tray` instance
|
678
|
+
explanation.reference.calls #=> details on each invocation of the method
|
679
|
+
explanation.reference.stubbings #=> all stubbings configured for the method
|
680
|
+
```
|
681
|
+
|
682
|
+
The above may be handy in cases where you want to assert the number of calls of
|
683
|
+
a method outside the `Mocktail.verify` API:
|
684
|
+
|
685
|
+
```ruby
|
686
|
+
assert_equal 1, explanation.reference.calls.size
|
687
|
+
```
|
688
|
+
|
689
|
+
The explanation will also contain a `message` like this:
|
690
|
+
|
691
|
+
```
|
692
|
+
`IceTray#fill' has no stubbings.
|
693
|
+
|
694
|
+
`IceTray#fill' calls:
|
695
|
+
|
696
|
+
fill(:chilled, 50)
|
697
|
+
```
|
698
|
+
|
699
|
+
Replaced singleton methods can also be passed to `explain()`, so something like
|
700
|
+
`Mocktail.explain(Shop.method(:open!))` from the earlier example would also work
|
701
|
+
(with `Shop` being the `receiver` on the explanation's `reference`).
|
558
702
|
|
559
703
|
#### Undefined methods
|
560
704
|
|
561
|
-
|
562
|
-
that
|
563
|
-
|
705
|
+
There's no API for this one, but Mocktail also offers explanations for methods
|
706
|
+
that don't exist yet. You'll see this error message whenever you try to call a
|
707
|
+
method that doesn't exist on a test double. The message is designed to
|
708
|
+
facilitate "paint-by-numbers" TDD, by including a sample definition of the
|
709
|
+
method you had attempted to call that can be copy-pasted into a source listing:
|
564
710
|
|
565
711
|
```ruby
|
566
712
|
class IceTray
|
@@ -589,7 +735,7 @@ class IceTray
|
|
589
735
|
end
|
590
736
|
```
|
591
737
|
|
592
|
-
###
|
738
|
+
### Mocktail.explain_nils
|
593
739
|
|
594
740
|
Is a faked method returning `nil` and you don't understand why?
|
595
741
|
|
@@ -656,75 +802,6 @@ The `reference` object will have details of the `call` itself, an array of
|
|
656
802
|
`other_stubbings` defined on the faked method, and a `backtrace` to determine
|
657
803
|
which call site produced the unexpected `nil` value.
|
658
804
|
|
659
|
-
#### Fake instances created by Mocktail
|
660
|
-
|
661
|
-
Any instances created by `Mocktail.of` or `Mocktail.of_next` can be passed to
|
662
|
-
`Mocktail.explain`, and they will list out all the calls and stubbings made for
|
663
|
-
each of their fake methods.
|
664
|
-
|
665
|
-
Calling `Mocktail.explain(ice_tray).message` following the example above will
|
666
|
-
yield:
|
667
|
-
|
668
|
-
```
|
669
|
-
This is a fake `IceTray' instance.
|
670
|
-
|
671
|
-
It has these mocked methods:
|
672
|
-
- fill
|
673
|
-
|
674
|
-
`IceTray#fill' stubbings:
|
675
|
-
|
676
|
-
fill(:tap_water, 30)
|
677
|
-
|
678
|
-
`IceTray#fill' calls:
|
679
|
-
|
680
|
-
fill(:tap_water, 50)
|
681
|
-
```
|
682
|
-
|
683
|
-
#### Modules and classes with singleton methods replaced
|
684
|
-
|
685
|
-
If you've called `Mocktail.replace()` on a class or module, it can also be
|
686
|
-
passed to `Mocktail.explain()` for a summary of all the stubbing configurations
|
687
|
-
and calls made against its faked singleton methods for the currently running
|
688
|
-
thread.
|
689
|
-
|
690
|
-
Imagine a `Shop` class with `self.open!` and `self.close!` singleton methods:
|
691
|
-
|
692
|
-
```ruby
|
693
|
-
Mocktail.replace(Shop)
|
694
|
-
|
695
|
-
stubs { |m| Shop.open!(m.numeric) }.with { :a_bar }
|
696
|
-
|
697
|
-
Shop.open!(42)
|
698
|
-
|
699
|
-
Shop.close!(42)
|
700
|
-
|
701
|
-
puts Mocktail.explain(Shop).message
|
702
|
-
```
|
703
|
-
|
704
|
-
Will print:
|
705
|
-
|
706
|
-
```ruby
|
707
|
-
`Shop' is a class that has had its singleton methods faked.
|
708
|
-
|
709
|
-
It has these mocked methods:
|
710
|
-
- close!
|
711
|
-
- open!
|
712
|
-
|
713
|
-
`Shop.close!' has no stubbings.
|
714
|
-
|
715
|
-
`Shop.close!' calls:
|
716
|
-
|
717
|
-
close!(42)
|
718
|
-
|
719
|
-
`Shop.open!' stubbings:
|
720
|
-
|
721
|
-
open!(numeric)
|
722
|
-
|
723
|
-
`Shop.open!' calls:
|
724
|
-
|
725
|
-
open!(42)
|
726
|
-
```
|
727
|
-
|
728
805
|
### Mocktail.reset
|
729
806
|
|
730
807
|
This one's simple: you probably want to call `Mocktail.reset` after each test,
|
@@ -13,6 +13,8 @@ module Mocktail
|
|
13
13
|
double_explanation(double)
|
14
14
|
elsif (type_replacement = TopShelf.instance.type_replacement_if_exists_for(thing))
|
15
15
|
replaced_type_explanation(type_replacement)
|
16
|
+
elsif (fake_method_explanation = fake_method_explanation_for(thing))
|
17
|
+
fake_method_explanation
|
16
18
|
else
|
17
19
|
no_explanation(thing)
|
18
20
|
end
|
@@ -20,13 +22,37 @@ module Mocktail
|
|
20
22
|
|
21
23
|
private
|
22
24
|
|
23
|
-
def
|
24
|
-
|
25
|
+
def fake_method_explanation_for(thing)
|
26
|
+
return unless thing.is_a?(Method)
|
27
|
+
method = thing
|
28
|
+
receiver = thing.receiver
|
29
|
+
|
30
|
+
receiver_data = if (double = Mocktail.cabinet.double_for_instance(receiver))
|
31
|
+
data_for_double(double)
|
32
|
+
elsif (type_replacement = TopShelf.instance.type_replacement_if_exists_for(receiver))
|
33
|
+
data_for_type_replacement(type_replacement)
|
34
|
+
end
|
35
|
+
|
36
|
+
if receiver_data
|
37
|
+
FakeMethodExplanation.new(FakeMethodData.new(
|
38
|
+
receiver: receiver,
|
39
|
+
calls: receiver_data.calls,
|
40
|
+
stubbings: receiver_data.stubbings
|
41
|
+
), describe_dry_method(receiver_data, method.name))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def data_for_double(double)
|
46
|
+
DoubleData.new(
|
25
47
|
type: double.original_type,
|
26
48
|
double: double.dry_instance,
|
27
49
|
calls: Mocktail.cabinet.calls_for_double(double),
|
28
50
|
stubbings: Mocktail.cabinet.stubbings_for_double(double)
|
29
51
|
)
|
52
|
+
end
|
53
|
+
|
54
|
+
def double_explanation(double)
|
55
|
+
double_data = data_for_double(double)
|
30
56
|
|
31
57
|
DoubleExplanation.new(double_data, <<~MSG)
|
32
58
|
This is a fake `#{double.original_type.name}' instance.
|
@@ -38,8 +64,8 @@ module Mocktail
|
|
38
64
|
MSG
|
39
65
|
end
|
40
66
|
|
41
|
-
def
|
42
|
-
|
67
|
+
def data_for_type_replacement(type_replacement)
|
68
|
+
TypeReplacementData.new(
|
43
69
|
type: type_replacement.type,
|
44
70
|
replaced_method_names: type_replacement.replacement_methods.map(&:name).sort,
|
45
71
|
calls: Mocktail.cabinet.calls.select { |call|
|
@@ -49,6 +75,10 @@ module Mocktail
|
|
49
75
|
stubbing.recording.double == type_replacement.type
|
50
76
|
}
|
51
77
|
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def replaced_type_explanation(type_replacement)
|
81
|
+
type_replacement_data = data_for_type_replacement(type_replacement)
|
52
82
|
|
53
83
|
ReplacedTypeExplanation.new(type_replacement_data, <<~MSG)
|
54
84
|
`#{type_replacement.type}' is a #{type_replacement.type.class.to_s.downcase} that has had its singleton methods faked.
|
@@ -11,7 +11,7 @@ module Mocktail
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def call(call)
|
14
|
-
raise NoMethodError
|
14
|
+
raise NoMethodError, <<~MSG, caller[1..]
|
15
15
|
No method `#{@stringifies_method_name.stringify(call)}' exists for call:
|
16
16
|
|
17
17
|
#{@stringifies_call.stringify(call, anonymous_blocks: true, always_parens: true)}
|
data/lib/mocktail/value.rb
CHANGED
@@ -4,6 +4,7 @@ require_relative "value/demo_config"
|
|
4
4
|
require_relative "value/double"
|
5
5
|
require_relative "value/double_data"
|
6
6
|
require_relative "value/explanation"
|
7
|
+
require_relative "value/fake_method_data"
|
7
8
|
require_relative "value/matcher_registry"
|
8
9
|
require_relative "value/signature"
|
9
10
|
require_relative "value/stubbing"
|
data/lib/mocktail/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mocktail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Searls
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-27 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -82,6 +82,7 @@ files:
|
|
82
82
|
- lib/mocktail/value/double.rb
|
83
83
|
- lib/mocktail/value/double_data.rb
|
84
84
|
- lib/mocktail/value/explanation.rb
|
85
|
+
- lib/mocktail/value/fake_method_data.rb
|
85
86
|
- lib/mocktail/value/matcher_registry.rb
|
86
87
|
- lib/mocktail/value/signature.rb
|
87
88
|
- lib/mocktail/value/stubbing.rb
|
@@ -116,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
117
|
- !ruby/object:Gem::Version
|
117
118
|
version: '0'
|
118
119
|
requirements: []
|
119
|
-
rubygems_version: 3.
|
120
|
+
rubygems_version: 3.3.6
|
120
121
|
signing_key:
|
121
122
|
specification_version: 4
|
122
123
|
summary: Take your objects, and make them a double
|