Воздержитесь от использования вашего контейнера DI в рамках ваших модульных тестов. В модульных тестах вы пытаетесь протестировать один класс или модуль изолированно, и мало что нужно для контейнера DI в этой области.
. Тестирование интеграции отличается от теста интеграции, поскольку вы хотите проверить, как компоненты в вашей системе интегрируются и работают вместе. В этом случае вы часто используете конфигурацию вашего производства DI и меняете некоторые из своих услуг для поддельных сервисов (например, ваш MailService
), но придерживайтесь как можно ближе к реальной вещи. В этом случае вы используете свой контейнер для разрешения всего графика объекта.
Желание использовать контейнер DI в модульных тестах часто также связано с неэффективными шаблонами. Например, если вы попытаетесь создать тестируемый класс со всеми его зависимостями в каждом тесте, вы получите много дублированного кода инициализации, и небольшое изменение в тестируемом классе может в этом случае пульсировать через систему и потребовать от вас смените десятки единичных тестов. Это, очевидно, вызывает проблемы с ремонтопригодностью.
Один шаблон, который помог мне в этом много в прошлом, - это использование простого тестового отраслевого метода SUT. Этот метод централизует создание тестируемого класса и минимизирует количество изменений, которые необходимо внести, когда изменяются зависимости тестируемого класса. Так может выглядеть такой заводской метод:
private ClassUnderTest CreateClassUnderTest(
ILogger logger = null, IMailSender mailSender = null, IEventPublisher publisher = null)
{
return new ClassUnderTest(
logger ?? new FakeLogger(),
mailSender ?? new FakeMailer(),
publisher ?? new FakePublisher());
}
Этот заводский метод дублирует аргументы конструктора класса, но делает их необязательными. Для любой конкретной неподходящей зависимости будет введена новая поддельная реализация.
Это работает очень хорошо, так как в большинстве тестов вас интересует только одна или две зависимости. Другие зависимости могут потребоваться для функционирования класса, но не интересны для этого конкретного теста. Таким образом, заводский метод позволяет вам предоставлять только те зависимости, которые интересны для теста, при устранении шума неиспользуемых зависимостей. Таким образом, заводский метод позволяет вам написать следующий тест:
public void Test() {
// Arrange
var logger = new ListLogger();
ClassUnderTest sut = CreateClassUnderTest(logger: logger);
// Act
sut.DoSomething();
// Arrange
Assert.IsTrue(logger.Count > 0);
}
Если вы заинтересованы в том, чтобы узнать, как писать тесты, читаемые, заслуживающие доверия и поддерживаемые ( RTM ), я советую вы должны прочитать книгу Роя Ошерове «Искусство модульного тестирования» (второе издание). Это очень помогло мне.
select login_date
,id
,flag
,case when flag = 'Y' then min(login_date) over(partition by id,grp) end as flag_date
from (select login_date,id,flag
,row_number() over(partition by id order by login_date) -
row_number() over(partition by id,flag order by login_date) as grp
from tbl
) t
Еще один подход для решения этой проблемы заключается в создании новой группы всякий раз, когда встречается значение «N». Внешний запрос остается прежним, меняется только внутренний.
select login_date
,id
,flag
,case when flag = 'Y' then min(login_date) over(partition by id,grp) end as flag_date
from (select login_date,id,flag
,sum(case when flag = 'N' then 1 else 0 end) over(partition by id order by login_date) as grp
from tbl
) t