Як знущатися з ConfigurationManager.AppSettings з moq


123

Я застряг у цьому пункті коду, який не знаю, як знущатися:

ConfigurationManager.AppSettings["User"];

Мені потрібно знущатися з ConfigurationManager, але у мене немає поняття, я використовую Moq .

Хтось може дати мені підказку? Дякую!

Відповіді:


103

Я вважаю, що один із стандартних підходів до цього - використовувати фасадну схему, щоб обернути диспетчер конфігурації, і тоді у вас є щось слабко пов'язане, над чим ви маєте контроль.

Отже, ви обмотаєте ConfigurationManager. Щось на зразок:

public class Configuration: IConfiguration
{
    public User
    {
        get
        { 
            return ConfigurationManager.AppSettings["User"];
        }
    }
}

(Ви можете просто витягнути інтерфейс із свого класу конфігурації, а потім використовувати цей інтерфейс скрізь у своєму коді). Тоді ви просто знущаєтесь з IConfiguration. Можливо, ви зможете реалізувати сам фасад кількома різними способами. Вище я вибрав лише для загортання окремих властивостей. Ви також отримуєте побічну вигоду від сильно набраної інформації для роботи, а не слабко набраних хеш-масивів.


6
Це концептуально і те, що я роблю. Однак я використовую Castle DictionaryAdapter Castle (частина Castle Core), який генерує реалізацію інтерфейсу на льоту. Я писав про це деякий час тому: blog.andreloker.de/post/2008/09/05/… (прокрутіть униз до "Рішення", щоб побачити, як я використовую Castle DictionaryAdapter)
Андре Локер

Це витончено, і це гарна стаття. Мені доведеться це пам’ятати на майбутнє.
Джошуа Енфілд

Я можу також додати - залежно від вашого пуризму та інтерпретацій - це замість цього можна також назвати делегатським проксі або адаптером.
Джошуа Енфілд

3
Зверху просто використовується Moq як "звичайний". Неперевірено, але щось на кшталт: var configurationMock = new Mock<IConfiguration>();і для налаштування:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
Джошуа Енфілд

Цей сценарій застосовується, коли шар залежить від IConfiguration і вам потрібно знущатися над IConfiguration, але як би ви протестували реалізацію IConfiguration? І якщо ви зателефонуєте в Unit Test ConfigurationManager.AppSettings ["User"], це не перевірить пристрій, а перевірить, які значення витягнуті з файлу конфігурації, а це не тест одиниці. Якщо вам потрібно перевірити реалізацію, дивіться @ zpbappi.com/testing-codes-with-configurationmanager-appsettings
nkalfov

173

Я використовую AspnetMvc4. Мить назад я написав

ConfigurationManager.AppSettings["mykey"] = "myvalue";

в моєму методі тестування, і це працювало чудово.

Пояснення: метод тестування працює в контексті з налаштуваннями програми, взяті з, як правило, web.configабо myapp.config. ConfigurationsManagerможе досягти цього глобального додатка та маніпулювати ним.

Хоча: Якщо у вас паралельно працює тестовий бігун, це не дуже гарна ідея.


8
Це дійсно розумний і простий спосіб вирішити проблему! Кудо за простоту!
Навап

1
Набагато простіше, ніж створити абстракцію в більшості випадків
Майкл Кларк

2
Це воно???? Блиск полягає в тій простоті, коли я ламав свій мозок про тестування саме цього герметичного класу.
Пьотр Кула

5
ConfigurationManager.AppSettingsце те, NameValueCollectionщо не є безпечним для потоків, тому паралельні тести, що використовують його без належної синхронізації, все одно не є хорошою ідеєю. В іншому випадку ви можете просто зателефонувати ConfigurationManager.AppSettings.Clear()в TestInitialize/ ctor і ви золото.
Охад Шнайдер

1
Простий і стислий. На сьогодні найкраща відповідь!
znn

21

Можливо, це не те, що вам потрібно зробити, але ви обдумали використовувати app.config у своєму тестовому проекті? Таким чином ConfigurationManager отримає значення, які ви вводите в app.config, і вам не потрібно нічого знущатися. Це рішення добре працює для моїх потреб, оскільки мені ніколи не потрібно тестувати конфігураційний файл "змінної".


7
Якщо поведінка коду під тестом змінюється залежно від значення значення конфігурації, його, звичайно, простіше перевірити, якщо він безпосередньо не залежить від AppSettings.
Андре Локер

2
Це погана практика, оскільки ви ніколи не тестуєте інші можливі налаштування. Відповідь Джошуа Енфілда чудово підходить для тестування.
mkaj

4
Хоча інші проти цієї відповіді, я б сказав, що їхня позиція дещо узагальнена. Це дуже вагома відповідь у деяких сценаріях, і це дійсно просто залежить від того, що вам потрібно. Наприклад, скажімо, у мене є 4 різних кластера, кожен з яких має різну базову URL-адресу. Ці 4 кластери витягнуті під час виконання проекту, що Web.configохоплює проект. Під час тестування витягнення з загальновідомих значень app.configдуже правильним. Тест одиниці просто повинен переконатися, що умови, коли він тягне, скажуть, що "cluster1" працює; у цьому випадку є лише 4 різних кластери.
Майк Перренуд

14

