{"id":13503006,"url":"https://github.com/DonaldWhyte/double","last_synced_at":"2025-03-29T13:30:34.845Z","repository":{"id":57621637,"uuid":"78951715","full_name":"DonaldWhyte/double","owner":"DonaldWhyte","description":"Mocking framework for Rust -- inspired by googlemock","archived":false,"fork":false,"pushed_at":"2019-09-22T14:38:07.000Z","size":142,"stargazers_count":109,"open_issues_count":6,"forks_count":6,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-31T22:33:32.612Z","etag":null,"topics":["mock","mocking-framework","rust-library","testing","unit-testing"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DonaldWhyte.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-14T16:28:52.000Z","updated_at":"2024-07-31T04:59:20.000Z","dependencies_parsed_at":"2022-09-19T14:53:20.997Z","dependency_job_id":null,"html_url":"https://github.com/DonaldWhyte/double","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DonaldWhyte%2Fdouble","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DonaldWhyte%2Fdouble/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DonaldWhyte%2Fdouble/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DonaldWhyte%2Fdouble/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DonaldWhyte","download_url":"https://codeload.github.com/DonaldWhyte/double/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246190171,"owners_count":20737985,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mock","mocking-framework","rust-library","testing","unit-testing"],"created_at":"2024-07-31T22:02:33.216Z","updated_at":"2025-03-29T13:30:34.544Z","avatar_url":"https://github.com/DonaldWhyte.png","language":"Rust","readme":"# Double\n\n### Full-featured mocking library in Rust with feature-rich argument matchers.\n\n[![Build Status](https://travis-ci.org/DonaldWhyte/double.svg?branch=master)](https://travis-ci.org/DonaldWhyte/double) [![Docs](https://docs.rs/double/badge.svg)](https://docs.rs/double)\n\nDouble lets you mock `trait` implementations so that you can track function call arguments and set return values or overrides functions at test time.\n\nHere's a quick example:\n\n```rust\n#[macro_use]\nextern crate double;\n\n// Code under test\ntrait BalanceSheet {\n    fn profit(\u0026self, revenue: u32, costs: u32) -\u003e i32;\n}\n\nfn double_profit(revenue: u32, costs: u32, balance_sheet: \u0026BalanceSheet) -\u003e i32 {\n    balance_sheet.profit(revenue, costs) * 2\n}\n\n// Test which uses a mock BalanceSheet\nmock_trait!(\n    MockBalanceSheet,\n    profit(u32, u32) -\u003e i32);\nimpl BalanceSheet for MockBalanceSheet {\n    mock_method!(profit(\u0026self, revenue: u32, costs: u32) -\u003e i32);\n}\n\nfn test_doubling_a_sheets_profit() {\n    // GIVEN:\n    let sheet = MockBalanceSheet::default();\n    sheet.profit.return_value(250);\n    // WHEN:\n    let profit = double_profit(500, 250, \u0026sheet);\n    // THEN:\n    // mock returned 250, which was doubled\n    assert_eq!(500, profit);\n    // assert that the revenue and costs were correctly passed to the mock\n    sheet.profit.has_calls_exactly_in_order(vec!((500, 250)));\n}\n\n// Executing test\nfn main() {\n    test_doubling_a_sheets_profit();\n}\n```\n\nMore examples are available in the [examples directory](./examples).\n\n### Defining a Mock\n\nMocking a `trait` requires two steps. One to generate the mock `struct` that will implement the mock and another to generate the bodies of the mocked `trait` methods.\n\nFor step one, we use the `mock_trait` macro. This takes the name of the mock `struct` to generate and a list specifying all of the `trait`'s methods, their arguments (omitting `self`) and their return values (specifying `-\u003e ()` if the method does not return a value).\n\nConsider the example below:\n\n```rust\ntrait BalanceSheet {\n    fn profit(\u0026self, revenue: u32, costs: u32) -\u003e i32;\n    fn clear(\u0026mut self);\n}\n\nmock_trait!(\n    MockBalanceSheet,\n    profit(u32, u32) -\u003e i32,\n    clear() -\u003e ());\n```\n\nHere, we generate a `struct` called `MockBalanceSheet`. This struct contains all the necessary data to store the number of types each method is called, what arguments they are invoked with and what values each method should return when invoked. This data is stored per-method, with the `struct` having a `double::Mock` field for each method. This is why all of the `trait`'s methods must be declared when the `struct` is generated.\n\nFor step 2, we generate the bodies of the mocked methods. The generated bodies contain boilerplate code for passing the method's arguments to the underlying `double::Mock` objects using `mock_method`. For example:\n\n```rust\nimpl BalanceSheet for MockBalanceSheet {\n    mock_method!(profit(\u0026self, revenue: u32, costs: u32) -\u003e i32);\n    mock_method!(clear(\u0026mut self));\n}\n```\n\n\u003e Notice how both immutable and mutable methods can be specified. One just passes `\u0026self` or `\u0026mut self` to `mock_method`, depending on whether the `trait` being mocked specifies the method as immutable or mutable.\n\nAfter both of these steps, the mock object is ready to use.\n\n### Using a Mock\n\nTests with mocks are typically structured like so:\n\n1. **GIVEN**: create mock objects and specify what values they return\n2. **WHEN**: run code under test, passing mock objects to it\n3. **THEN**: assert mocks were called the expected number of times, with the expected arguments\n\nFor example, suppose we wish to test some code that uses a `BalanceSheet` generate a HTML page showing the current profit of something:\n\n```rust\nfn generate_profit_page\u003cT: BalanceSheet\u003e(revenue: u32, costs: u32, sheet: \u0026T) {\n    let profit_str = sheet.profit(revenue, costs).to_string();\n    return \"\u003chtml\u003e\u003cbody\u003e\u003cp\u003eProfit is: $\" + profit_str + \"\u003c/p\u003e\u003c/body\u003e\u003c/html\u003e\";\n}\n```\n\nWe can use our generated `MockBalanceSheet` to test this function:\n\n```rust\nfn test_balance {\n    // GIVEN:\n    // create instance of mock and configure its behaviour (will return 42)\n    let mock = MockBalanceSheet::default();\n    mock.profit.return_value(42);\n\n    // WHEN:\n    // run code under test\n    let page = generate_profit_page(30, 20);\n\n    // THEN:\n    // ensure mock affected output in the right away\n    assert_eq!(\"\u003chtml\u003e\u003cbody\u003e\u003cp\u003eProfit is: $42\u003c/p\u003e\u003c/body\u003e\u003c/html\u003e\")\n    // also assert that the mock's profit() method was called _exactly_ once,\n    // with the arguments 30 (for revenue) and 20 (for costs).\n    assert_true!(mock.profit.has_calls_exactly(\n        vec!((30, 20))\n    ));\n}\n```\n\n#### GIVEN: Setting Mock Behaviour\n\nMocks can be configured to return a single value, a sequence of values (one value for each call) or invoke a function/closure. Additionally, it is possible to make a mock return special values/invoke special functions when specific arguments are passed in.\n\nThese behaviours are configured by invoking methods on the mock objects themselves. These methods are listed in the table below.\n\n| Method | What It Does |\n| ------ | ------------ |\n| `use_fn_for((args), dyn Fn(...) -\u003e retval)` | invoke given function and return the value it returns when specified `(args)` are passed in |\n| `use_closure_for((args), \u0026dyn Fn(...) -\u003e retval)` | invoke given closure and return the value it returns when specified `(args)` are passed in |\n| `return_value_for((args), val)` | return `val` when specified `(args)` are passed in |\n| `use_fn(dyn Fn(...) -\u003e retval)` | invoke given function and return the value it returns by default |\n| `use_closure(\u0026dyn Fn(...) -\u003e retval)` | invoke given closure and return the value it returns by default |\n| `return_values(vec\u003cretval\u003e)` | return values in given vector by default, return one value for each invocation of the mock method. If there are no more values in the vector, return the default value specified by `return_value()`  |\n| `return_value(val)` | return `val` by default |\n\nIf no behaviour is specified, the mock will just return the default value of the return type, as specified by the `Default` trait.\n\nExample usage:\n\n```rust\n// Configure mock to return 9001 profit when given args 42 and 10. Any other\n// arguments will cause the mock to return a profit of 1.\nlet sheet = MockBalanceSheet::default();\nsheet.profit.return_value_for((42, 10), 9001);\nsheet.profit.return_value(1);\n\n// Configure mock to call arbitrary function. The mock will return the\n// result of the function back to the caller.\nfn subtract(revenue: u32, costs: u32) -\u003e i32 {\n    revenue - costs\n}\nlet sheet2 = MockBalanceSheet::default();\nsheet.use_fn(subtract);\n```\n\nCode examples on how to use these are available in the [**rustdocs**](https://docs.rs/double).\n\nIt is possible to use many of these in conjunction. For example, one can tell a mock to return a specific value for args `(42, 10)` using `return_value_for()`, but return the default value of 1 for everything else using `return_value()`.\n\nWhen a mock method is invoked, it uses a precdence order to determine if it should return a default value, return a specific value, invoke a function and so on.\n\nThe precedence order of these methods is the same order they are specified in the above table. For example, if `use_fn` and `return_value` are invoked, then the mock will invoke the function passed to `use_fn` and not return a value.\n\nIf a method returns an `Option\u003cT\u003e` or a `Result\u003cT, E\u003e`, then one can use the following convenience functions for specifying default return values:\n\n| Method        | Returns     | What It Does                         |\n| ------------- | ----------- | ------------------------------------ |\n| `return_some` | `Some(val)` | return `Some(val)` enum of `Option`  |\n| `return_none` | `None`      | returs the `None` enum of `Option`   |\n| `return_ok`   | `Ok(val)`   | return `Ok(val)` enum of `Result`    |\n| `return_err`  | `Err(val)`   | return `Err(val)` enum of `Result`   |\n\n#### THEN: Asserting Code Under Test Used Mock in Expected Way\n\nAfter the test has run, we can verify the mock was called the right number of times and with the right arguments.\n\nThe table below lists the methods that can be used to verify the mock was invoked as expected.\n\n| Method                                                 | Returns       | What It Does |\n| ------------------------------------------------------ | ------------- | ------------ |\n| `calls()`                                              | `Vec\u003c(Args)\u003e` | return the arguments of each mock invocation, ordered by invocation time. |\n| `called()`                                             | `bool`        | return `true` if method was called at least once. |\n| `num_calls()`                                          | `usize`       | number of times method was called. |\n| `called_with((args))`                                  | `bool`        | return `true` if method was called at least once with given `args`. |\n| `has_calls(vec!((args), ...))`                         | `bool`        | return `true` if method was called at least once for each of the given `args` tuples. |\n| `has_calls_in_order(vec!((args), ...))`                | `bool`        | return `true` if method was called at least once for each of the given `args` collections, and called with arguments in the same order as specified in the input `vec`. |\n| `has_calls_exactly(vec!((args), ...))`                 | `bool`        | return `true` if method was called exactly once for each of the given `args` collections. |\n| `has_calls_exactly_in_order(vec!((args), ...))`        | `bool`        | return `true` if method was called exactly once for each of the given `args` collections, and called with arguments in the same order as specified in the input `vec`. |\n| `called_with_pattern(matcher_set)`                      | `bool`        | return `true` if method was called at least once with args that match the given matcher set. |\n| `has_patterns(vec!(matcher_set, ...))`                  | `bool`        | return `true` if all of the given matcher sets were matched at least once by the mock's calls. |\n| `has_patterns_in_order(vec!(matcher_set, ...))`         | `bool`        | return `true` if mock has calls that match all the specified matcher sets. The matcher sets must be matched in the order they are specified by the input `matcher_set` vector. |\n| `has_patterns_exactly(vec!(matcher_set, ...))`          | `bool`        | return `true` if all of the given matcher sets were matched at least once by the mock's calls. The number of calls equal the number of specified matcher sets. |\n| `has_patterns_exactly_in_order(vec!(matcher_set, ...))` | `bool`        | return `true` if mock has calls that match all the specified matcher sets. The matcher sets must be matched in the order they are specified by the input `matcher_set` vector. T the number of calls equal the number of specified matcher sets. |\n\nExample usage:\n\n```rust\nlet sheet = MockBalanceSheet::default();\n\n// invoke mock method\nsheet.profit(42, 10);\nsheet.profit(5, 0);\n\n// assert the invocation was recorded correctly\nassert!(sheet.profit.called());\nassert!(sheet.profit.called_with((42, 10)));\nassert!(sheet.profit.has_calls((42, 10)));\nassert!(sheet.profit.has_calls_in_order((42, 10), (5, 0)));\nassert!(sheet.profit.has_calls_exactly((5, 0), (42, 10)));\nassert!(sheet.profit.has_calls_exactly_in_order((42, 10), (5, 0)));\n```\n\n\u003e See section **Pattern Matching** for detail on how to use the pattern-based assertions.\n\n#### Reusing Mocks Across Multiple Tests\n\nInvoke `reset_calls()` to clear all recorded calls of a mock method.\n\nTo ensure individual tests are as isolated (thus, less likely to have bugs) as possible, it is recommended that different mock objects are constructed for different test cases.\n\nNevertheless, there might a some case where reusing the same mock and its return values results in easier to read and more maintainable test code. In those cases, `reset_calls()` can be used to clear calls from previous tests.\n\n### Pattern Matching\n\nWhen a mock function has been used in a test, we typically want to make assertions about what the mock has been called with. For example, suppose we're testing some logic that determines the next action of a robot. We might want to assert what this logic told the robot to do:\n\n```rust\nlet robot = MockRobot::default();\ndo_something_with_the_robot(\u0026robot);\nassert!(robot.move_forward.called_with(100);\n```\n\nThe above code checks that `do_something_with_the_robot()` should tell the robot to move 100 units forward. However, sometimes you might not want to be this specific. This can make tests being too rigid. Over specification leads to brittle tests and obscures the intent of tests. Therefore, it is encouraged to specify only what's necessary \u0026mdash; no more, no less.\n\nIf you care that `moved_forward()` will be called but aren't interested in its actual argument, you can simply assert on the call count:\n\n```rust\nassert!(robot.move_forward.called())\nassert!(robot.move_forward.num_calls() == 1u)\n```\n\nBut what if the behaviour we wanted to check is a little more nuanced? What if we wanted to check that the robot was moved forward at least 100 units, but it didn't matter if the robot moved even further than that? If this case, our assertion is more specific than \"was `move_forward()` called?\", but the constraint is not as tight as \"has to be moved _exactly_ 100 units\".\n\nIf we know the current implementation will move exactly 100 units, it is tempting to just use check for exact equality. However, as mentioned, this makes the tests very brittle. If the implementation is technically free to change, and start moving more than 100 units, then this test breaks. The developer has to go to the tests and fix the broken test. That change would not be required if the test wasn't overly restrictive. This may sound minor. However, code bases grow. This means the number of tests also grows. If all of the tests are brittle, then it becomes a huge burden to maintain/update these tests whenever production code changes.\n\n`double` allows developers to avoid this by using fuzzy assertions. One can perform looser assertions on mock argument values using **pattern matching**. In the robot example, we can assert that the robot move forward 100 _or more_ units with one line of code:\n\n```rust\nuse double::matcher::*;\n\nassert!(robot.move_forward.called_with_pattern(p!(ge, 100)));\n```\n\nLet's break this down. First, we changed `called_with` to `called_with_pattern`. Then, we pass in the matcher we want to use like so:\n\n```rust\np!(ge, 100)\n```\n\nThe `p!` macro generates a matcher function that the mock object accepts. `ge` is the name of the matcher (ge = greater than or equal to). `100` is a *matcher argument* that configures the matcher to match on the right values. Passing `100`  to `ge` means \"construct a matching function that matches values that are \u003e= `100`\".\n\nPattern matching is also possible with functions that take multiple arguments. We simply wrap individual argument matchers using the `matcher!` macro:\n\n```rust\nassert!(robot.move.called_with_pattern(\n    matcher!( p!(ge, 100), p!(eq, Direction::Left) )\n));\n```\n\nThe code above is asserting that the robot's `move()` method was called, and that the robot moved at least 100 units in the `Left` direction.\n\nThere are other check functions like `called_with_pattern()` that make use of pattern matchers. See section **THEN: Asserting Code Under Test Used Mock in Expected Way** for a list of these functions.\n\n#### Formal Definition\n\nFormally, a pattern matcher is defined a function that receives a single argument's value. It performs some check on the value, returning `true` if the argument's value \"matches\" the desired pattern and `false` otherwise. For example, the `any` matcher (which makes any value) is defined as:\n\n```rust\npub fn any\u003cT\u003e(_: \u0026T) -\u003e bool {\n    true\n}\n```\n\nwhere `_` is the argument value. Most matchers are _parametrised_. For example, the `eq` matcher takes one parameter -- the expected value. Likewise, the matcher `ge` takes one parameter, which specifies the number that the expected value should be greater than or equal to.\n\nMatchers can take any number of parameters. Additional parameters are specified as additional arguments to the matcher function. For example, here's the definition of the `eq` matcher function.\n\n```rust\npub fn eq\u003cT: PartialEq\u003e(arg: \u0026T, target_val: T) -\u003e bool {\n    *arg == target_val\n}\n```\n\nFor a parametrised matcher to be used, it must be bound to a parameter set. The `p!` macro does this. It takes a matcher function and a set of parameters to bind to it, then returns a new closure function which is bound to those parameters. For example:\n\n```rust\nlet bound_matcher = p!(eq, 42);\nassert!(bound_matcher(42) == true);\nassert!(bound_matcher(10) == false);\n```\n\nNotice how the bound matcher takes a single argument \u0026mdash; the argument value being matched. The matcher function's other arguments are bound within the returned closure.\n\nWhen passing matchers to a `Mock`'s assertion calls (e.g. `called_with_pattern` and `has_patterns`), they need to be passed as a _matcher set_. `Mock`'s assertion checks operation on the full set of arguments the mocked function has, not just individual arguments. For example, if a mocked function takes three arguments, then `called_with_pattern` expects a matcher set of size 3. The set contains one matcher for each of the mock's arguments.\n\nMatcher sets are constructed using the `matcher!` macro. This macro takes a bound matcher function for each argument in the mocked function. The order of the matcher functions corresponds to the order of the arguments in the mocked function.\n\nHere's an example of `matcher!` in use:\n\n```rust\nlet arg1_matcher = p!(eq, 42);\nlet arg2_matcher = p!(lt, 10);\nlet arg_set_matcher = matcher!(arg1_matcher, arg2_matcher);\n\nassert!(matcher((42, 5)) == true);\nassert!(matcher((42, -5)) == true);\nassert!(matcher((42, 10)) == false);\nassert!(matcher((100, 5)) == false);\n```\n\nIn reality, most `matcher!` and `p!` invocations will be made within the assertion call. Combining `matcher!` and `p!` invocations inline allows developers to write concise and expressive assertions like this:\n\n```rust\n// This reads:\n//     * first arg should be \u003e= 100\n//     * second arg should be `Direction::Left`\n\nassert!(robot.move.called_with_pattern(\n    matcher!( p!(ge, 100), p!(eq, Direction::Left) )\n));\n```\n\n#### Nesting Matchers\n\nIt is possible to nest matchers. For example, you might want to assert that an argument matches _multiple_ patterns.\n\nGoing back to the robot example, perhaps we don't care about the _exact_ amount the robot moved forward, but we care it's between some range. Let's say want to assert that the moved between the 100-200 unit range. No less, no more.\n\nWe use use two matchers, `ge` and `le`, for the one argument. We wrap them in the composite matcher `all_of`, like so:\n\n```rust\nassert!(robot.move_forward.called_with_pattern(\n    matcher!(\n        p!(all_of, vec!(\n            p!(ge, 100),\n            p!(le, 200))))\n));\n```\n\nUsers can nested matchers using the `p!` macro an arbitrary number of times. Try not to go too far with this feature though, as it can lead to tests that are difficult to read.\n\n\u003e NOTE: The above was for illustration. The simpler way to perform a value range check is using the non-composite `between_exc` and `between_inc` macros.\n\n#### Built-in Matchers\n\nThis section lists all the standard matchers built-in into the library. See the **Defining your Own Matchers** section if none of these fit your use case.\n\n##### Wildcards\n\n|         |                                               |\n| ------- | --------------------------------------------- |\n| `any()` | argument can be any value of the correct type |\n\n##### Comparison Matchers\n\n|                    |                                                                 |\n| ------------------ | --------------------------------------------------------------- |\n| `eq(value)`        | `argument == value`                                             |\n| `ne(value)`        | `argument != value`                                             |\n| `lt(value)`        | `argument \u003c value`                                              |\n| `le(value)`        | `argument \u003c= value`                                             |\n| `gt(value)`        | `argument \u003e value`                                              |\n| `ge(value)`        | `argument \u003e= value`                                             |\n| `is_some(matcher)` | argument is an `Option::Some`, whose contents matches `matcher` |\n| `is_ok(matcher)`   | argument is an `Result::Ok`, whose contents matches `matcher`   |\n| `is_err(matcher)`  | argument is an `Result::er`, whose contents matches `matcher`   |\n\n##### Floating-Point Matchers\n\n|                               |                                                                                             |\n| ----------------------------- | ------------------------------------------------------------------------------------------- |\n| `f32_eq(value)`               | argument is a value approximately equal to the `f32` `value`, treating two NaNs as unequal. |\n| `f64_eq(value)`               | argument is a value approximately equal to the `f64` `value`, treating two NaNs as unequal. |\n| `nan_sensitive_f32_eq(value)` | argument is a value approximately equal to the `f32` `value`, treating two NaNs as equal.   |\n| `nan_sensitive_f64_eq(value)` | argument is a value approximately equal to the `f64` `value`, treating two NaNs as equal.   |\n\n##### String Matchers\n\n|                       |                                                   |\n| --------------------- | ------------------------------------------------- |\n| `contains(string)`    | argument contains `string` as a sub-string.       |\n| `starts_with(prefix)` | argument starts with string `prefix`.             |\n| `starts_with(suffix)` | argument ends with string `suffix`.               |\n| `eq_nocase(string)`   | argument is equal to `string`, ignoring case.     |\n| `ne_nocase(value)`    | argument is not equal to `string`, ignoring case. |\n\n##### Container Matchers\n\nThere are currently no matchers to inspect the contents of containers. These will be added in future version of `double`. There is a [GitHub issue](https://github.com/DonaldWhyte/double/issues/12) to track this work.\n\n##### Composite Matchers\n\n|                                |                                                    |\n| ------------------------------ | -------------------------------------------------- |\n| `all_of(vec!(m1, m2, ... mn))` | argument matches all of the matchers `m1` to `mn`. |\n| `any_of(vec!(m1, m2, ... mn))` | matches at least one of the matchers `m1` to `mn`. |\n| `not(m)`                       | argument doesn't match matcher `m`.                |\n\n#### Defining your Own Matchers\n\nIf none of the built-in matchers fit your use case, you can define your own.\n\nSuppose we were testing a restful service. We have some request handling logic. We want to test the handling logic responded to the request correctly. In this context, \"correctly\" means it responded with a JSON object that contains the \"time\" key.\n\nHere's the production code to test:\n\n```rust\ntrait ResponseSender {\n    fn send_response(\u0026mut self, response: \u0026str);\n}\n\nfn request_handler(response_sender: \u0026mut ResponseSender) {\n    // business logic here\n    response_sender.send_response(\n        \"{ \\\"current_time\\\": \\\"2017-06-10 20:30:00\\\" }\");\n}\n```\n\nLet's mock response sender and assert on the contents of the JSON response:\n\n```rust\nmock_trait!(\n    MockResponseSender,\n    send_response(\u0026str) -\u003e ());\nimpl ResponseSender for MockResponseSender {\n    mock_method!(send_response(\u0026mut self, response: \u0026str));\n}\n\n#[test]\nfn ensure_current_time_field_is_returned() {\n    // GIVEN:\n    let mut mock_sender = MockResponseSender::default();\n\n    // WHEN:\n    request_handler(\u0026mock_sender);\n\n    // THEN:\n    // check the sender received a response that contains a current_time field\n}\n```\n\nThis check is cumbersome. One has to manually extract the text string passed to the mock, parse it as JSON (handling invalid JSON) and manually checking\n\nFor one test, perhaps this is not an issue. However, imagine we had multiple test cases for dozens of API endpoints. Duplicating the same JSON assertion logic across all the tests leads to code repetition. This repetition buries the intentions of the tests and makes them harder to change.\n\nCustom matchers to the rescue! We can use matchers to check if the response text string is a valid JSON object that has a certain key/field.\n\nA matcher is defined as a function that takes at least one argument (the `arg` being matched) and zero or more parameters. It returns a `bool` that indicates if `arg` is a match. We have one parameter in this case \u0026mdash; the `key` we're asserting exists in the response.\n\n```rust\nextern crate json;\nuse self::json;\n\nfn is_json_object_with_key(arg: \u0026str, key: \u0026str) -\u003e bool {\n    match json::parse(str) {\n        Ok(json_value) =\u003e match json_value {\n            Object(object) =\u003e match object.get(key) {\n                Some(_) =\u003e true  // JSON object that contains key\n                None =\u003e false    // JSON object that does contain key\n            },\n            _ =\u003e false  // not a object (must be another JSON type)\n        },\n        Err(_) =\u003e false  // not valid JSON\n    }\n}\n```\n\nUsing the matcher then requires binding it to a parameter (using `p!`) and passing it to a mock assertion method, like so:\n\n```rust\nfn ensure_current_time_field_is_returned() {\n    // GIVEN:\n    let mut mock_sender = MockResponseSender::default();\n\n    // WHEN:\n    request_handler(\u0026mock_sender);\n\n    // THEN:\n    // we expect a \"time\" field to be in the response JSON\n    assert(response_sender.send_response.called_with_pattern(\n        p!(is_json_object_with_key, \"time\")\n    ));\n    // we DO NOT expect a \"time\" field to be in the response JSON\n    assert(!response_sender.send_response.called_with_pattern(\n        p!(is_json_object_with_key, \"records\")\n    ));\n}\n```\n\n### Other Use Cases\n\n#### Mocking Methods without a Return Value\n\nIf a method does not return anything, the return value can be omitted when generating the method using double's macros:\n\n```rust\ntrait Queue {\n    fn enqueue(\u0026mut self, value: i32);\n    fn dequeue(\u0026mut self) -\u003e i32;\n}\n\nmock_trait!(\n    MockQueue,\n    enqueue(i32) -\u003e (), // still have to specify return value here...\n    dequeue() -\u003e i32);\nimpl Queue for MockQueue {\n    mock_method!(enqueue(\u0026mut self, value: i32));  // ...but not here!\n    mock_method!(dequeue(\u0026mut self) -\u003e i32);\n}\n```\n\n#### Mocking Methods That Return Types Which Do Not Implement `Default`\n\nThe `mock_trait!` macro assumes the return types of all the methods in the mocked `trait` implement `Default`. This makes it convenient to construct the mock object. One can invoke `MockTrait::default()` to construct the mock object and auto-configure it return default values for all methods in one go.\n\nIf a `trait` provides a method that returns a type that _doesn't_ implement `Default`, then one must generate the mock using `mock_trait_no_default!`. This macro generates a mock that doesn't implement `Default`. Clients must construct instances of the generated mock using `MockTrait::new()`, manually specifying the default return values for each method.\n\nFor example:\n\n```rust\n// `Result` does not implement the `Default` trait. Trying to mock `UserStore`\n// using the `mock_trait!` macro will fail. We use `mock_trait_no_default!`\n// instead.\npub trait UserStore {\n    fn get_username(\u0026self, id: i32) -\u003e Result\u003cString, String\u003e;\n}\n\nmock_trait_no_default!(\n    MockUserStore,\n    get_username(i32) -\u003e Result\u003cString, String\u003e);\n\nimpl UserStore for MockUserStore {\n    mock_method!(get_username(\u0026self, id: i32) -\u003e Result\u003cString, String\u003e);\n}\n\nfn test_manually_setting_default_retval() {\n    // GIVEN:\n    // Construct instance of the mock, manually specifying the default\n    // return value for `get_username()`.\n    let mock = MockUserStore::new(\n        Ok(\"default_user_name\".to_owned()));  // get_username() default retval\n    // WHEN:\n    let result = mock.get_username(10001);\n    // THEN:\n    assert_eq!(Ok(\"default_username\".to_owned()), result);\n}\n```\n\n#### Mocking Methods That Take `\u0026str` References\n\n`\u0026str` is a common argument type. However, double does not support mocking methods with `\u0026str` arguments with additional boilerplate.\n\nThis is because a mock cannot _store_ received `\u0026str` arguments. The mock needs to the _own_ the given arguments and `\u0026str` is a non-owning reference. Therefore, the mock trait has to be specified like so:\n\n```rust\ntrait TextStreamWriter {\n    fn write(\u0026mut self, text: \u0026str);\n}\n\nmock_trait!(\n    MockTextStreamWriter,\n    // have to use `String`, not `\u0026str` here, since `\u0026str` is a reference\n    write(String) -\u003e ()\n);\n\nimpl TextStreamWriter for MockTextStreamWriter {\n    mock_method!(write(\u0026mut self, text: \u0026str), self, {\n        // manually convert the reference to an owned `String` before passing\n        // it to the underlying mock object\n        self.write.call(text.to_owned())\n    });\n}\n```\n\nThe `mock_method` variant used above allows you to specify  the body of the generated function manually. The custom body simply converts the `\u0026str` argument to an owned string and passes it into the underlying `write` `Mock` object manually. (normally auto-generated bodies do this for you).\n\n\u003e NOTE: The name of the underlying mock object is always the same as the mocked\nmethod's name. So in the custom `write` body, you should pass the arguments down to `self.write`.\n\n`\u0026str` parameters are common. We understand that it is inconvenient to manually specify the body each time they appear. There are plans to add a macro to generate a body that calls `to_owned()` automatically. This section will be updated when that has been released.\n\n#### Mocking Methods with Generic Type Parameter\n\nMocking methods with generic type parameters require extra effort. For example, suppose one had a `Comparator` trait that was responsible for comparing any two values in the program. It might look something like this:\n\n```rust\ntrait Comparator {\n   fn is_equal\u003cT: Eq\u003e(\u0026self, a: \u0026T, b: \u0026T) -\u003e bool;\n}\n```\n\n`T` can be multiple types. Currently, we cannot store call arguments that\nhave generic types in the underlying `Mock` objects. Therefore, one has to\nconvert the generic types to a different, common representation. One way\nto get around this limitation is converting each generic type to a `String`.\ne.g. for the `Comparator` trait:\n\n```rust\n# #[macro_use] extern crate double;\n\nuse std::string::ToString;\n\ntrait Comparator {\n   fn is_equal\u003cT: Eq + ToString\u003e(\u0026self, a: \u0026T, b: \u0026T) -\u003e bool;\n}\n\nmock_trait!(\n    MockComparator,\n    // store all passed in call args as strings\n    is_equal((String, String)) -\u003e bool\n);\n\nimpl Comparator for MockComparator {\n    mock_method!(is_equal\u003c(T: Eq + ToString)\u003e(\u0026self, a: \u0026T, b: \u0026T) -\u003e bool, self, {\n        // Convert both arguments to strings and manually pass to underlying\n        // mock object.\n        // Notice how the both arguments as passed as a single tuple. The\n        // underlying mock object always expects a single tuple.\n        self.is_equal.call((a.to_string(), b.to_string()))\n    });\n}\n```\n\nIf the `to_string` conversions for all `T` are not lossy, then our mock expectations can be exact. If the `to_string` conversions _are_ lossy, then this mechanism can still be used, providing all the properties of the passed in objects are captured in the resultant `String`s.\n\nThis approach requires the writer to ensure the code under test adds the `ToString` trait to the `trait`'s type argument constraints. This limitation forces test writers to modify production code to use `double` for mocking.\n\nDespite this, there is still value in using `double` for mocking generic methods with type arguments. Despite adding boilerplate to production code and manually implementing mock method bodies being cumbersome, the value add is that all argument matching, expectations, calling test functions, etc. are all still handled by `double`.\n\nThe authors of double argue that reimplenting the aforementined features is more cumbersome than the small amount of boilerplate required to mock methods with type arguments.\n\n#### Using double Mocks for Free Functions\n\n`double::Mock` objects can also be used for free functions. Consider the following function:\n\n```rust\nfn generate_sequence(func: \u0026dyn Fn(i32) -\u003e i32, min: i32, max: i32) -\u003e Vec\u003ci32\u003e {\n    // exclusive range\n    (min..max).map(func).collect()\n}\n```\n\nThis iterates through a range of integers, mapping each integer to another integer using the supplied transformation function, `func`.\n\nRather than generate your own mock transformation function boilerplate when testing `generate_sequence`, one can use the macro `mock_func!`. This macro generates a `double::Mock` object and a closure that wraps it for you. For example:\n\n```rust\n#[macro_use]\nextern crate double;\n\nfn generate_sequence(func: \u0026dyn Fn(i32) -\u003e i32, min: i32, max: i32) -\u003e Vec\u003ci32\u003e {\n    // exclusive range\n    (min..max).map(func).collect()\n}\n\nfn test_function_used_correctly() {\n    // GIVEN:\n    mock_func!(\n        mock,     // name of variable that stores mock object\n        mock_fn,  // name of variable that stores closure wrapper\n        i32,      // return value type\n        i32);     // argument1 type\n    mock.use_closure(Box::new(|x| x * 2));\n\n    // WHEN:\n    let sequence = generate_sequence(\u0026mock_fn, 1, 5);\n\n    // THEN:\n    assert_eq!(vec!(2, 4, 6, 8), sequence);\n    assert!(mock.has_calls_exactly(vec!(\n      1, 2, 3, 4\n    )));\n}\n\nfn main() {\n    test_function_used_correctly();\n}\n```\n\nYou specify the variable names that should store the generated mock object and closure in the first two arguments of the `mock_func!` macro.\n\nIf the function's return type does not implement `Default`, then one must use the `mock_func_no_default!` macro, like so:\n\n```rust\n// ...\n\nfn test_function_with_custom_defaults() {\n    // GIVEN:\n    mock_func_no_default!(\n        mock,\n        mock_fn,\n        i32,   // return value type\n        42,    // default return value\n        i32);  // argument1 type\n    mock.use_closure_for((3), Box::new(|x| x * 2));\n\n    // WHEN:\n    let sequence = generate_sequence(\u0026mock_fn, 1, 5);\n\n    // THEN:\n    assert_eq!(vec!(42, 42, 6, 42), sequence);\n    assert!(mock.has_calls_exactly(vec!(\n      1, 2, 3, 4\n    )));\n}\n\nfn main() {\n    test_function_with_custom_defaults();\n}\n```\n","funding_links":[],"categories":["Rust"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDonaldWhyte%2Fdouble","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDonaldWhyte%2Fdouble","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDonaldWhyte%2Fdouble/lists"}