Сделать recording enhancement который будет записывать факт применения enhancement внутри себя. Вне зависимости от того, сколькими и какими вызовами это было сделано.
Для оставшихся мы используем тот же симулятор, только создаем его уже в залогиненом виде
ИЛИ используем другой ограниченный интерфейс со своим симулятором
Затем, чтобы протестировать вот эти требования:
•SUT должен логиниться с credentials, полученными из конфигурации
•на каждый успешный логин должен быть логаут
•если логин неуспешен, отправка (и вообще никакие действия с нотификатором) не осуществляется
Для тех, кто зависит от логина реализация нужна, значит мы ее делаем.
Для тех кто не зависит от логина, предоставляем factory method InMemorySender.CreateLoggedInSender() и не тащим подробности логина и логаута в их тесты.
Если нам нужен очень редко Login и Logout надо просто сделать интерфейс, который не содержит их и в большинстве случаев используется именно он. Возможно даже стоит выделить LoggedInSender чтобы система типов контролировала, что либо классу передали залогиненый сендер, либо он сам залогинен.
Ок можете истолковывать мое "не проверять вызовы" как "проверять вызовы, но только так, чтобы все корректные с точки зрения абстракции вызовы считались тестом корректными. Или в максимальной степени соответствовать этому условию"
Симулятор — часть тестов. Поэтому тесты изменятся.
Ок, не придется менять каждый конкретный тест, придется менять только общую для всех часть в симуляторе
Чтобы снизить стоимость реализации надо применять hexagonal architecture и required interface на границах. То есть если у вас есть файловая система, а вам надо использовать только одну папку с файлами, и от нее интересно только поиск по имени и чтение содержимого, надо делать абстракцию BlobStorage с реализацией FolderBlobStorage и InMemoryBlobStorage и не симулировать всякие там атрибуты и прочее
Тесты не изменятся — он будет предоставлять notifier и ожидать что результатом будет отправленное собщение. Изменятся только реализация и симулятор.
То есть у SUT контракт "если вы мне предоставите notifier я туда пошлю сообщение" а тест "вот тебе notifier а я проверяю сообщения" и таким образом тест проверяет контракт, а все подробности релизации скрыты в симуляторе и в SUT
Сделать фейк (можно вслед за Арло назвать это симулятором пока я не разберусь с определением :)) который позволяет работать с результатом действий и создавать и работать быстро и, таким образом, не шарить между тестами
Ну и чем это отличается от того, что написано в посте как нежелательное?
Тем что остальные аспекты абстракции будут реализованы полностью. То есть подсчет вызовов будет работать только и исключительно в тестировании кешей и чего-то такого и даже в нем сама корректность вызовов будет проверяться при помощи того, что я называю фейком
Количество этих тестов обусловлено только и исключительно тем, какой модуль вы тестируете. Если вам надо
протестировать кэширующий декоратор, то у вас большая часть тестов будет именно такой.
Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.
Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).
Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'.
Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive.
Я бы сделал считающий декоратор для абстракции (интересно, можно автоматом сгенерить). Это весьма специфичные тесты и я бы постарался делать такие тесты как можно меньше
Если делать нотифаер моком, то тест будет хрупкий — зависеть от последовательности вызова методов и прочее.
Что??
Mock подразумевает canned response — то есть если меня вызвали два раза на первый я возвращаю это а на второй то и еще проверяю что в первый раз аргументы такие, в другой другие, нет?
Сделать recording enhancement который будет записывать факт применения enhancement внутри себя. Вне зависимости от того, сколькими и какими вызовами это было сделано.
То есть если какое-то подмножество интерфейса используется чаще, то надо его выделять и использовать и фейкать.
Для оставшихся мы используем тот же симулятор, только создаем его уже в залогиненом виде
ИЛИ используем другой ограниченный интерфейс со своим симулятором
Затем, чтобы протестировать вот эти требования:
•SUT должен логиниться с credentials, полученными из конфигурации
•на каждый успешный логин должен быть логаут
•если логин неуспешен, отправка (и вообще никакие действия с нотификатором) не осуществляется
Напишите требования к SUT использующие данные методы, а я скажу, зачем
Для тех, кто зависит от логина реализация нужна, значит мы ее делаем.
Для тех кто не зависит от логина, предоставляем factory method InMemorySender.CreateLoggedInSender() и не тащим подробности логина и логаута в их тесты.
Это да.
http://xunitpatterns.com/Shared%20Fixture.html
То есть если новый экземпляр симулятора создавать отдельно для каждого теста, он не будет shared fixture
Если нам нужен очень редко Login и Logout надо просто сделать интерфейс, который не содержит их и в большинстве случаев используется именно он. Возможно даже стоит выделить LoggedInSender чтобы система типов контролировала, что либо классу передали залогиненый сендер, либо он сам залогинен.
Ок можете истолковывать мое "не проверять вызовы" как "проверять вызовы, но только так, чтобы все корректные с точки зрения абстракции вызовы считались тестом корректными. Или в максимальной степени соответствовать этому условию"
Ок, не придется менять каждый конкретный тест, придется менять только общую для всех часть в симуляторе
Если это не полная реализация, значит это реализация не этого интерфейса, а какого-то его подмножества.
И как только какой-то метод где-нибудь в глубине начнет использовать что-то выходящее за это подмножество куча тестов могут посыпаться.
То есть тесты будут показывать не то, что требование не выполняется, а то, что просто что-то изменилось.
Фейк должен поддерживать только то, что нужно от "Реального объекта" и это "Только то, что нужно" должно иметь имя интерфейса
Чтобы снизить стоимость реализации надо применять hexagonal architecture и required interface на границах. То есть если у вас есть файловая система, а вам надо использовать только одну папку с файлами, и от нее интересно только поиск по имени и чтение содержимого, надо делать абстракцию BlobStorage с реализацией FolderBlobStorage и InMemoryBlobStorage и не симулировать всякие там атрибуты и прочее
Оно не проверяет. Допустим у нас поменяется интерфейс. Вместо sendNotification будет
startSendNotification, addTextPath, endSendNoptification
Тесты не изменятся — он будет предоставлять notifier и ожидать что результатом будет отправленное собщение. Изменятся только реализация и симулятор.
То есть у SUT контракт "если вы мне предоставите notifier я туда пошлю сообщение" а тест "вот тебе notifier а я проверяю сообщения" и таким образом тест проверяет контракт, а все подробности релизации скрыты в симуляторе и в SUT
Сделать фейк (можно вслед за Арло назвать это симулятором пока я не разберусь с определением :)) который позволяет работать с результатом действий и создавать и работать быстро и, таким образом, не шарить между тестами
Тем что остальные аспекты абстракции будут реализованы полностью. То есть подсчет вызовов будет работать только и исключительно в тестировании кешей и чего-то такого и даже в нем сама корректность вызовов будет проверяться при помощи того, что я называю фейком
Да именно так.
Вы правы, я перепутал. У Фаулера
Я бы сделал считающий декоратор для абстракции (интересно, можно автоматом сгенерить). Это весьма специфичные тесты и я бы постарался делать такие тесты как можно меньше
А почему нам не интересны конкретные операции со строкой (а интересен результат) но такое нельзя применить с другими абстракциями.
Не станут ли тесты более стабильными, если такой же принцип применять к другим абстракциям?