I'm trying to mock out the serial port object provided by serialport::new()
and having an awful time with it. The Rc<RefCell<dyn SomeTrait>>
pattern is one I've been using for a while now for any singleton dependencies - like a serial port - and I've grown accustomed to it. It works and it lets me use mockall
to write highly focused unit tests where all dependencies are mocked out.
But, today I ran into serialport::new()
, which returns a Box<dyn SerialPort>
, and I can't figure out a way to convert that into Rc<RefCell<dyn SerialPort>>
. So I wrapped the whole box and ended up with Rc<RefCell<Box<dyn SerialPort>>>
. But now I can't get my unit test to compile without defining the mock object in TestContext
as another Rc<RefCell<Box<dyn SerialPort>>>
- which would prevent me from being able to actually invoke special mock-related methods, like .expect_send()
.
I could use some help here. What follows is my use-case recreated in the simplest form possible as well as the compiler error that I get when building the test.
use std::cell::RefCell;
use std::rc::Rc;
/// This is not my code. This is a simplified version of the serialport crate.
mod serialport {
pub trait SerialPort {
fn send(&self);
}
pub struct SomeConcreteSerialPort {}
impl SerialPort for SomeConcreteSerialPort {
fn send(&self) {}
}
pub fn new() -> Box<dyn SerialPort> {
Box::new(SomeConcreteSerialPort {})
}
}
struct MyStruct {
port: Rc<RefCell<Box<dyn serialport::SerialPort>>>,
}
impl MyStruct {
fn new() -> Self {
Self {
port: Rc::new(RefCell::new(serialport::new())),
}
}
fn do_send(&self) {
self.port.borrow().send();
}
}
fn main() {
let my_struct = MyStruct::new();
my_struct.do_send();
println!("The foo is done!");
}
#[cfg(test)]
mod test {
use mockall::mock;
use std::cell::RefCell;
use std::rc::Rc;
use crate::serialport;
use crate::MyStruct;
mock! {
SerialPort {}
impl serialport::SerialPort for SerialPort {
fn send(&self);
}
}
struct TestContext {
mock_port: Rc<RefCell<Box<MockSerialPort>>>,
testable: MyStruct,
}
impl TestContext {
fn new() -> Self {
let mock_port = Rc::new(RefCell::new(Box::new(MockSerialPort::new())));
Self {
mock_port: Rc::clone(&mock_port),
testable: MyStruct { port: mock_port },
}
}
}
#[test]
fn test_happy_path() {
let context = TestContext::new();
context.mock_port.borrow().expect_send().once();
context.testable.do_send();
}
}
error[E0308]: mismatched types
--> src/main.rs:71:44
|
71 | testable: MyStruct { port: mock_port },
| ^^^^^^^^^ expected trait object `dyn SerialPort`, found struct `MockSerialPort`
|
= note: expected struct `Rc<RefCell<Box<(dyn SerialPort + 'static)>>>`
found struct `Rc<RefCell<Box<MockSerialPort>>>`
I've also published the code to a GitHub repo if you'd like to reproduce it locally: https://github.com/DavidZemon/MockingProblems/