Пересмешивание локальных переменных в 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, обычно эти параметры лучше передавать в большинстве случаев.