Cara memalsukan ConfigurationManager.AppSettings dengan moq

124

Saya terjebak pada titik kode ini yang saya tidak tahu cara mengejeknya:

ConfigurationManager.AppSettings["User"];

Saya harus mengejek ConfigurationManager, tetapi saya tidak tahu, saya menggunakan Moq .

Seseorang bisa memberi saya tip? Terima kasih!

Otuyh
sumber

Jawaban:

105

Saya percaya satu pendekatan standar untuk ini adalah dengan menggunakan fasad pola untuk membungkus manajer konfigurasi dan kemudian Anda memiliki sesuatu yang digabungkan secara longgar yang dapat Anda kendalikan.

Jadi, Anda akan membungkus ConfigurationManager. Sesuatu seperti:

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

(Anda bisa mengekstrak antarmuka dari kelas konfigurasi Anda dan kemudian menggunakan antarmuka itu di mana-mana dalam kode Anda) Kemudian Anda hanya mengejek IConfiguration. Anda mungkin dapat menerapkan fasad itu sendiri dengan beberapa cara berbeda. Di atas saya memilih hanya untuk membungkus properti individu. Anda juga mendapatkan keuntungan tambahan karena memiliki informasi yang diketik dengan kuat untuk digunakan daripada array hash yang diketik lemah.

Joshua Enfield
sumber
6
Secara konseptual, inilah yang saya lakukan juga. Namun, saya menggunakan Castle DictionaryAdapter (bagian dari Castle Core) yang menghasilkan implementasi antarmuka dengan cepat. Saya telah menulis tentang itu beberapa waktu lalu: blog.andreloker.de/post/2008/09/05/… (gulir ke bawah ke "A Solution" untuk melihat bagaimana saya menggunakan Castle DictionaryAdapter)
Andre Loker
Itu bagus dan itu artikel yang bagus. Saya harus mengingat ini untuk masa depan.
Joshua Enfield
Saya mungkin juga menambahkan - tergantung pada kemurnian dan interpretasi Anda - ini bisa atau juga disebut Proxy atau Adaptor Delegasi.
Joshua Enfield
3
Dari atas itu hanya menggunakan Moq sebagai "normal". Belum teruji, tapi sesuatu seperti: var configurationMock = new Mock<IConfiguration>();dan untuk penyiapan:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
Joshua Enfield
Skenario ini digunakan ketika sebuah layer bergantung pada IConfiguration dan Anda perlu meniru IConfiguration, tetapi bagaimana Anda akan menguji Implementasi IConfiguration? Dan jika Anda memanggil dalam Unit Test ConfigurationManager.AppSettings ["User"] maka ini tidak akan menguji unit, tetapi akan menguji nilai apa yang diambil dari file konfigurasi, yang bukan merupakan pengujian unit. Jika Anda perlu memeriksa implementasinya, lihat @ zpbappi.com/testing-codes-with-configurationmanager-appsettings
nkalfov
174

Saya menggunakan AspnetMvc4. Beberapa saat yang lalu saya menulis

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

dalam metode pengujian saya dan itu bekerja dengan sempurna.

Penjelasan: metode pengujian berjalan dalam konteks dengan pengaturan aplikasi yang diambil dari, biasanya a web.configatau myapp.config.ConfigurationsManagerdapat mencapai objek application-global ini dan memanipulasinya.

Meskipun: Jika Anda memiliki runner pengujian yang menjalankan pengujian secara paralel, ini bukanlah ide yang baik.

LosManos
sumber
8
Ini benar-benar cara yang cerdas dan sederhana untuk menyelesaikan masalah! Kudos untuk kesederhanaannya!
Navap
1
Jauh lebih mudah daripada membuat abstraksi dalam banyak kasus
Michael Clark
2
Itu dia???? Kecemerlangannya ada dalam kesederhanaan saat aku memeras otak tentang cara menguji kelas tersegel khusus ini.
Piotr Kula
6
ConfigurationManager.AppSettingsadalah NameValueCollectionyang tidak aman untuk thread, jadi pengujian paralel yang menggunakannya tanpa sinkronisasi yang tepat bukanlah ide yang baik. Jika tidak, Anda hanya dapat memanggil ConfigurationManager.AppSettings.Clear()di Anda TestInitialize/ ctor dan Anda sedang emas.
Ohad Schneider
1
Sederhana dan ringkas. Sejauh ini jawaban terbaik!
znn
21

Mungkin bukan itu yang perlu Anda capai, tetapi apakah Anda pernah mempertimbangkan untuk menggunakan app.config dalam proyek pengujian Anda? Jadi ConfigurationManager akan mendapatkan nilai yang Anda masukkan ke app.config dan Anda tidak perlu meniru apa pun. Solusi ini berfungsi dengan baik untuk kebutuhan saya, karena saya tidak perlu menguji file konfigurasi "variabel".

