mockable
This crate provides usefull traits to make easier to mock your code using mockall
crate.
Getting Started
Add this to your Cargo.toml
:
[dependencies]
mockable = { version = "0.1.0", features = [ ... ] }
[dev-dependencies]
mockable = { version = "0.1.0", features = ["mock"] }
Documentation
Documentation
Clock
The Clock
trait provides a way to mock the current time.
Note: This trait is only available when the clock
feature is enabled.
use chrono::{DateTime, Duration, Utc};
use mockable::{Clock, DefaultClock, MockClock};
fn now(clock: &dyn Clock) -> DateTime<Utc> {
clock.utc()
}
let time = now(&DefaultClock);
let expected = Utc::now();
let mut clock = MockClock::new();
clock
.expect_utc()
.returning(move || expected);
let time = now(&clock);
assert_eq!(time, expected);
Command Runner
The CommandRunner
trait provides a way to mock the execution of commands.
Note: This trait is only available when the cmd
feature is enabled.
use std::io::Result;
use mockall::predicate::eq;
use mockable::{Command, CommandOutput, CommandRunner, DefaultCommandRunner, MockCommandRunner};
async fn run(cmd: Command, runner: &dyn CommandRunner) -> Result<CommandOutput> {
runner.run(cmd).await
}
tokio_test::block_on(async {
let cmd = Command {
args: vec!["-n".to_string(), "Hello world!".to_string()],
cwd: None,
env: None,
gid: None,
program: "echo".to_string(),
uid: None,
};
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());
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);
});
Env
The Env
trait provides a way to mock the environment variables.
use mockable::{DefaultEnv, Env, EnvParseResult, MockEnv};
fn get(env: &dyn Env) -> Option<EnvParseResult<u32>> {
env.u32("KEY")
}
std::env::set_var("KEY", "42");
let env = DefaultEnv::new();
let val = get(&env).unwrap().unwrap();
assert_eq!(val, 42);
let mut env = MockEnv::new();
env
.expect_u32()
.returning(|_| Some(Ok(24)));
let val = get(&env).unwrap().unwrap();
assert_eq!(val, 24);
File System
The FileSystem
trait provides a way to mock the file system operations.
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<Box<dyn Metadata>> {
fs.metadata(path)
}
let metadata = get_metadata(Path::new("/"), &DefaultFileSystem).unwrap();
assert!(metadata.is_dir());
let mut fs = MockFileSystem::new();
fs
.expect_metadata()
.with(eq(Path::new("/")))
.returning(|_| {
let mut metadata = MockMetadata::new();
metadata
.expect_is_dir()
.returning(|| true);
Ok(Box::new(metadata))
});
let metadata = get_metadata(Path::new("/"), &fs).unwrap();
assert!(metadata.is_dir());
HTTP Client
The HttpClient
trait provides a way to mock the HTTP client.
Note: This trait is only available when the http
feature is enabled.
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<Box<dyn HttpResponse>> {
client.send(req).await
}
tokio_test::block_on(async {
let req = HttpRequest {
headers: Default::default(),
method: Method::GET,
query: Default::default(),
url: "https://google.com".to_string(),
};
let client = DefaultHttpClient;
let resp = send(req.clone(), &client).await.unwrap();
assert!(resp.status().is_success());
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());
});
Mock
The Mock
trait provides a way to mock a function.
use mockable::Mock;
use mockall::automock;
let mock: Mock<()> = Mock::never();
let mock = Mock::once(|| 42);
assert_eq!(mock.call(), 42);
let mock = Mock::with(vec![
Box::new(|| 1),
Box::new(|| 2),
Box::new(|| 3)]
);
assert_eq!(mock.call(), 1);
assert_eq!(mock.call(), 2);
assert_eq!(mock.call(), 3);
let mock = Mock::always(|idx| idx);
assert_eq!(mock.call(), 0);
assert_eq!(mock.call(), 1);
assert_eq!(mock.call(), 2);
#[automock]
trait MyTrait {
fn foo(&self) -> &'static str;
}
let mock = Mock::once(move || "bar");
let mut mymock = MockMyTrait::new();
mymock
.expect_foo()
.returning({
let mock = mock.clone();
move || mock.call()
});
assert_eq!(mymock.foo(), "bar");
assert_eq!(mock.count(), 1);
System
The System
trait provides a way to mock the system.
UUID Generator
The UuidGenerator
trait provides a way to mock the UUID generator.
Note: This trait is only available when the uuid
feature is enabled.
use mockable::{DefaultUuidGenerator, MockUuidGenerator, UuidGenerator};
use uuid::Uuid;
fn generate(generator: &dyn UuidGenerator) -> Uuid {
generator.generate_v4()
}
let uuid = generate(&DefaultUuidGenerator);
let expected = Uuid::new_v4();
let mut generator = MockUuidGenerator::new();
generator
.expect_generate_v4()
.returning(move || expected);
let uuid = generate(&generator);
assert_eq!(uuid, expected);