Совет по C#: Как создавать модульные тесты для проверки моделей

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

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

Для этого сценария хорошо подходит BDD, а для его постепенной реализации можно использовать TDD.

Хорошо, но как проверить, что модели и атрибуты модели, которые вы определили, верны?

Давайте определим простую модель:

public class User
{
    [Required]
    [MinLength(3)]
    public string FirstName { get; set; }

    [Required]
    [MinLength(3)]
    public string LastName { get; set; }

    [Range(18, 100)]
    public int Age { get; set; }
}

Правильно ли мы определили нашу модель? Охватываем ли мы все крайние случаи? Хорошо написанный набор юнит-тестов – наш лучший друг!

У нас есть два варианта: мы можем написать интеграционные тесты для отправки запросов к нашей системе, которая работает с сервером in-memory, и проверить полученный ответ. Или мы можем использовать внутренний класс Validator, который используется в ASP.NET для проверки моделей ввода, для создания тонких и быстрых Unit-тестов. Давайте воспользуемся вторым подходом.

Вот метод-утилита, который мы можем использовать в наших тестах:

public static IList<ValidationResult> ValidateModel(object model)
{
    var results = new List<ValidationResult>();

    var validationContext = new ValidationContext(model, null, null);

    Validator.TryValidateObject(model, validationContext, results, true);

    if (model is IValidatableObject validatableModel)
       results.AddRange(validatableModel.Validate(validationContext));

    return results;
}

Одним словом, мы создаем контекст проверки без каких-либо внешних зависимостей, ориентированный только на входную модель: new ValidationContext(model, null, null).

Далее мы проверяем каждое поле, вызывая TryValidateObject, и сохраняем результаты проверки в списке, result.

Наконец, если модель реализует интерфейс IValidatableObject, который раскрывает метод Validate, мы вызываем этот метод Validate() и сохраняем возвращенные ошибки валидации в созданном ранее списке result.

Как видите, мы можем обрабатывать как проверку, исходящую из атрибутов полей, таких как [Required], так и пользовательскую проверку, заданную в методе Validate() класса модели.

Теперь мы можем использовать этот метод для проверки того, прошла ли проверка и, в случае неудачи, какие ошибки были возвращены:

[Test]
public void User_ShouldPassValidation_WhenModelIsValid()
{
    var model = new User { FirstName = "Davide", LastName = "Bellone", Age = 32 };
    var validationResult = ModelValidationHelper.ValidateModel(mode);
    Assert.That(validationResult, Is.Empty);
}

[Test]
public void User_ShouldNotPassValidation_WhenLastNameIsEmpty()
{
    var model = new User { FirstName = "Davide", LastName = null, Age = 32 };
    var validationResult = ModelValidationHelper.ValidateModel(mode);
    Assert.That(validationResult, Is.Not.Empty);
}


[Test]
public void User_ShouldNotPassValidation_WhenAgeIsLessThan18()
{
    var model = new User { FirstName = "Davide", LastName = "Bellone", Age = 10 };
    var validationResult = ModelValidationHelper.ValidateModel(mode);
    Assert.That(validationResult, Is.Not.Empty);
}
+1
0
+1
0
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *