亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何單元測試方法調用 IConfiguration.Get<T> 擴展

如何單元測試方法調用 IConfiguration.Get<T> 擴展

C#
蝴蝶不菲 2022-10-23 15:03:26
我有一個非常簡單的方法,我需要進行單元測試。public static class ValidationExtensions{    public static T GetValid<T>(this IConfiguration configuration)    {        var obj = configuration.Get<T>();        Validator.ValidateObject(obj, new ValidationContext(obj), true);        return obj;    }}問題是這configuration.Get<T>是一個靜態擴展方法,不屬于IConfiguration. 我無法更改該靜態方法的實現。我在想,也許最簡單的方法是創建一個內存配置提供程序?但我不知道是否可以在不將其綁定到網絡主機的情況下創建一個。
查看完整描述

4 回答

?
慕碼人2483693

TA貢獻1860條經驗 獲得超9個贊

配置模塊獨立于網絡主機相關功能。


您應該能夠創建一個內存配置來進行測試,而無需將其綁定到 Web 主機。


查看以下示例測試


public class TestConfig {

    [Required]

    public string SomeKey { get; set; }

    [Required] //<--NOTE THIS

    public string SomeOtherKey { get; set; }

}


//...


[Fact]

public void Should_Fail_Validation_For_Required_Key() {

    //Arrange

    var inMemorySettings = new Dictionary<string, string>

    {

        {"Email:SomeKey", "value1"},

        //{"Email:SomeOtherKey", "value2"}, //Purposely omitted for required failure

        //...populate as needed for the test

    };


    IConfiguration configuration = new ConfigurationBuilder()

        .AddInMemoryCollection(inMemorySettings)

        .Build();


    //Act

    Action act = () => configuration.GetSection("Email").GetValid<TestConfig>();


    //Assert

    ValidationException exception = Assert.Throws<ValidationException>(act);

    //...other assertions of validation results within exception object

}

在我看來,這將接近于集成測試,但理想情況下,您只是使用依賴于框架的特性來隔離擴展方法的測試。


查看完整回答
反對 回復 2022-10-23
?
Helenr

TA貢獻1780條經驗 獲得超4個贊

大多數模擬庫(Moq、FakeItEasy 等)都不能模擬擴展方法。


因此,您必須以 aIConfiguration.Get<T>返回 T 實例的方式“填充”您的 IConfiguration。Nkoski 答案適用于很多場景,但如果您需要測試調用的代碼,IConfiguration.Get<T>則可以使用下面的示例:


using System;

using System.IO;

using System.Text;

using System.Text.Json;

using System.Collections.Generic;

using Microsoft.Extensions.Configuration;

using Xunit;


public class TestClass {

    public class Movie

    {

        public string Name { get; set; }

        public decimal Rating { get; set; }

        public IList<string> Stars { get; set; } //it works with collections

    }


    [Fact]

    public void MyTest()

    {

        var movie = new Movie { 

            Name = "Some Movie",

            Rating = 9, 

            Stars = new List<string>{"Some actress", "Some actor"}

        };


        var movieAsJson = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(movie));

        using(var stream = new MemoryStream(movieAsJson))

        {

            var config = new ConfigurationBuilder().AddJsonStream(stream).Build();

            var movieFromConfig = config.Get<Movie>();

            //var sut = new SomeService(config).SomeMethodThatCallsConfig.Get<Movie>()

        }

    }

}


查看完整回答
反對 回復 2022-10-23
?
吃雞游戲

TA貢獻1829條經驗 獲得超7個贊

解決問題的一種略有不同的方法,避免了 Mock 和大量設置噪音:

InMemoryConfiguration幾乎給了我我需要的東西,所以我對其進行了擴展,以便您可以在構建配置后修改值(我的情況是我在構建配置時不知道所有模擬值)

https://gist.github.com/martinsmith1968/9567de76d2bbe537af05d76eb39b1162

底部的單元測試顯示用法


查看完整回答
反對 回復 2022-10-23
?
慕田峪7331174

TA貢獻1828條經驗 獲得超13個贊

[TestClass]

public class UnitTest1

{

    [TestMethod]

    public void TestMethod1()

    {

        IConfiguration mock = new MockConfiguration();

        var simpleObject = mock.GetValid<SimpleObject>();

        Assert.AreEqual(simpleObject.MyConfigStr, "123");

    }

}


public class SimpleObject

{

    public string MyConfigStr { get; set; }

}



public class MockConfiguration : IConfiguration

{

    public IConfigurationSection GetSection(string key)

    {

        return new MockConfigurationSection()

        {

            Value = "123"

        };

    }


    public IEnumerable<IConfigurationSection> GetChildren()

    {

        var configurationSections = new List<IConfigurationSection>()

        {

            new MockConfigurationSection()

            {

                Value = "MyConfigStr"

            }

        };

        return configurationSections;

    }


    public Microsoft.Extensions.Primitives.IChangeToken GetReloadToken()

    {

        throw new System.NotImplementedException();

    }


    public string this[string key]

    {

        get => throw new System.NotImplementedException();

        set => throw new System.NotImplementedException();

    }

}


public class MockConfigurationSection : IConfigurationSection

{

    public IConfigurationSection GetSection(string key)

    {

        return this;

    }


    public IEnumerable<IConfigurationSection> GetChildren()

    {

        return new List<IConfigurationSection>();

    }


    public IChangeToken GetReloadToken()

    {

        return new MockChangeToken();

    }


    public string this[string key]

    {

        get => throw new System.NotImplementedException();

        set => throw new System.NotImplementedException();

    }


    public string Key { get; }

    public string Path { get; }

    public string Value { get; set; }

}


public class MockChangeToken : IChangeToken

{

    public IDisposable RegisterChangeCallback(Action<object> callback, object state)

    {

        return new MockDisposable();

    }


    public bool HasChanged { get; }

    public bool ActiveChangeCallbacks { get; }

}


public class MockDisposable : IDisposable

{

    public void Dispose()

    {

    }

}

為 IConfiguration 創建了一個模擬并模仿 ConfigBinder 的行為


using Microsoft.Extensions.Configuration;

using Microsoft.Extensions.Primitives;

添加了這兩個名稱空間以進行編譯


查看完整回答
反對 回復 2022-10-23
  • 4 回答
  • 0 關注
  • 166 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號