如何单元测试方法调用 IConfiguration.get<T> extension



我有一个非常简单的方法,我需要进行单位测试。

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。我无法更改该静态方法的实现。

我在想,也许最简单的方法是创建内存配置提供商?但是我不知道我是否可以创建一个而不将其绑定到Web主机。

配置模块独立于Web主机相关的功能。

您应该能够创建一个内存配置以对其进行测试,而无需将其绑定到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
}

我认为这将接近集成测试,但是理想情况下,您只是使用依赖框架的功能来隔离扩展方法的测试。

解决问题,避免模拟和大量设置噪声的方式略有不同:

inmemoryconfiguration 几乎给了我所需的东西,因此我扩展了它,以便在构建配置后可以修改值构建配置)

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

底部的单元测试显示使用

大多数模拟库(订单,伪造等)无法模拟扩展方法。

因此,您必须以 IConfiguration.Get<T>返回T. nkoski答案的实例的方式"填充" iConFiguration,适用于场景的 lot ,但如果您需要/strong>要测试调用IConfiguration.Get<T>的代码,您可以使用示例Bellow:

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>()
        }
    }
}
[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;

添加了这两个编译的名称空间

相关内容

  • 没有找到相关文章

最新更新