Iridio
sumber
7
Jika perilaku kode yang diuji berubah bergantung pada nilai nilai konfigurasi, tentu lebih mudah untuk mengujinya jika tidak bergantung pada AppSettings secara langsung.
Andre Loker
2
Ini adalah praktik yang buruk karena Anda tidak pernah menguji kemungkinan pengaturan lain. Jawaban Joshua Enfield sangat bagus untuk pengujian.
mkaj
4
Sementara yang lain menentang jawaban ini, saya akan mengatakan posisi mereka agak digeneralisasi. Ini adalah jawaban yang sangat valid dalam beberapa skenario dan itu benar-benar tergantung pada apa yang Anda butuhkan. Misalnya, saya memiliki 4 cluster berbeda, masing-masing memiliki URL dasar yang berbeda. 4 cluster tersebut ditarik, selama runtime, dari Web.configproyek yang mencakup. Selama pengujian, menarik beberapa nilai terkenal dari app.configitu sangat valid. Tes unit hanya perlu memastikan kondisi saat menarik mengatakan "cluster1" berfungsi; hanya ada 4 cluster berbeda dalam kasus ini.
Mike Perrenoud
14

Anda dapat menggunakan shims untuk memodifikasi objek AppSettingskhusus NameValueCollection. Berikut adalah contoh bagaimana Anda bisa mencapai ini:

[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.        
    }
}

Anda dapat membaca selengkapnya tentang shims dan fakes di, Isolating Code Under Test with Microsoft Fakes . Semoga ini membantu.

Zorayr
sumber
6
Penulis menanyakan bagaimana dengan moq, bukan tentang MS Fakes.
JPCF
6
Dan bagaimana ini berbeda? Itu mencapai mengejek dengan menghapus ketergantungan data dari kodenya. Menggunakan C # Fakes adalah salah satu pendekatan!
Zorayr
9

Apakah Anda pernah mempertimbangkan untuk menghina daripada mengejek? The AppSettingsproperti adalah 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...
    }
}

Manfaatnya adalah implementasi yang lebih sederhana dan tidak bergantung pada System.Configuration sampai Anda benar-benar membutuhkannya.

DanielLarsenNZ
sumber
3
Saya paling suka pendekatan ini. Di satu sisi, membungkus manajer konfigurasi dengan IConfigurationseperti yang disarankan Joshua Enfield bisa jadi level yang terlalu tinggi, dan Anda mungkin melewatkan bug yang ada karena hal-hal seperti penguraian nilai konfigurasi yang buruk. Di sisi lain, menggunakan ConfigurationManager.AppSettingssecara langsung seperti yang disarankan LosManos adalah terlalu banyak detail implementasi, belum lagi hal itu dapat memiliki efek samping pada pengujian lain dan tidak dapat digunakan dalam pengujian paralel tanpa sinkronisasi manual (karena NameValueConnectiontidak aman untuk thread).
Ohad Schneider
2

Itu adalah properti statis, dan Moq dirancang untuk metode instance Moq atau kelas yang dapat diejek melalui pewarisan. Dengan kata lain, Moq tidak akan membantu Anda di sini.

Untuk mengejek statika, saya menggunakan alat yang disebut Mol , yang gratis. Ada alat isolasi kerangka kerja lainnya, seperti Typemock yang dapat melakukan ini juga, meskipun saya yakin itu adalah alat berbayar.

Ketika datang ke statika dan pengujian, opsi lain adalah membuat sendiri keadaan statis, meskipun ini sering kali bisa menjadi masalah (seperti, saya membayangkan itu akan terjadi dalam kasus Anda).

Dan, akhirnya, jika kerangka isolasi bukanlah pilihan dan Anda berkomitmen pada pendekatan ini, fasad yang disebutkan oleh Joshua adalah pendekatan yang baik, atau pendekatan apa pun secara umum di mana Anda memfaktorkan kode klien ini jauh dari logika bisnis yang Anda gunakan. sedang digunakan untuk menguji.

Erik Dietrich
sumber
1

Saya pikir menulis penyedia app.config Anda sendiri adalah tugas yang sederhana dan lebih berguna daripada yang lainnya. Terutama Anda harus menghindari pemalsuan seperti shims dll karena segera setelah Anda menggunakannya Edit & Lanjutkan tidak berfungsi lagi.

Penyedia yang saya gunakan terlihat seperti ini:

Secara default, mereka mendapatkan nilai dari App.configtetapi untuk pengujian unit, saya dapat mengganti semua nilai dan menggunakannya di setiap pengujian secara independen.

Tidak perlu antarmuka apa pun atau menerapkannya setiap kali berulang kali. Saya memiliki dll utilitas dan menggunakan pembantu kecil ini di banyak proyek dan unit tes.

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; } }
}
t3chb0t
sumber
1

Bagaimana dengan hanya mengatur apa yang Anda butuhkan? Karena, saya tidak ingin mengejek NET, apakah saya ...?

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

Anda mungkin harus membersihkan AppSettings sebelumnya untuk memastikan aplikasi hanya melihat apa yang Anda inginkan.

Eike
sumber