sus 0.32.0 → 0.33.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30853f93c0da945d2bb067d53fceba5fdc873ddc87c69369c364f2e61a5cce96
4
- data.tar.gz: b3564630a2abe168125dedb7d16d9d70ef1116a99f6f9596fa39e0e215a4a437
3
+ metadata.gz: 8f23ab2c7fb7e993b1d0e957104ec363d45512e7de084b3d8c539dd68f0944f0
4
+ data.tar.gz: 81786b021f4c5b2a27abcd589fc9844123f35c6c8b42495f56cbf1fba0f22778
5
5
  SHA512:
6
- metadata.gz: 812cc85caf14056ae5536b0847d1ccbe90b3ef74608c15168176aef9360b339e704c64c7c0c6b99f42951c3587c2d193bce82f09e98ccf0815504097b46e9507
7
- data.tar.gz: f932395d4a5e082d5e5ee9a2ddb9e0bba304cca73cb3f66c9856838f91ae34e96d096da22490af6b27cad405fbcd11b02a6e6be75a25eef9efb44bb4ce167b7f
6
+ metadata.gz: 9cac16658c6feadbdd5ea53d39f395527d4af3f620c1e06576e71f0f4bb8b59947f2aaec4da117eda384932e958e7c1cfe018d7573bc630d408142ce68abf916
7
+ data.tar.gz: 3d4964ca4aab54d6764072d8a1c015dc28c0ed2a4d84ec96877d62affc415eabf80094f74fbfc24cf88ca25c102f2f669560f0605b6f0474721c6ad0886cfab5
checksums.yaml.gz.sig CHANGED
@@ -1,4 +1,3 @@
1
- nm���|�X;��ѝ��1���ېg2����0.g]?�:>�*�6��,�wm��0�,9kl��>���4 ��$�?���qf����rn�O�F=w��^����.�NI>٭����&?�vh]�����^�iw�\
2
- |S���o(.���{�>��:�"���\���.O�����C��7�#H�(�"ӄ��w�n����7�7D���o��$H��S��q�t*,��zk�D�W%��M�V�fr1���GA;x�Y�+�����{a�:�6�P����u(���B�?P� |�О��:#:�Z��^���
3
- 3���ͣ��/J�E�BWJU֛�Ur
4
- ������#F�n�8�[�J�
1
+ r�V_ܬ���Mx 2y��o��9U1bRU9-���' 5����h�*�E6��c��{�[���ʧuwع7η��sz���O:�2���� ��
2
+ �
3
+ 4Ռm*�6���7&VS=�g��uc^�I�dQ��W�'=��ݲ�E�"��Od<7���Ȧ)���Λ��omI�v���(;�,�=��������exgu���%I���>����I���n�E��� t�1tn�5����8Za}"8 � |n�1p1���q�WQ�e6��X�R��<�0�-V�n&Vd�#�{�z@�,.D�o}���M'��5֦ v�sݤ�M6��h��K$����qv�vv��=������B�*��u$�ɟ? h�+E
@@ -0,0 +1,95 @@
1
+ # Mocking
2
+
3
+ There are two types of mocking in sus: `receive` and `mock`. The `receive` matcher is a subset of full mocking and is used to set expectations on method calls, while `mock` can be used to replace method implementations or set up more complex behavior.
4
+
5
+ Mocking non-local objects permanently changes the object's ancestors, so it should be used with care. For local objects, you can use `let` to define the object and then mock it.
6
+
7
+ Sus does not support the concept of test doubles, but you can use `receive` and `mock` to achieve similar functionality.
8
+
9
+ ## Method Call Expectations
10
+
11
+ The `receive(:method)` expectation is used to set up an expectation that a method will be called on an object. You can also specify arguments and return values. However, `receive` is not sequenced, meaning it does not enforce the order of method calls. If you need to enforce the order, use `mock` instead.
12
+
13
+ ```ruby
14
+ describe MyThing do
15
+ let(:my_thing) {subject.new}
16
+
17
+ it "calls the expected method" do
18
+ expect(my_thing).to receive(:my_method)
19
+
20
+ expect(my_thing.my_method).to be == 42
21
+ end
22
+ end
23
+ ```
24
+
25
+ ### With Arguments
26
+
27
+ ```ruby
28
+ it "calls the method with arguments" do
29
+ expect(object).to receive(:method_name).with(arg1, arg2)
30
+ # or .with_arguments(be == [arg1, arg2])
31
+ # or .with_options(be == {option1: value1, option2: value2})
32
+ # or .with_block
33
+
34
+ object.method_name(arg1, arg2)
35
+ end
36
+ ```
37
+
38
+ ### Returning Values
39
+
40
+ ```ruby
41
+ it "returns a value" do
42
+ expect(object).to receive(:method_name).and_return("expected value")
43
+ result = object.method_name
44
+ expect(result).to be == "expected value"
45
+ end
46
+ ```
47
+
48
+ ### Raising Exceptions
49
+
50
+ ```ruby
51
+ it "raises an exception" do
52
+ expect(object).to receive(:method_name).and_raise(StandardError, "error message")
53
+
54
+ expect{object.method_name}.to raise_exception(StandardError, message: "error message")
55
+ end
56
+ ```
57
+
58
+ ### Multiple Calls
59
+
60
+ ```ruby
61
+ it "calls the method multiple times" do
62
+ expect(object).to receive(:method_name).twice.and_return("result")
63
+ # or .with_call_count(be == 2)
64
+ expect(object.method_name).to be == "result"
65
+ expect(object.method_name).to be == "result"
66
+ end
67
+ ```
68
+
69
+ ## Mock Objects
70
+
71
+ Mock objects are used to replace method implementations or set up complex behavior. They can be used to intercept method calls, modify arguments, and control the flow of execution. They are thread-local, meaning they only affect the current thread, therefore are not suitable for use in tests that have multiple threads.
72
+
73
+ ```ruby
74
+ describe ApiClient do
75
+ let(:http_client) {Object.new}
76
+ let(:client) {ApiClient.new(http_client)}
77
+ let(:users) {["Alice", "Bob"]}
78
+
79
+ it "makes GET requests" do
80
+ mock(http_client) do |mock|
81
+ mock.replace(:get) do |url, headers: {}|
82
+ expect(url).to be == "/api/users"
83
+ expect(headers).to be == {"accept" => "application/json"}
84
+ users.to_json
85
+ end
86
+
87
+ # or mock.before {|...| ...}
88
+ # or mock.after {|...| ...}
89
+ # or mock.wrap(:new) {|original, ...| original.call(...)}
90
+ end
91
+
92
+ expect(client.fetch_users).to be == users
93
+ end
94
+ end
95
+ ```
data/context/shared.md ADDED
@@ -0,0 +1,185 @@
1
+ # Shared Test Behaviors and Fixtures
2
+
3
+ ## Overview
4
+
5
+ Sus provides shared test contexts which can be used to define common behaviours or tests that can be reused across one or more test files.
6
+
7
+ When you have common test behaviors that you want to apply to multiple test files, add them to the `fixtures/` directory. When you have common test behaviors that you want to apply to multiple implementations of the same interface, within a single test file, you can define them as shared contexts within that file.
8
+
9
+ ## Shared Fixtures
10
+
11
+ ### Directory Structure
12
+
13
+ ```
14
+ my-gem/
15
+ ├── lib/
16
+ │ ├── my_gem.rb
17
+ │ └── my_gem/
18
+ │ └── my_thing.rb
19
+ ├── fixtures/
20
+ │ └── my_gem/
21
+ │ └── a_thing.rb # Provides MyGem::AThing shared context
22
+ └── test/
23
+ ├── my_gem.rb
24
+ └── my_gem/
25
+ └── my_thing.rb
26
+ ```
27
+
28
+ ### Creating Shared Fixtures
29
+
30
+ Create shared behaviors in the `fixtures/` directory using `Sus::Shared`:
31
+
32
+ ```ruby
33
+ # fixtures/my_gem/a_user.rb
34
+
35
+ require "sus/shared"
36
+
37
+ module MyGem
38
+ AUser = Sus::Shared("a user") do |role|
39
+ let(:user) do
40
+ {
41
+ name: "Test User",
42
+ email: "test@example.com",
43
+ role: role
44
+ }
45
+ end
46
+
47
+ it "has a name" do
48
+ expect(user[:name]).not.to be_nil
49
+ end
50
+
51
+ it "has a valid email" do
52
+ expect(user[:email]).to be(:include?, "@")
53
+ end
54
+
55
+ it "has a role" do
56
+ expect(user[:role]).to be_a(String)
57
+ end
58
+ end
59
+ end
60
+ ```
61
+
62
+ ### Using Shared Fixtures
63
+
64
+ Require and use shared fixtures in your test files:
65
+
66
+ ```ruby
67
+ # test/my_gem/user_manager.rb
68
+ require 'my_gem/a_user'
69
+
70
+ describe MyGem::UserManager do
71
+ it_behaves_like MyGem::AUser, "manager"
72
+ # or include_context MyGem::AUser, "manager"
73
+ end
74
+ ```
75
+
76
+ ### Multiple Shared Fixtures
77
+
78
+ You can create multiple shared fixtures for different scenarios:
79
+
80
+ ```ruby
81
+ # fixtures/my_gem/users.rb
82
+ module MyGem
83
+ module Users
84
+ AStandardUser = Sus::Shared("a standard user") do
85
+ let(:user) do
86
+ { name: "John Doe", role: "user", active: true }
87
+ end
88
+
89
+ it "is active" do
90
+ expect(user[:active]).to be_truthy
91
+ end
92
+ end
93
+
94
+ AnAdminUser = Sus::Shared("an admin user") do
95
+ let(:user) do
96
+ { name: "Admin User", role: "admin", active: true }
97
+ end
98
+
99
+ it "has admin role" do
100
+ expect(user[:role]).to be == "admin"
101
+ end
102
+ end
103
+ end
104
+ end
105
+ ```
106
+
107
+ Use specific shared fixtures:
108
+
109
+ ```ruby
110
+ # test/my_gem/authorization.rb
111
+ require 'my_gem/users'
112
+
113
+ describe MyGem::Authorization do
114
+ with "standard user" do
115
+ # If there are no arguments, you can use `include` directly:
116
+ include MyGem::Users::AStandardUser
117
+
118
+ it "denies admin access" do
119
+ auth = subject.new
120
+ expect(auth.can_admin?(user)).to be_falsey
121
+ end
122
+ end
123
+
124
+ with "admin user" do
125
+ include MyGem::Users::AnAdminUser
126
+
127
+ it "allows admin access" do
128
+ auth = subject.new
129
+ expect(auth.can_admin?(user)).to be_truthy
130
+ end
131
+ end
132
+ end
133
+ ```
134
+
135
+ ### Modules
136
+
137
+ You can also define shared behaviors in modules and include them in your test files:
138
+
139
+ ```ruby
140
+ # fixtures/my_gem/shared_behaviors.rb
141
+ module MyGem
142
+ module SharedBehaviors
143
+ def self.included(base)
144
+ base.it "uses shared data" do
145
+ expect(shared_data).to be == "some shared data"
146
+ end
147
+ end
148
+
149
+ def shared_data
150
+ "some shared data"
151
+ end
152
+ end
153
+ end
154
+ ```
155
+
156
+ ### Enumerating Tests
157
+
158
+ Some tests will be run multiple times with different arguments (for example, multiple database adapters). You can use `Sus::Shared` to define these tests and then enumerate them:
159
+
160
+ ```ruby
161
+ # test/my_gem/database_adapter.rb
162
+
163
+ require "sus/shared"
164
+
165
+ ADatabaseAdapter = Sus::Shared("a database adapter") do |adapter|
166
+ let(:database) {adapter.new}
167
+
168
+ it "connects to the database" do
169
+ expect(database.connect).to be_truthy
170
+ end
171
+
172
+ it "can execute queries" do
173
+ expect(database.execute("SELECT 1")).to be == [[1]]
174
+ end
175
+ end
176
+
177
+ # Enumerate the tests with different adapters
178
+ MyGem::DatabaseAdapters.each do |adapter|
179
+ describe "with #{adapter}", unique: adapter.name do
180
+ it_behaves_like ADatabaseAdapter, adapter
181
+ end
182
+ end
183
+ ```
184
+
185
+ Note the use of `unique: adapter.name` to ensure each test is uniquely identified, which is useful for reporting and debugging - otherwise the same test line number would be used for all iterations, which can make it hard to identify which specific test failed.
data/context/usage.md ADDED
@@ -0,0 +1,380 @@
1
+ # Using Sus Testing Framework
2
+
3
+ ## Overview
4
+
5
+ Sus is a modern Ruby testing framework that provides a clean, BDD-style syntax for writing tests. It's designed to be fast, simple, and expressive.
6
+
7
+ ## Basic Structure
8
+
9
+ Here is an example structure for testing with Sus - the actual structure may vary based on your gem's organization, but aside from the `lib/` directory, sus expects the following structure:
10
+
11
+ ```
12
+ my-gem/
13
+ ├── config/
14
+ │ └── sus.rb # Sus configuration file
15
+ ├── lib/
16
+ │ ├── my_gem.rb
17
+ │ └── my_gem/
18
+ │ └── my_thing.rb
19
+ ├── fixtures/
20
+ │ └── my_gem/
21
+ │ └── a_thing.rb # Provides MyGem::AThing shared context
22
+ └── test/
23
+ ├── my_gem.rb # Tests MyGem
24
+ └── my_gem/
25
+ └── my_thing.rb # Tests MyGem::MyThing
26
+ ```
27
+
28
+ ### Configuration File
29
+
30
+ Create `config/sus.rb`:
31
+
32
+ ```ruby
33
+ # frozen_string_literal: true
34
+
35
+ # Use the covered gem for test coverage reporting:
36
+ require 'covered/sus'
37
+ include Covered::Sus
38
+
39
+ def before_tests(assertions, output: self.output)
40
+ # Starts the clock and sets up the test environment:
41
+ super
42
+ end
43
+
44
+ def after_tests(assertions, output: self.output)
45
+ # Stops the clock and prints the test results:
46
+ super
47
+ end
48
+ ```
49
+
50
+ ### Fixtures Files
51
+
52
+ `fixtures/` gets added to the `$LOAD_PATH` automatically, so you can require files from there without needing to specify the full path.
53
+
54
+ ### Test Files
55
+
56
+ Sus runs all Ruby files in the `test/` directory by default. But you can also create tests in any file, and run them with the `sus my_tests.rb` command.
57
+
58
+ ## Basic Syntax
59
+
60
+ ```ruby
61
+ # frozen_string_literal: true
62
+
63
+ describe MyThing do
64
+ let(:my_thing) {subject.new}
65
+
66
+ with "#my_method" do
67
+ it "does something" do
68
+ expect(my_thing.my_method).to be == 42
69
+ end
70
+ end
71
+ end
72
+ ```
73
+
74
+ ### `describe` - Test Groups
75
+
76
+ Use `describe` to group related tests:
77
+
78
+ ```ruby
79
+ describe MyThing do
80
+ # The subject will be whatever is described:
81
+ let(:my_thing) {subject.new}
82
+ end
83
+ ```
84
+
85
+ ### `it` - Individual Tests
86
+
87
+ Use `it` to define individual test cases:
88
+
89
+ ```ruby
90
+ it "returns the expected value" do
91
+ expect(result).to be == "expected"
92
+ end
93
+ ```
94
+
95
+ You can use `it` blocks at the top level or within `describe` or `with` blocks.
96
+
97
+ ### `with` - Context Blocks
98
+
99
+ Use `with` to create context-specific test groups:
100
+
101
+ ```ruby
102
+ with "valid input" do
103
+ let(:input) {"valid input"}
104
+ it "succeeds" do
105
+ expect{my_thing.process(input)}.not.to raise_exception
106
+ end
107
+ end
108
+
109
+ # Non-lazy state can be provided as keyword arguments:
110
+ with "invalid input", input: nil do
111
+ it "raises an error" do
112
+ expect{my_thing.process(input)}.to raise_exception(ArgumentError)
113
+ end
114
+ end
115
+ ```
116
+
117
+ When testing methods, use `with` to specify the method being tested:
118
+
119
+ ```ruby
120
+ with "#my_method" do
121
+ it "results a value" do
122
+ expect(my_thing.method).to be == 42
123
+ end
124
+ end
125
+
126
+ with ".my_class_method" do
127
+ it "returns a value" do
128
+ expect(MyThing.class_method).to be == "class value"
129
+ end
130
+ end
131
+ ```
132
+
133
+ ### `let` - Lazy Variables
134
+
135
+ Use `let` to define variables that are evaluated when first accessed:
136
+
137
+ ```ruby
138
+ let(:helper) {subject.new}
139
+ let(:test_data) {"test value"}
140
+
141
+ it "uses the helper" do
142
+ expect(helper.process(test_data)).to be_truthy
143
+ end
144
+ ```
145
+
146
+ ### `before` and `after` - Setup/Teardown
147
+
148
+ Use `before` and `after` for setup and teardown logic:
149
+
150
+ ```ruby
151
+ before do
152
+ # Setup logic.
153
+ end
154
+
155
+ after do
156
+ # Cleanup logic.
157
+ end
158
+ ```
159
+
160
+ Error handling in `after` allows you to perform cleanup even if the test fails with an exception (not a test failure).
161
+
162
+ ```ruby
163
+ after do |error = nil|
164
+ if error
165
+ # The state of the test is unknown, so you may want to forcefully kill processes or clean up resources.
166
+ Process.kill(:KILL, @child_pid)
167
+ else
168
+ # Normal cleanup logic.
169
+ Process.kill(:TERM, @child_pid)
170
+ end
171
+
172
+ Process.waitpid(@child_pid)
173
+ end
174
+ ```
175
+
176
+ ### `around` - Setup/Teardown
177
+
178
+ Use `around` for setup and teardown logic:
179
+
180
+ ```ruby
181
+ around do |&block|
182
+ # Setup logic.
183
+ super() do
184
+ # Run the test.
185
+ block.call
186
+ end
187
+ ensure
188
+ # Cleanup logic.
189
+ end
190
+ ```
191
+
192
+ Invoking `super()` calls any parent `around` block, allowing you to chain setup and teardown logic.
193
+
194
+ ## Assertions
195
+
196
+ ### Basic Assertions
197
+
198
+ ```ruby
199
+ expect(value).to be == expected
200
+ exepct(value).to be >= 10
201
+ expect(value).to be <= 100
202
+ expect(value).to be > 0
203
+ expect(value).to be < 1000
204
+ expect(value).to be_truthy
205
+ expect(value).to be_falsey
206
+ expect(value).to be_nil
207
+ expect(value).to be_equal(another_value)
208
+ expect(value).to be_a(Class)
209
+ ```
210
+
211
+ ### Strings
212
+
213
+ ```ruby
214
+ expect(string).to be(:start_with?, "prefix")
215
+ expect(string).to be(:end_with?, "suffix")
216
+ expect(string).to be(:match?, /pattern/)
217
+ expect(string).to be(:include?, "substring")
218
+ ```
219
+
220
+ ### Ranges and Tolerance
221
+
222
+ ```ruby
223
+ expect(value).to be_within(0.1).of(5.0)
224
+ expect(value).to be_within(5).percent_of(100)
225
+ ```
226
+
227
+ ### Method Calls
228
+
229
+ To call methods on the expected object:
230
+
231
+ ```ruby
232
+ expect(array).to be(:include?, "value")
233
+ expect(string).to be(:start_with?, "prefix")
234
+ expect(object).to be(:respond_to?, :method_name)
235
+ ```
236
+
237
+ ### Collection Assertions
238
+
239
+ ```ruby
240
+ expect(array).to have_attributes(length: be == 1)
241
+ expect(array).to have_value(be > 1)
242
+
243
+ expect(hash).to have_keys(:key1, "key2")
244
+ expect(hash).to have_keys(key1: be == 1, "key2" => be == 2)
245
+ ```
246
+
247
+ ### Attribute Testing
248
+
249
+ ```ruby
250
+ expect(user).to have_attributes(
251
+ name: be == "John",
252
+ age: be >= 18,
253
+ email: be(:include?, "@")
254
+ )
255
+ ```
256
+
257
+ ### Exception Assertions
258
+
259
+ ```ruby
260
+ expect do
261
+ risky_operation
262
+ end.to raise_exception(RuntimeError, message: be =~ /expected error message/)
263
+ ```
264
+
265
+ ## Combining Predicates
266
+
267
+ Predicates can be nested.
268
+
269
+ ```ruby
270
+ expect(user).to have_attributes(
271
+ name: have_attributes(
272
+ first: be == "John",
273
+ last: be == "Doe"
274
+ ),
275
+ comments: have_value(be =~ /test comment/),
276
+ created_at: be_within(1.minute).of(Time.now)
277
+ )
278
+ ```
279
+
280
+ ### Logical Combinations
281
+
282
+ ```ruby
283
+ expect(value).to (be > 10).and(be < 20)
284
+ expect(value).to be_a(String).or(be_a(Symbol), be_a(Integer))
285
+ ```
286
+
287
+ ### Custom Predicates
288
+
289
+ You can create custom predicates for more complex assertions:
290
+
291
+ ```ruby
292
+ def be_small_prime
293
+ (be == 2).or(be == 3, be == 5, be == 7)
294
+ end
295
+ ```
296
+
297
+ ## Block Expectations
298
+
299
+ ### Testing Blocks
300
+
301
+ ```ruby
302
+ expect{operation}.to raise_exception(Error)
303
+ expect{operation}.to have_duration(be < 1.0)
304
+ ```
305
+
306
+ ### Performance Testing
307
+
308
+ You should generally avoid testing performance in unit tests, as it will be highly unstable and dependent on the environment. However, if you need to test performance, you can use:
309
+
310
+ ```ruby
311
+ expect{slow_operation}.to have_duration(be < 2.0)
312
+ expect{fast_operation}.to have_duration(be < 0.1)
313
+ ```
314
+
315
+ - For less unsable performance tests, you can use the `sus-fixtures-time` gem which tries to compensate for the environment by measuring execution time.
316
+
317
+ - For benchmarking, you can use the `sus-fixtures-benchmark` gem which measures a block of code multiple times and reports the execution time.
318
+
319
+ ## File Operations
320
+
321
+ ### Temporary Directories
322
+
323
+ Use `Dir.mktmpdir` for isolated test environments:
324
+
325
+ ```ruby
326
+ around do |block|
327
+ Dir.mktmpdir do |root|
328
+ @root = root
329
+ block.call
330
+ end
331
+ end
332
+
333
+ let(:test_path) {File.join(@root, "test.txt")}
334
+
335
+ it "can create a file" do
336
+ File.write(test_path, "content")
337
+ expect(File).to be(:exist?, test_path)
338
+ end
339
+ ```
340
+
341
+ ## Test Output
342
+
343
+ In general, tests should not produce output unless there is an error or failure.
344
+
345
+ ### Informational Output
346
+
347
+ You can use `inform` to print informational messages during tests:
348
+
349
+ ```ruby
350
+ it "logs an informational message" do
351
+ rate = copy_data(source, destination)
352
+ inform "Copied data at #{rate}MB/s"
353
+ expect(rate).to be > 0
354
+ end
355
+ ```
356
+
357
+ This can be useful for debugging or providing context during test runs.
358
+
359
+ ### Console Output
360
+
361
+ The `sus-fixtures-console` gem provides a way to surpress and capture console output during tests. If you are using code which generates console output, you can use this gem to capture it and assert on it.
362
+
363
+ ## Running Tests
364
+
365
+ ```bash
366
+ # Run all tests
367
+ bundle exec sus
368
+
369
+ # Run specific test file
370
+ bundle exec sus test/specific_test.rb
371
+ ```
372
+
373
+ ## Best Practices
374
+
375
+ 1. **Use real objects** instead of mocks when possible.
376
+ 2. **Dependency injection** for testability.
377
+ 3. **Isolatae mutable state** using temporary directories.
378
+ 4. **Clear test descriptions** that explain the behavior.
379
+ 5. **Group tests** with `describe` (classes) and `with` for better organization.
380
+ 6. **Keep tests simple** and focused on one behavior.
data/lib/sus/receive.rb CHANGED
@@ -1,15 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022-2024, by Samuel Williams.
4
+ # Copyright, 2022-2025, by Samuel Williams.
5
5
 
6
6
  require_relative "respond_to"
7
7
 
8
8
  module Sus
9
9
  class Receive
10
- CALL_ORIGINAL = Object.new
11
-
12
- def initialize(base, method)
10
+ def initialize(base, method, &block)
13
11
  @base = base
14
12
  @method = method
15
13
 
@@ -17,7 +15,8 @@ module Sus
17
15
  @arguments = nil
18
16
  @options = nil
19
17
  @block = nil
20
- @returning = CALL_ORIGINAL
18
+
19
+ @returning = block
21
20
  end
22
21
 
23
22
  def print(output)
@@ -60,12 +59,27 @@ module Sus
60
59
  return self
61
60
  end
62
61
 
63
- def and_return(*returning)
64
- if returning.size == 1
65
- @returning = returning.first
62
+ def and_return(*returning, &block)
63
+ if block_given?
64
+ if returning.any?
65
+ raise ArgumentError, "Cannot specify both a block and returning values."
66
+ end
67
+
68
+ @returning = block
69
+ elsif returning.size == 1
70
+ @returning = proc{returning.first}
66
71
  else
67
- @returning = returning
72
+ @returning = proc{returning}
73
+ end
74
+
75
+ return self
76
+ end
77
+
78
+ def and_raise(...)
79
+ @returning = proc do
80
+ raise(...)
68
81
  end
82
+
69
83
  return self
70
84
  end
71
85
 
@@ -97,7 +111,7 @@ module Sus
97
111
 
98
112
  validate(mock, assertions, arguments, options, block)
99
113
 
100
- next @returning
114
+ next @returning.call(*arguments, **options, &block)
101
115
  end
102
116
  end
103
117
 
@@ -110,7 +124,7 @@ module Sus
110
124
  end
111
125
 
112
126
  def call_original?
113
- @returning == CALL_ORIGINAL
127
+ @returning.nil?
114
128
  end
115
129
 
116
130
  class WithArguments
@@ -128,7 +142,7 @@ module Sus
128
142
  end
129
143
  end
130
144
  end
131
-
145
+
132
146
  class WithOptions
133
147
  def initialize(predicate)
134
148
  @predicate = predicate
@@ -153,7 +167,7 @@ module Sus
153
167
  def print(output)
154
168
  output.write("with block", @predicate)
155
169
  end
156
-
170
+
157
171
  def call(assertions, subject)
158
172
  assertions.nested(self) do |assertions|
159
173
 
@@ -161,7 +175,7 @@ module Sus
161
175
  end
162
176
  end
163
177
  end
164
-
178
+
165
179
  class Times
166
180
  ONCE = Be.new(:==, 1)
167
181
 
@@ -172,7 +186,7 @@ module Sus
172
186
  def print(output)
173
187
  output.write("with call count ", @condition)
174
188
  end
175
-
189
+
176
190
  def call(assertions, subject)
177
191
  assertions.nested(self) do |assertions|
178
192
  Expect.new(assertions, subject).to(@condition)
@@ -182,8 +196,8 @@ module Sus
182
196
  end
183
197
 
184
198
  class Base
185
- def receive(method)
186
- Receive.new(self, method)
199
+ def receive(method, &block)
200
+ Receive.new(self, method, &block)
187
201
  end
188
202
  end
189
203
  end
data/lib/sus/version.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2024, by Samuel Williams.
4
+ # Copyright, 2021-2025, by Samuel Williams.
5
5
 
6
6
  module Sus
7
- VERSION = "0.32.0"
7
+ VERSION = "0.33.1"
8
8
  end
data/license.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MIT License
2
2
 
3
- Copyright, 2021-2024, by Samuel Williams.
3
+ Copyright, 2021-2025, by Samuel Williams.
4
4
  Copyright, 2022, by Brad Schrag.
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
data/readme.md CHANGED
@@ -29,6 +29,11 @@ Please see the [project documentation](https://socketry.github.io/sus/) for more
29
29
 
30
30
  Please see the [project releases](https://socketry.github.io/sus/releases/index) for all releases.
31
31
 
32
+ ### v0.33.0
33
+
34
+ - Add support for `agent-context` gem.
35
+ - [`receive` now supports blocks and `and_raise`.](https://socketry.github.io/sus/releases/index#receive-now-supports-blocks-and-and_raise.)
36
+
32
37
  ### v0.32.0
33
38
 
34
39
  - `Sus::Config` now has a `prepare_warnings!` hook which enables deprecated warnings by default. This is generally considered good behaviour for a test framework.
data/releases.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Releases
2
2
 
3
+ ## v0.33.0
4
+
5
+ - Add support for `agent-context` gem.
6
+
7
+ ### `receive` now supports blocks and `and_raise`.
8
+
9
+ The `receive` predicate has been enhanced to support blocks and the `and_raise` method, allowing for more flexible mocking of method calls.
10
+
11
+ ``` ruby
12
+ # `receive` with a block:
13
+ expect(interface).to receive(:implementation) {10}
14
+
15
+ # `and_return` with a block:
16
+ expect(interface).to receive(:implementation).and_return{FakeImplementation.new}
17
+
18
+ # `and_raise` for error handling:
19
+ expect(interface).to receive(:implementation).and_raise(StandardError, "An error occurred")
20
+ ```
21
+
3
22
  ## v0.32.0
4
23
 
5
24
  - `Sus::Config` now has a `prepare_warnings!` hook which enables deprecated warnings by default. This is generally considered good behaviour for a test framework.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,12 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.32.0
4
+ version: 0.33.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  - Brad Schrag
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain:
12
11
  - |
@@ -38,15 +37,13 @@ cert_chain:
38
37
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
39
38
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
40
39
  -----END CERTIFICATE-----
41
- date: 2024-11-08 00:00:00.000000000 Z
40
+ date: 1980-01-02 00:00:00.000000000 Z
42
41
  dependencies: []
43
- description:
44
- email:
45
42
  executables:
46
43
  - sus
44
+ - sus-host
47
45
  - sus-parallel
48
46
  - sus-tree
49
- - sus-host
50
47
  extensions: []
51
48
  extra_rdoc_files: []
52
49
  files:
@@ -54,6 +51,9 @@ files:
54
51
  - bin/sus-host
55
52
  - bin/sus-parallel
56
53
  - bin/sus-tree
54
+ - context/mocking.md
55
+ - context/shared.md
56
+ - context/usage.md
57
57
  - lib/sus.rb
58
58
  - lib/sus/assertions.rb
59
59
  - lib/sus/base.rb
@@ -109,7 +109,6 @@ metadata:
109
109
  documentation_uri: https://socketry.github.io/sus/
110
110
  funding_uri: https://github.com/sponsors/ioquatix/
111
111
  source_code_uri: https://github.com/socketry/sus.git
112
- post_install_message:
113
112
  rdoc_options: []
114
113
  require_paths:
115
114
  - lib
@@ -117,15 +116,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
117
116
  requirements:
118
117
  - - ">="
119
118
  - !ruby/object:Gem::Version
120
- version: '3.1'
119
+ version: '3.2'
121
120
  required_rubygems_version: !ruby/object:Gem::Requirement
122
121
  requirements:
123
122
  - - ">="
124
123
  - !ruby/object:Gem::Version
125
124
  version: '0'
126
125
  requirements: []
127
- rubygems_version: 3.5.22
128
- signing_key:
126
+ rubygems_version: 3.6.7
129
127
  specification_version: 4
130
128
  summary: A fast and scalable test runner.
131
129
  test_files: []
metadata.gz.sig CHANGED
Binary file