Страницы

Поиск по вопросам

среда, 17 апреля 2019 г.

Мокинг статических методов. Когда это нужно делать?

Ранее как-то особо не обращал внимание на статические методы и сейчас когда вижу, что для некоторых (иногда даже кажется что для большинства) мне не нужно задавать/хранить состояние, то решил сделать их статическими. Но становиться вопрос, что тогда на счет мокинга этих методов в классах, где они являются зависимостями, ведь в интерфейсе нельзя объявить статический метод и соответственно другую реализацию подсунуть становить затруднительно стандартными средствами.
Например, есть код
public class Hasher { public static void RenameByHash(string filePath) { //... } }
public class MyService { public void ProccessPres(string presentationPath) { //...
Hasher.RenameByHash(presentationPath);
//... } }
Я ведь теперь при тестировании метода ProccessPres() не могу замокать Hasher.RenameByHash(). И прихожу к тому, что приходиться вообще отказаться от статических методов, т.к. не могу изолировать их поведение и тестировать только свой сервис.
Мокинг приватных методов. Быть может мне необходимо понять какие-то концептуальные вещи касаемо статических методов. К примеру, я как-то пытался делать юнит-тест для приватного метода, пока один чувак на форуме внятно не дал понять, что это неправильно с концептуальной точки зрения и нужно тестировать только публичные метода класса, которые являются API этих классов, тем с помощью которого к нему стучится внешний мир. Да, это тоже не догма, наверное, и для приватных методов иногда нужны юнит-тесты, но я этого избегаю.
Фреймворки для мокинга статических методов. Также есть статья в которой говорится, что для мокинга статических методов можно использовать более навороченные мокинг-фреймворки типа Typemock, JustMock, Moles. Но насколько нужно это мне, как я писал выше, было бы ясно если я убедился бы в том, что правильно понимаю теоретическую основу статических методов касательно их имитации. Быть может мокать поведение статические методов в большинстве случаев и вовсе не нужно как не нужно мокать приватные методы.


Ответ

Имхо, статические методы должны
1) Делать очень маленькую специфичную работу 2) Не должны напрямую относиться к бизнес логике 3) Как следствие 1) и 2) - их не надо мокать 4) Их можно тестировать отдельно
По большому счету они очень похожи на расширяющие методы.
Например,
string.IsNullOrEmpty(...) DateTime.ConvertToLocalTIme(...)
Но если у вас есть какой то бизнес класс, который не обладает состоянием (например, какой то сервис отсылки сообщений или калькулятор стоимости товара или что то подобное), то есть смысл делать его синглтоном, то есть иметь только 1 экземпляр такого сервиса на всё приложение/домен/подсистему и тд.
Я предпочитаю делать синглтоны за счет инверсии зависимости. То есть класс сам не знает, что он синглтон - это указывается в корне композиции при регистрации типов в IoC контейнере или вручную. Таким образом, классы без состояния можно тестировать как любые другие классы.
UPD
На самом деле, если заходить дальше, то ваши подсистемы по идее должны взаимодействовать посредством контрактов (это такая общая рекомендация), что обычно означает интерфейсов. Грубо говоря, при использовании IoC весь необходимый для класса воспомогательный бизнес функционал должен быть инжектирован через конструтор, свойство или каким либо другоим методом (я почти всегда выбираю конструктор). То есть используюя какой то бизнес функционал, ваш класс должен вызвать метод какого либо инстанса (ведь вы не можете инжектировать статический класс/метод). Таким образом мы приходим к тому, что вызов статического метода стороннего класса идет в разрез с принципом инверсии зависимости.
С другой стороны, преобразование обычного класса (или его пубичного метода) в статический увеличивает связность классов, так как интерфейсы не содерджат статических методов, а значит вызвать статический класс/метод вы можете только явно. То есть делая класс или его публичный метод статическим, вы уменьшаете гибкость системы вцелом. Потому есть смысл это делать только в тех случаях, когда у таких классов/методов в приципе возможна только одна имплементация, которая не содержит состояния и которую вы точно не захотите менять никогда (string.IsNullOrEmpty четко дает понять, что происходит внутри метода и другой реализации этого метода быть не может) - то есть в таких случаях вы жертвуете гибкостью там, где она в принципе не нужна. Во всех остальных случаях я стараюсь избегать модификатора static вообще.
Что же касается внутренней реализации класса - приватных статических членов, это уже другой разговор, тут уже можно делать статическим все, что удобно, так как это внутренняя кухня класса (особенно если от этого класса запрещено наследование).

Комментариев нет:

Отправить комментарий