README.md in mocktail-1.1.3 vs README.md in mocktail-1.2.0

- old
+ new

@@ -10,11 +10,11 @@ getting them in the hands of the code you're testing, stub & verify behavior, and even safely override class methods. If you'd prefer a voice & video introduction to Mocktail aside from this README, you might enjoy this ⚡️[Lightning -Talk](https://blog.testdouble.com/talks/2022-05-18-please-mock-me?utm_source=twitter&utm_medium=organic-social&utm_campaign=conf-talk)⚡️ +Talk](https://blog.testdouble.com/talks/2022-05-18-please-mock-me?utm_source=twitter&utm_medium=organic-social&utm_campaign=conf-talk)⚡️ from RailsConf 2022. ## An aperitif Before getting into the details, let's demonstrate what Mocktail's API looks @@ -402,10 +402,14 @@ * `ignore_extra_args` will allow the demonstration to forego specifying optional arguments while still being considered satisfied * `ignore_block` will similarly allow the demonstration to forego specifying a block, even if the actual call receives one +Note that if you want to verify a method _wasn't_ called at all or called a +specific number of times—especially if you don't care about the parameters, you +may want to look at the [Mocktail.calls()](#mocktailcalls) API. + ### Mocktail.matchers You'll probably never need to call `Mocktail.matchers` directly, because it's the object that is passed to every demonstration block passed to `stubs` and `verify`. By default, a stubbing (e.g. `stubs { email.send("text") }`) is only @@ -804,9 +808,57 @@ ``` The `reference` object will have details of the `call` itself, an array of `other_stubbings` defined on the faked method, and a `backtrace` to determine which call site produced the unexpected `nil` value. + +### Mocktail.calls + +When practicing test-driven development, you may want to ensure that a +dependency wasn't called at all, and don't particularly care about the +parameters. To provide a terse way to express this, Mocktail offers a top-level +`calls(double, method_name = nil)` method that returns an array of the calls to +the mock (optionally filtered to a particular method name) in the order they +were called. + +Suppose you were writing a test of this method for example: + +```ruby +def import_users + users_response = @gets_users.get + if users_response.success? + @upserts_users.upsert(users_response.data) + end +end +``` + +A test case of the negative branch of that `if` statement (when `success?` is +false) might simply want to assert that `@upserts_users.upsert` wasn't called at +all, regardless of its parameters. + +The easiest way to do this is to use `Mocktail.calls()` method, which is an +alias of [Mocktail.explain(double).reference.calls](#mocktailexplain) that can +filter to a specific method name. In the case of a test of the above method, you +could assert: + +```ruby +# Assert that the `upsert` method on the mock was never called +assert_equal 0, Mocktail.calls(@upserts_users, :upsert).size + +# Assert that NO METHODS on the mock were called at all: +assert_equal 0, Mocktail.calls(@upserts_users).size +``` + +If you're interested in doing more complicated introspection in the nature of +the calls, their ordering, and so forth, the `calls` method will return +`Mocktail::Call` values with the args, kwargs, block, and information about the +original class and method being mocked. + +(While this behavior can technically be accomplished with `verify(times: 0) { … +}`, it's verbose and error prone to do so. Because `verify` is careful to only +assert exact argument matches, it can get pretty confusing to remember to tack +on `ignore_extra_args: true` and to call the method with zero args to cover all +cases.) ### Mocktail.reset This one's simple: you probably want to call `Mocktail.reset` after each test, but you _definitely_ want to call it if you're using `Mocktail.replace` or