Пересмешивание локальных переменных в C#

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

public static bool IsUpdateAvailable()
{
    Version installedVersion = Util.GetInstalledVersionFromRegistry();
    Version availableVersion = Util.GetAvailableVersionFromRemote();

    bool isRemoteVersionNewer = IsVersionNewer(installedVersion, availableVersion);

    return isRemoteVersionNewer;
}

Таким образом, проблема заключается в том, чтобы две локальные переменные (selectedVersion, availableVersion) считывали свои значения не из реальных источников (в данном случае из реестра и Интернета), а из какого-то фальшивого источника. Я не могу изменить вышеупомянутый метод. И я пытаюсь понять, как я могу смоделировать эти две переменные, используя, например, Moq или Microsoft Fakes. Я выполнил поиск по Интернету, но не смог найти соответствующий пример кода. Итак, как я могу смоделировать локальные переменные вышеупомянутого метода и проверить этот метод?

3 ответа

Решение

Я также предлагаю внедрение зависимости, если вы можете это сделать. Если вы не можете решить эту проблему, вы можете использовать прокладки.

http://msdn.microsoft.com/en-us/library/hh549176(v=vs.110).aspx

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

(Код не скомпилирован и не протестирован)

public static Version GetInstalledVersionFromRegistry()
{
    return Util.GetInstalledVersionFromRegistry();
}

public static Version GetVersionFromRemote()
{
    return Util.GetAvailableVersionFromRemote();
}

public static bool IsUpdateAvailable()
{
    Version installedVersion = GetInstalledVersionFromRegistry();
    Version availableVersion = GetVersionFromRemote();

    bool isRemoteVersionNewer = IsVersionNewer(installedVersion, availableVersion);

    return isRemoteVersionNewer;
}

Тестирование с использованием прокладок

using (ShimsContext.Create())
 {
        ShimYourclass.GetInstalledVersionFromRegistry=()=>new Version();
        ShimYourclass.GetInstalledVersionFromRegistry=()=>new Version();

        //test your class here    
        Yourclass.IsUpdateAvailable();
 }

Это та же проблема со всеми статическими и жестко закодированными зависимостями. Старайтесь избегать статики везде, где можете, и в первую очередь классифицируйте ее как плохую. Разрабатывайте реальные аргументы для пометки чего-либо как статического, а не потому, что R# сказал вам об этом.

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

ctor(Version installedVersion, Version availableVersion) {
    // Maybe store them in private fields.
}

public bool IsUpdateAvailable()
{
    bool isRemoteVersionNewer = IsVersionNewer(installedVersion, availableVersion);

    return isRemoteVersionNewer;
}

Полагаю, теперь вы видите, что вам не нужно издеваться над переменными. Вы можете просто создавать разные экземпляры вашего объекта с разными входами. Тот, который создает объект с вашим Util. методы и другие с тестовыми данными для ваших юнит-тестов.

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

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

Хотя, как говорит TimmKrause, обычно эти параметры лучше передавать в большинстве случаев.

Другие вопросы по тегам