Как проверить приватные поля, которые изменены с помощью открытых методов - PullRequest
5 голосов
/ 31 июля 2010

Может кто-нибудь подсказать мне предложенные способы тестирования приватного поля в классе, который модифицируется с помощью открытого метода. Я прочитал много комментариев от людей, которые утверждают, что тестирование частных пользователей не рекомендуется, поскольку они являются внутренними для реализации, однако этот сценарий кажется отличается от большинства других ответов.

Я думал о том, чтобы защитить закрытое поле и создать тестовый подкласс, который выставляет поле, но что, если я не смогу изменить внутреннее содержимое класса?

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

public class Sample
{

    private Dictionary<string, string> _values;
    private DateTime _created;

    public Sample()
    {
        _values = new Dictionary<string, string>();
        _created = DateTime.Now;
    }

    public void AddValue(string key, string value)
    {
        _values.Add(key, value);
    }
}

Ответы [ 5 ]

4 голосов
/ 31 июля 2010

Вам все еще не нужно проверять свои личные переменные. Вы тестируете свои интерфейсы. В этом случае я бы, вероятно, добавил бы свойство Count и использовал бы его для некоторых тестов (или чего-то подобного)

2 голосов
/ 31 июля 2010

Я думаю, что это будет примером, где внедрение зависимостей может помочь.

Здесь происходит то, что вы хотите проверить, правильно ли обновлен внутренний объект (словарь _values) при вызове AddValue. Вы можете добиться этого, вставив в тестовый класс фиктивный словарь.

Это можно сделать, например, следующее. Во-первых, вам нужно немного изменить свой класс Sample:

public class Sample
{
    private IDictionary<string, string> _values = new Dictionary<string, string>();

    protected virtual IDictionary<string, string> GetDictionary()
    {
        return this._values;
    }

    public void AddValue(string key, string value)
    {
        GetDictionary().Add(key, value);
        //    ^^^
        // notice this!
    }
}

Теперь это позволяет вам заменить словарь по умолчанию другим (который вы можете наблюдать в настройках теста), производным от вашего класса Sample и введением фиктивного словаря путем переопределения метода InitializeDictionary:

// this derived class is only needed in your test project:
internal class SampleTest : Sample
{
    public SampleTest(IDictionary<string, string> dictionaryToUse)
    {
        this._dictionaryToUse = dictionaryToUse;
    }

    private IDictionary<string, string> _dictionaryToUse;

    protected override IDictionary<string, string> GetDictionary()
    {
        return this._dictionaryToUse;
    }
}

В вашей тестовой настройке вы можете теперь протестировать этот класс SampleTest вместо вашего Sample класса. Это должно быть в порядке, поскольку производный класс идентичен , за исключением , что позволяет указать словарь, который он будет использовать внутри. Модульный тест для проверки AddValue теперь может выглядеть так:

[Test]
public void AddValue_addSomething_DictionaryHasOneAdditionalEntry()
{
    var mockDictionary = new Dictionary<string, string>();
    var sample = new SampleTest(mockDictionary);
    var oldCount = mockDictionary.Count;

    sample.AddValue(...);

    Assert.AreEqual(oldCount + 1, mockDictionary.Count);
}

Отказ от ответственности: Я ни в коем случае не эксперт по юнит-тестированию, поэтому мой пример может быть ошибочным или даже слишком сложным. Мое намерение состояло в том, чтобы просто продемонстрировать, что вы можете проверить внутренние свойства класса, если вы разрабатываете свой класс достаточно проверяемым способом, например разрешая средства внедрения зависимости.

2 голосов
/ 31 июля 2010

Я думаю, что идея в модульных тестах состоит в том, чтобы проверить поведение объекта по его открытой поверхности, а не по его внутренней реализации.Другими словами, вы не должны обращаться к _values в своем тесте;вместо этого вы должны вызвать публичный метод AddValue(), чтобы добавить несколько ключей / значений, а затем какой-нибудь другой публичный метод, например, GetKeys() или что-то еще, чтобы снова получить информацию.Затем ваш модульный тест должен проверить, является ли эта информация правильной, с учетом информации, которая вошла.

Основная причина этого заключается в том, что модульный тест не должен делать никаких предположений о деталях реализации.Представьте, что по какой-либо причине вам нужно заменить Dictionary<> на SortedDictionary<> позже, или вам вдруг понадобятся два отдельных словаря.Вам не нужно вносить какие-либо изменения в модульный тест, чтобы это работало;у вас должна быть возможность изменить внутреннюю реализацию, а затем проверить ее правильность, используя тот же модульный тест, который вы уже получили.

2 голосов
/ 31 июля 2010

Один из возможных вариантов, если вы хотите убедиться, что _values содержит то, что вы ожидаете после того, как позвоните на Sample.AddValue, будет изменен:

private Dictionary<string, string> _values;

internal Dictionary<string, string> _values;

И затем добавьте InternalsVisibleTo attribute в AssemblyInfo.cs для вашего проекта, ссылаясь на ваш тестовый проект.После этого будет выставлено поле _values ​​для вашего тестового проекта.

Это далеко от идеала, потому что вы будете "тестировать" детали внутренней реализации, но если это так или ничего, это отправная точка!

1 голос
/ 31 июля 2010

Как указал Роб, не рекомендуется иметь доступ к закрытым полям, но если вам отчаянно приходится и не удается на самом деле проверить присвоение значения этому полю, вы можете сделать это следующим образом:

        Type sampleType = sampleInstance.GetType();
        FieldInfo fieldInfo = sampleType.GetField("_values", BindingFlags.Instance, BindingFlags.NonPublic);
        Dictionary<string, string> info = fieldInfo.GetValue(sampleType);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...