Проблема тестирования Trait-ов


#1

Имею следующую структуру repl(отразил примерно то что есть у меня).

Задача в том что я должен мокать структуры А и B. И вот тут я уже по всякому пробовал. Конечно хочется сделать что то типа:

struct C{
     general: General,    
}

И допустим сделать методы get_a, get_b которые бы возвращали мутабильные ссылки на данные структуры. Но при тесте мне их нужно заменить. а следовательно Item должен выглядеть следующим образом:

    pub struct Item{
        a:  Box<Logic_A>,
        b:  Box<Logic_B>
    }

Но тогда у меня проблема что Item перестает быть сериализуеммым объектом. И тут либо свой серилизатор писать, либо использовать auto trait который пока не стабильный.

Вопрос можно ли как то по другому решить данную проблему? Для тестирования использую mock-it


#2

Можно сделать DI на основе обобщенных типов: https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=b6408c05c643f55af472abda43a61b0d

Или с помощью псевдонимов типов: https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=a67ccf25701822be73f3dde10d5f3e82


#3

Вообще, не так давно уже обсуждалась необходимость удобного статического DI. Сошлись на том, что нужно сделать процедурный макрос, который позволил бы структуре

#[gear]
struct Foo {
    #[inject = "foo_field"]
    field: DefaultType,
}

Задавать тип-заменитель в конфиге:

[foo_field]
path::to::MockType

Тогда для тестирования достаточно было бы иметь отдельный конфиг и во время компиляции для тестов производилась бы замена на одни типы, а при нормальной сборке на другие.


#4

Первый вариант то что нужно, просто не реальное человеческое спасибо :grinning: Уже 2 дня бьюсь. Что только не пробовал. Немного по другому записывал обобщение типов, из за чего думал, что дело гиблое. Но Ваш способ работает.

Второй способ не подходит по причине того, что не только модули разные, но еще и крейты.


#5

Где то натыкался на данное обсуждение. Но как мне довольно опасная вещь в большом проекте… Боюсь может привести к большой лапше, что потом с ума можно будет сойти разбирайся во всем этом деле при поиске очередной проблемы.


#6

Рад что помогло :slight_smile:
С обобщенными типами, правда, тут есть проблема, что их придется пробрасывать через все уровни вложенности объектов: от места непосредственного использования, до того уровня, где нужно будет подставлять моки.


#7

Это не такая большая проблема, как если бы пришлось пробрасывать времена жизни :wink:


#8

Как во Вьетнаме побывал… Что то я не учел того, что для всех имплементаций тоже необходимо реализовывать проброс. А так как я уже очень много кода и тестов написал. В итоге 2 с половиной часа вносил необходимые правки… Завтра буду думать как абстрагироваться еще больше от ООП. Вроде есть пара мыслей, нужно проверить на практике. А то практически везде прописывать + Default + serde::Serialize + serde::de::DeserializeOwned как то совсем не впечатляет, да и завтра добавлю новое поле и опять бегать по всем местам прописывая, совсем не хочется


#9

В итоге вернулся к старой идеи(не знаю почему от нее отказался…)Проще для каждого трейта (Logic_A, и Logic_B) создать метод seriliaze и собственно сделать свой серилизатор для Item. Конечно жалко что нельзя написать

pub struct Item {  
   a: Box<Logic_A + Serialize + Deserialize>
   b: Box<Logic_B + Serialize + Deserialize> 
}

Это бы намного упросило задачу. Если конечно auto trait попадет в релиз, то я так понимаю это станет возможно.