This crate provides usefull traits to make easier to mock your code using mockall
crate.
Add this to your Cargo.toml
:
```toml [dependencies] mockable = { version = "0.1", features = [ ... ] }
[dev-dependencies] mockable = { version = "0.1", features = ["mock"] } ```
The Clock
trait provides a way to mock the current time.
Note: This trait is only available when the clock
feature is enabled.
```rust use chrono::{DateTime, Duration, Utc}; use mockable::{Clock, DefaultClock, MockClock};
fn now(clock: &dyn Clock) -> DateTime
// Default let time = now(&DefaultClock);
// Mock let expected = Utc::now(); let mut clock = MockClock::new(); clock .expectutc() .returning(move || expected); let time = now(&clock); asserteq!(time, expected); ```
The CommandRunner
trait provides a way to mock the execution of commands.
Note: This trait is only available when the cmd
feature is enabled.
```rust use std::io::Result;
use mockall::predicate::eq; use mockable::{Command, CommandOutput, CommandRunner, DefaultCommandRunner, MockCommandRunner};
async fn run(cmd: Command, runner: &dyn CommandRunner) -> Result
tokiotest::blockon(async { let cmd = Command { args: vec!["-n".tostring(), "Hello world!".tostring()], cwd: None, env: None, gid: None, program: "echo".to_string(), uid: None, };
// Default
let runner = DefaultCommandRunner;
let outputs = run(cmd.clone(), &runner).await.unwrap();
assert_eq!(outputs.code, Some(0));
assert_eq!(outputs.stdout, "Hello world!".as_bytes().to_vec());
// Mock
let expected = CommandOutput {
code: Some(0),
stderr: vec![],
stdout: "Hello world!".as_bytes().to_vec(),
};
let mut runner = MockCommandRunner::new();
runner
.expect_run()
.with(eq(cmd.clone()))
.returning({
let expected = expected.clone();
move |_| Ok(expected.clone())
});
let output = run(cmd, &runner).await.unwrap();
assert_eq!(output, expected);
}); ```
The Env
trait provides a way to mock the environment variables.
```rust use mockable::{DefaultEnv, Env, EnvParseResult, MockEnv};
fn get(env: &dyn Env) -> Option
std::env::set_var("KEY", "42");
// Default let env = DefaultEnv::new(); let val = get(&env).unwrap().unwrap(); assert_eq!(val, 42);
// Mock let mut env = MockEnv::new(); env .expectu32() .returning(|| Some(Ok(24))); let val = get(&env).unwrap().unwrap(); assert_eq!(val, 24); ```
The FileSystem
trait provides a way to mock the file system operations.
```rust use std::{io::Result, path::Path};
use mockall::predicate::eq; use mockable::{DefaultFileSystem, FileSystem, Metadata, MockFileSystem, MockMetadata};
fn get_metadata(path: &Path, fs: &dyn FileSystem) -> Result
// Default let metadata = getmetadata(Path::new("/"), &DefaultFileSystem).unwrap(); assert!(metadata.isdir());
// Mock let mut fs = MockFileSystem::new(); fs .expectmetadata() .with(eq(Path::new("/"))) .returning(|| { let mut metadata = MockMetadata::new(); metadata .expectisdir() .returning(|| true); Ok(Box::new(metadata)) }); let metadata = getmetadata(Path::new("/"), &fs).unwrap(); assert!(metadata.isdir()); ```
The HttpClient
trait provides a way to mock the HTTP client.
Note: This trait is only available when the http
feature is enabled.
```rust use mockall::predicate::eq; use mockable::{DefaultHttpClient, HttpClient, HttpRequest, HttpResponse, MockHttpClient, MockHttpResponse}; use reqwest::{Method, Result, StatusCode};
async fn send(req: HttpRequest, client: &dyn HttpClient) -> Result
tokiotest::blockon(async { let req = HttpRequest { headers: Default::default(), method: Method::GET, query: Default::default(), url: "https://google.com".to_string(), };
// Default
let client = DefaultHttpClient;
let resp = send(req.clone(), &client).await.unwrap();
assert!(resp.status().is_success());
// Mock
let mut client = MockHttpClient::new();
client
.expect_send()
.with(eq(req.clone()))
.returning(|_| {
let mut resp = MockHttpResponse::new();
resp
.expect_status()
.returning(|| StatusCode::OK);
Ok(Box::new(resp))
});
let resp = send(req, &client).await.unwrap();
assert!(resp.status().is_success());
}); ```
The Mock
trait provides a way to mock a function.
```rust use mockable::Mock; use mockall::automock;
// Never let mock: Mock<()> = Mock::never(); // fist call will panic
// Once let mock = Mock::once(|| 42); assert_eq!(mock.call(), 42);
// Several let mock = Mock::with(vec![ Box::new(|| 1), Box::new(|| 2), Box::new(|| 3)] ); asserteq!(mock.call(), 1); asserteq!(mock.call(), 2); assert_eq!(mock.call(), 3); // next call will panic
// Always let mock = Mock::always(|idx| idx); asserteq!(mock.call(), 0); asserteq!(mock.call(), 1); assert_eq!(mock.call(), 2); // next call will never panic
// with mockall
trait MyTrait { fn foo(&self) -> &'static str; }
let mock = Mock::once(move || "bar"); let mut mymock = MockMyTrait::new(); mymock .expectfoo() .returning({ let mock = mock.clone(); move || mock.call() }); asserteq!(mymock.foo(), "bar"); assert_eq!(mock.count(), 1); ```
The System
trait provides a way to mock the system.
The UuidGenerator
trait provides a way to mock the UUID generator.
Note: This trait is only available when the uuid
feature is enabled.
```rust use mockable::{DefaultUuidGenerator, MockUuidGenerator, UuidGenerator}; use uuid::Uuid;
fn generate(generator: &dyn UuidGenerator) -> Uuid { generator.generate_v4() }
// Default let uuid = generate(&DefaultUuidGenerator);
// Mock let expected = Uuid::newv4(); let mut generator = MockUuidGenerator::new(); generator .expectgeneratev4() .returning(move || expected); let uuid = generate(&generator); asserteq!(uuid, expected); ```