I know there’s mockall which seems to be geared towards implementation methods and traits, but I’m wondering about just structs with non-function properties.

In my tests, I want to define initialized structs with values. It works fine to just do it like I normally would in code, but I’m wondering if there’s more to it than that. Like if I have a cat struct:

struct Cat { name : String } `

#[cfg(test)] pub mod test { use super::Cat; fn test_create_cat() -> Cat { Cat { name. : String::from("Fred") }; }

That’s fine, but should I be doing it differently? What about mockall, is it not meant for structs with properties?

  • hallettj@beehaw.org
    link
    fedilink
    arrow-up
    2
    ·
    1 year ago

    I don’t think you can mock a struct. I’m guessing a mock would be a different struct type with the same fields, or a subset of fields. But since Rust types are nominative, not structural, you can’t substitute one struct type with another even if the data is identical.

    When you need a struct for a test you create a value of the original type. Some problems that might come up are,

    • you need to call a constructor to create a struct, and it has side-effects that you don’t want to run in the test
    • you need to call a constructor that is not visible to the test
    • the struct requires data that is difficult or cumbersome to produce in the test

    For the first two problems one option is to use conditional compilation to create test-only constructors that bypass code you don’t want to run, or that have broader visibility.

    For the last problem you might want to refactor the code to use traits which you can create mock/test implementations for. Or you might change the struct to make the problematic data optional, or lazily initialized.

    • nerdbloodOP
      link
      fedilink
      arrow-up
      2
      ·
      1 year ago

      This is a great answer, thanks. I’ll have to look more into conditional compilation. That’s new to me.

      • TehPers@beehaw.org
        link
        fedilink
        arrow-up
        1
        ·
        1 year ago

        A few days later, but keep in mind that if you write your tests in the module you declare your structs, you’ll have access to its “private” (non-pub) members since those are technically module scoped (default scope is pub(self)).

        pub struct Cat {
            name: String,
        }
        
        #[cfg(test)]
        mod tests {
            use super::*;
        
            #[test]
            fn create_cat() {
                let cat = Cat {
                    name: "fluffy".into(),
                };
            }
        }
        

        Playground