Модульное тестирование и макетирование доменных объектов - PullRequest
0 голосов
/ 02 октября 2018

У меня есть класс домена, который выглядит следующим образом:

public class Employee
{
    public Guid EmployeeId { get; private set; }
    public string Name { get; private set; }
    public string Surname { get; private set; }
    ...
    // other properties

    public ICollection<Language> Languages { get; private set; }
        = new List<Language>();

    public ICollection<Skill> Skills { get; private set; }
        = new List<Skill>();

    public void AddLanguage(Language language)
    {
        if (language == null)
            return;

        Languages.Add(language);
    }

    public void DeleteLanguage(Guid languageId)
    {
        var languageToDelete = Languages
            .SingleOrDefault(x => x.LanguageId == languageId);

        if(languageToDelete == null)
            throw new ArgumentException("Language entry doesn't exist.");

        Languages.Remove(languageToDelete);
    }
}

Я хотел бы протестировать данные методы, но я застрял.

У меня есть:

    [Fact]
    public void AddLanguage_AfterCallWithValidObject_LanguagesCollectionContainsAddedObject()
    {
        var language = new Mock<Language>();
        var employee = new Employee("Name", "Surname", ...);

        employee.AddLanguage(language.Object);

        Assert.Contains(employee.EmployeeLanguages, x => x.Language.Equals(language.Object));
    }

    [Fact]
    public void DeleteLanguage_WhenLanguageWithGivenIdDoesntExist_ThrowArgumentException()
    {
        var languageToDelete = new Language("English");

        var employee = new Mock<Employee>();
        employee.Setup(x => x.Languages).Returns(new List<Languages>
        {
            new Language("Spanish"),
            new Language("German")
        });

        employee.Object.DeleteLanguage(languageToDelete);

        // Asserts here
    }

В первом тесте я также хотел бы заявить, что Languages.Add (skill) метод был вызван, но я не знаю, как это сделать.

  1. Этоэлегантный способ сделать это?Я думал о насмешливом методе Add , но я не уверен, что это хорошая идея.

Во втором тесте я не могу просто насмехаться над объектом Employee как это не интерфейс.Я думал о разоблачении Employee , но я прочитал, что не следует делать это только для целей тестирования.

Как мне высмеивать свойство Languages ​​, не раскрывая Employee в качестве интерфейса?Является ли это возможным?Это хорошая практика, чтобы делать такие вещи? Является ли моя общая концепция для тестирования этих методов в порядке?(Я новичок в модульном тестировании)

Ответы [ 2 ]

0 голосов
/ 03 октября 2018

Вы должны тестировать объекты как «черный ящик», не полагаясь на детали реализации.В вашем случае детали реализации состоят в том, что Employee класс использует метод ICollection.Add.

И вам определенно не нужно вообще насмехаться в вашем случае.
Имитировать только зависимости, которые сделаюттесты медленные или очень, очень, очень, очень сложные для настройки для теста.

[Fact]
public void AddLanguage_ShouldSaveGivenLanguage()
{
    var language = new Language();
    var employee = new Employee("Name", "Surname");

    employee.AddLanguage(language);

    var expectedLanguages = new[] { language };
    employee.EmployeeLanguages.Should().BeEquivalentTo(expectedLanguages);
}

Используйте общедоступный класс API Employee, обеспечивающий его настройку для теста (задний блок).
Для тестированияDeleteLanguage добавление фиктивных языков через общедоступный API класса.

[Fact]
public void DeleteLanguage_WhenLanguageExists_Remove()
{
    var language1 = new Language("German");
    var language2 = new Language("French");
    var languageToDelete = new Language("English");

    var employee = new Employee("Name", "Surname");
    employee.AddLanguage(language1);
    employee.AddLanguage(language2);
    employee.AddLanguage(languageToDelete);

    employee.DeleteLanguage(languageToDelete);

    var expectedLanguages = new[] { language1, language2 };
    employee.EmployeeLanguages.Should().BeEquivalentTo(expectedLanguages);
}

[Fact]
public void DeleteLanguage_WhenLanguageNotExists_ThrowException()
{
    var language1 = new Language("German");
    var language2 = new Language("French");
    var notExistedLanguage = new Language("English");

    var employee = new Employee("Name", "Surname");
    employee.AddLanguage(language1);
    employee.AddLanguage(language2);

    Action delete = () => employee.DeleteLanguage(languageToDelete);

    delete.Should()
          .Throw<ArgumentException>()
          .WithMessage("Language entry doesn't exist.");
}

Вы заметили, как громоздко employee.EmployeeLanguages читает, вы можете переименовать свойство просто в employee.Languages.

Для читабельностиутверждения, которые я использовал Библиотека FluentAssertions

0 голосов
/ 02 октября 2018

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

//Arrange
var e = new Employee();
var l = new Mock<Language>(); 

//Act
e.AddLanguage(l.Object);

//Assert
Assert.IsTrue(e.Languages.Contains(l.Object));

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...