Messagebox и модульное тестирование
Я пытаюсь найти лучший способ отцепить messageboxes от моей логики, чтобы я мог правильно unittest его. Теперь мне было интересно, будет ли достаточно, если я просто сделаю отдельный вспомогательный класс (C#), который я могу заглушить позже для моего messagebox. Например:
static class messageBoxHelper
{
public static void msgBoxAlg(string message, string title, MessageBoxButtons buttons, MessageBoxIcon icons, bool show)
{
if (show)
{
MessageBox.Show(message, title, buttons, icons);
}
}
тогда каждый раз, когда мне нужно будет использовать messagebox, я бы просто использовал messageboxHelper/msgBoxAlg(...) вместо messagebox.шоу.(..). Используя bool show, Я могу включить или отключить его во время тестирования.
Я просто интересно, если это "правильный путь". Под этим я подразумеваю, есть ли более простой или лучший способ сделать это правильно? Я не могу просто бросить мессенджеры, они передают "жизненно важную" информацию пользователю ("вы хотите закрыть это окно?"Да/нет и т. д.). Это также может быть просто я не использую надлежащую программную инженерию, и я должен отделить мои мессенджеры от моего bussinesslogic больше?
4 ответов
Да, это правильно. Но вместо статического класса, вы должны реализовать IDialogService
и вставьте его в классы, которые должны отображать диалоги:
public interface IDialogService
{
void ShowMessageBox(...);
...
}
public class SomeClass
{
private IDialogService dialogService;
public SomeClass(IDialogService dialogService)
{
this.dialogService = dialogService;
}
public void SomeLogic()
{
...
if (ok)
{
this.dialogService.ShowMessageBox("SUCCESS", ...);
}
else
{
this.dialogService.ShowMessageBox("SHIT HAPPENS...", ...);
}
}
}
в ходе проверки SomeClass
вы должны ввести макет объекта IDialogService
вместо реальной.
Если вам нужно проверить больше логики пользовательского интерфейса, рекомендуется использовать MVVM узор.
посмотрите на инверсию управления (IoC), основной принцип заключается в том, что вещи, которые выполняют ect действий, должны быть переданы в качестве интерфейса, затем вы используете контейнер IoC для привязки интерфейсов к конкретным реализациям для вашего приложения. Чтобы легко достичь этого, в вашем случае передайте то, что делает окна сообщений в качестве интерфейса, и в вашем модульном тесте создайте макет (поддельную) версию этой службы сообщений, которая не показывает окно сообщения
посмотреть http://martinfowler.com/articles/injection.html для деталей на IoC, мой любимый контейнер Ninject (http://ninject.org)
В идеале вы хотите, чтобы код вашего тестирования с модульными тестами был логическим, а не пользовательским интерфейсом. Поэтому логика вашего тестирования не должна действительно отображение окна сообщения. Если вы хотите протестировать пользовательский интерфейс, я бы предложил закодированные тесты пользовательского интерфейса.
судя по вашему вопросу, я бы предположил, что ваш код не должен использовать MessageBox
. Возможно, вместо этого рассмотрите возможность использования обратного вызова или произвольных Action
, или подходы, упомянутые Люком Макгрегором и Сергей Владимирович!--7-->
"модульный тест", в его точном значении, является тестом атомарного поведения. Это не единственный вид тестов, управляемых кодом, которые вы можете сделать для своего кода. Особенно для тестирования более длинных сценариев с диалоговыми окнами" Да/Нет", которые вы упомянули, крупномасштабные тесты, управляемые кодом, часто более эффективны, чем модульные тесты.
однако, чтобы писать их было проще, было бы неплохо не только создать специальную службу, как упоминал Сергей, но и сделать ее звонки асинхронный:
public interface IDialogService
{
Task<bool> ShowYesNoMessageBox(...);
...
}
обертывая messageboxes в несинхронные вызовы служб и издеваясь над ними, для более длинных сценариев вы начнете противоречить шаблону "аранжировать-действовать-утверждать", предсказывая действия пользователя до того, как это произойдет (выполнение "аранжировать" вместо "действовать"), что может вызвать многочисленные проблемы при тестировании, особенно если ваши тесты выполняются с помощью BDD/SpecFlow. Асинхронность этих вызовов позволяет избежать подобных проблем. Смотрите мой статья в блоге для получения подробной информации и образцы крупномасштабных тестов с messageboxes.