Store data in testobject

Hello,

I am currently writing a test in which a trait has a function to a non mutable reference to it self. Because of this non mutability, I cannot store the data from the write function of the manager in the TestBlockdevice. One possiblity to make the self of the write function in the Blockdevice trait mutable, but I have to do this only for the test, otherwise non mutable is fine.
Is there another way?

trait Blockdevice {
    fn write(&self, data: &[&str]);
}

struct DummyBlockdevice {

}

impl Blockdevice for DummyBlockdevice {
    fn write(&self, data: &[&str]) {
        // Write data out, no mutable of self required
    }
}

struct Manager<D> {
    d: D
}

impl<D: Blockdevice> Manager<D> {
    fn new(d: D) -> Self {
        Self {
            d
        }
    }

    fn write(&self, string: &[&str]) {
        self.d.write(string);
    }
    
    fn block_device(&self) -> &D {
        &self.d
    }
}

pub fn main() {

}

#[cfg(test)]
mod tests {

    use super::*;

    struct TestBlockdevice {

    }

    impl Blockdevice for TestBlockdevice {
        fn write(&self, data: &[&str]) {
            // store data so it can be retrived afterwards
            // One possibility: &self -> &mut self but only required for the test
        }
    }

    impl TestBlockdevice {
        fn get_data(&self) {
            // Get back stored string to check if everything was correct
        }
    }


    #[test]
    fn write_test() {
        let m = Manager::new(TestBlockdevice {});

        m.write(&["Test"]);
        
        let bd = m.block_device();
        let data = bd.get_data();
        // check that data is correct
    
    }
    
}

It sounds like you want Interior Mutibility. In your case I would recommend RefCell::borrow_mut, which has a small runtime overhead, but that should be perfectly acceptable in a test suite.

1 Like

One useful kind of interior mutability for test types is a channel; e.g. TestBlockdevice would hold a std::sync::mpsc::Sender<Vec<String>> or something like that. The test would keep the receiving side of the channel, and check it at the end.

The advantage of this over using only a RefCell, Mutex, etc. is that you don't need the Manager::block_device() function to to be examine the state — the TestBlockdevice could be completely hidden or already dropped, and it doesn't matter, because the test has its own access to the output.

Or if a channel sender, being write-only, doesn't fit the use-case, then you can use a &RefCell<Something> or Rc<RefCell<Something>> to share the state between the two. The important idea is giving the test shared access to the state, not relying on being able to extract the test object from the thing being tested.

1 Like

Thank you @binarycat and @kpreid for the hints. Interior Mutibility worked fine, but I will check out also the Sender option to get an idea how it works!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.