admin管理员组文章数量:1127954
Say I have two traits
traits.rs
pub trait MessagingService {
fn new() -> Self
...
}
pub trait WebService {
fn create_client() -> Client;
fn send_request(req: RequestBuilder) -> Option<Value>;
}
and a module where I implement these two traits for discord
services/discord.rs
pub struct DiscordService {
client: Client,
}
impl WebService for DiscordService {
// a.k.a the transport layer
fn send_request(req: RequestBuilder) -> Option<Value> {
// contains all the code involved that involves actual HTTP requests
}
}
impl MessagingService for DiscordService {
// contains the code that
// i) build request
// ii) processing of the values returned by WebService::send_request
fn update_task_status(&self, task: &mut Task) {
let body = json!({"what":"ever"});
let _ = Self::send_request(
self.client
.post(format!(
"{BASE_URL}/channels/{}/messages",
CONF.discord_channel
))
.json(&body),
);
}
}
I want to mock the transport part, i.e. WebService
so I could test the functions of MessagingService
independently.
I read on several occasions that the proper rustacean way to proceed is to provide a mock for the trait instead of mocking the object. I understood this as providing a new implementation of this very trait in my tests.
services/discord.rs
...
#[cfg(test)]
mod tests {
use reqwest::blocking::{Client, RequestBuilder};
use serde_json::Value;
use super::DiscordService;
use crate::structs::WebService;
impl WebService for DiscordService {
fn send_request(req: RequestBuilder) -> Option<Value>{
// builds Values from sourced files instead
// of sending HTTP requests
}
}
#[test]
fn some_test() {
let notifier: DiscordService = DiscordService::new();
// test notifier.update_task_status
}
}
It looked nice on paper but I get a conflicting implementations of trait WebService for type discord::DiscordService
. I thought that mod tests {}
would work like an encapsulation, allowing me to provide an alternative implementation of WebService
but it does not seem to be the case. I do not want to create a new struct like DiscordServiceMock
, for it would make some parts of my code untestable.
Is there a way to provide such implementation that would no end up in conflict? Is there an other way?
Say I have two traits
traits.rs
pub trait MessagingService {
fn new() -> Self
...
}
pub trait WebService {
fn create_client() -> Client;
fn send_request(req: RequestBuilder) -> Option<Value>;
}
and a module where I implement these two traits for discord
services/discord.rs
pub struct DiscordService {
client: Client,
}
impl WebService for DiscordService {
// a.k.a the transport layer
fn send_request(req: RequestBuilder) -> Option<Value> {
// contains all the code involved that involves actual HTTP requests
}
}
impl MessagingService for DiscordService {
// contains the code that
// i) build request
// ii) processing of the values returned by WebService::send_request
fn update_task_status(&self, task: &mut Task) {
let body = json!({"what":"ever"});
let _ = Self::send_request(
self.client
.post(format!(
"{BASE_URL}/channels/{}/messages",
CONF.discord_channel
))
.json(&body),
);
}
}
I want to mock the transport part, i.e. WebService
so I could test the functions of MessagingService
independently.
I read on several occasions that the proper rustacean way to proceed is to provide a mock for the trait instead of mocking the object. I understood this as providing a new implementation of this very trait in my tests.
services/discord.rs
...
#[cfg(test)]
mod tests {
use reqwest::blocking::{Client, RequestBuilder};
use serde_json::Value;
use super::DiscordService;
use crate::structs::WebService;
impl WebService for DiscordService {
fn send_request(req: RequestBuilder) -> Option<Value>{
// builds Values from sourced files instead
// of sending HTTP requests
}
}
#[test]
fn some_test() {
let notifier: DiscordService = DiscordService::new();
// test notifier.update_task_status
}
}
It looked nice on paper but I get a conflicting implementations of trait WebService for type discord::DiscordService
. I thought that mod tests {}
would work like an encapsulation, allowing me to provide an alternative implementation of WebService
but it does not seem to be the case. I do not want to create a new struct like DiscordServiceMock
, for it would make some parts of my code untestable.
Is there a way to provide such implementation that would no end up in conflict? Is there an other way?
Share Improve this question asked Jan 8 at 17:18 zar3bskizar3bski 3,1517 gold badges30 silver badges68 bronze badges1 Answer
Reset to default 1No, this is not how Rust works or should be used. Multiple, overlapping implementations for a type are not allowed, as stated by the Rust Reference:
A trait implementation is considered incoherent if either the orphan rules check fails or there are overlapping implementation instances.
There are several solutions to this. None of them are Rust-specific.
If you stick to the idea of mocking a part of your code, you can treat a trait as an interface and use some kind of strategy pattern (which involves injecting different strategies). Then implement a test strategy (like DiscordServiceMock
) and use it in your tests.
Here is a small example of how to use WebService
as a strategy for DiscordService
:
use std::marker::PhantomData;
trait WebService {
fn send_request() -> i32;
}
trait MessagingService {
fn update_task_status(&self) -> i32;
}
#[derive(Default)]
struct DiscordService<T>(PhantomData<T>);
impl<T: WebService> MessagingService for DiscordService<T> {
fn update_task_status(&self) -> i32 {
T::send_request()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Default)]
struct WebServiceMock;
impl WebService for WebServiceMock {
fn send_request() -> i32 {
100
}
}
#[test]
fn test_name() {
let service = DiscordService::<WebServiceMock>::default();
assert_eq!(service.update_task_status(), 100);
}
}
本文标签: rustHow to provide a mock for a trait (no mockall magics)Stack Overflow
版权声明:本文标题:rust - How to provide a mock for a trait (no mockall magics) - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736699350a1948345.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论