Ви можете використовувати shims для зміни AppSettingsспеціального NameValueCollectionоб'єкта. Ось приклад того, як можна досягти цього:

[TestMethod]
public void TestSomething()
{
    using(ShimsContext.Create()) {
        const string key = "key";
        const string value = "value";
        ShimConfigurationManager.AppSettingsGet = () =>
        {
            NameValueCollection nameValueCollection = new NameValueCollection();
            nameValueCollection.Add(key, value);
            return nameValueCollection;
        };

        ///
        // Test code here.
        ///

        // Validation code goes here.        
    }
}

Ви можете прочитати докладнішу інформацію про лайків і підробок за адресою: Ізолюючи код під тестом за допомогою Microsoft Fakes . Сподіваюся, це допомагає.


6
Автор запитує, як з moq, а не про MS Fakes.
JPCF

6
І чим це відрізняється? Він досягає глузування, видаляючи залежність даних зі свого коду. Використання фактів C # - це один підхід!
Зорайр

9

Чи розглядали ви заглушку замість глузування? AppSettingsвластивість NameValueCollection:

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        // Arrange
        var settings = new NameValueCollection {{"User", "Otuyh"}};
        var classUnderTest = new ClassUnderTest(settings);

        // Act
        classUnderTest.MethodUnderTest();

        // Assert something...
    }
}

public class ClassUnderTest
{
    private readonly NameValueCollection _settings;

    public ClassUnderTest(NameValueCollection settings)
    {
        _settings = settings;
    }

    public void MethodUnderTest()
    {
        // get the User from Settings
        string user = _settings["User"];

        // log
        Trace.TraceInformation("User = \"{0}\"", user);

        // do something else...
    }
}

Переваги - це більш проста реалізація і відсутність залежності від System.Configuration, поки вам це справді не потрібно.


3
Мені подобається такий підхід найкраще. З одного боку, обробка конфігураційного менеджера за допомогою IConfigurationДжошуа Енфілд може бути занадто високим, і ви можете пропустити помилки, які існують через такі речі, як неправильний аналіз значення конфігурації. З іншого боку, використання ConfigurationManager.AppSettingsпрямо, як підказує LosManos, є занадто великою деталізацією щодо впровадження, не кажучи вже про те, що це може мати побічні ефекти на інших тестах і не може бути використане в паралельних тестових програмах без ручної синхронізації (як NameValueConnectionце не безпечно для потоків).
Охад Шнайдер

2

Це статична властивість, і Moq призначений для методів або класів екземплярів Moq, які можна знущатися через успадкування. Іншими словами, Moq тут не допоможе вам.

Для глузування зі статики я використовую інструмент під назвою Moles , який є безкоштовним. Є й інші інструменти для ізоляції рамок, наприклад Typemock, які також можуть це зробити, хоча я вважаю, що це платні інструменти.

Що стосується статики та тестування, інший варіант - створити статичний стан самостійно, хоча це часто може бути проблематичним (як, я думаю, це було б у вашому випадку).

І, нарешті, якщо рамки ізоляції не є варіантом, і ви прихильні до цього підходу, фасад, згаданий Джошуа, є хорошим підходом, або будь-яким підходом взагалі, коли ви визначаєте клієнтський код цього від ділової логіки, яку ви Ви використовуєте для тестування.


1

Я думаю, що написання власного постачальника послуг app.config - це просте завдання і корисніше за все інше. Особливо слід уникати будь-яких підробок, таких як прокладки тощо, тому що як тільки ви їх використовуєте, редагування та продовження більше не працює.

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

За замовчуванням вони отримують значення, App.configале для одиничних тестів я можу переосмислити всі значення та використовувати їх у кожному тесті самостійно.

Не потрібно жодних інтерфейсів і не реалізовувати його знову і знову. У мене є dll утиліта і користуюся цим маленьким помічником у багатьох проектах та тестах.

public class AppConfigProvider
{
    public AppConfigProvider()
    {
        ConnectionStrings = new ConnectionStringsProvider();
        AppSettings = new AppSettingsProvider();
    }

    public ConnectionStringsProvider ConnectionStrings { get; private set; }

    public AppSettingsProvider AppSettings { get; private set; }
}

public class ConnectionStringsProvider
{
    private readonly Dictionary<string, string> _customValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

    public string this[string key]
    {
        get
        {
            string customValue;
            if (_customValues.TryGetValue(key, out customValue))
            {
                return customValue;
            }

            var connectionStringSettings = ConfigurationManager.ConnectionStrings[key];
            return connectionStringSettings == null ? null : connectionStringSettings.ConnectionString;
        }
    }

    public Dictionary<string, string> CustomValues { get { return _customValues; } }
}

public class AppSettingsProvider
{
    private readonly Dictionary<string, string> _customValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

    public string this[string key]
    {
        get
        {
            string customValue;
            return _customValues.TryGetValue(key, out customValue) ? customValue : ConfigurationManager.AppSettings[key];
        }
    }

    public Dictionary<string, string> CustomValues { get { return _customValues; } }
}

1

Як щодо того, щоб просто встановити те, що вам потрібно? Тому що я не хочу глузувати з .NET, чи я ...?

System.Configuration.ConfigurationManager.AppSettings["myKey"] = "myVal";

Напевно, слід заздалегідь очистити AppSettings, щоб переконатися, що програма бачить лише те, що ви хочете.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.