Daniil T.
Daniil T.

Reputation: 1175

MOQ IConfiguration with specific parameters

Today I was writing a unit test for one of my class which has a parameter of IConfiguration in the constructor. I tried to freeze dependency and create sut.

 configuration = builders.Freeze<IConfiguration>();
 apiConfiguration = builders.Create<IAPIConfiguration>();

When I ran the test I got an exception because in API configuration constructor I have validation line.

 this.API_KEY = configuration["API:Key"] ?? throw new NoNullAllowedException("API key wasn't found.");

It seems that it didn't mock the right way or at least the way I wanted. I start to wonder is there any way to mock out IConfiguration class with customizable keys?

UPDATED:

SUT:

public class APIConfiguration : IAPIConfiguration
    {
        public APIConfiguration(IConfiguration configuration)
        {
            this.API_KEY = configuration["API:Key"] ?? throw new NoNullAllowedException("API key wasn't found.");
            this._url = configuration["API:URL"] ?? throw new NoNullAllowedException("API key wasn't found.");
        }

        public string API_KEY { get; }

        private string _url
        {
            get { return this._url; }
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                    throw new NoNullAllowedException("API url wasn't found.");
                this._url = value;
            }
        }

        public Uri URL
        {
            get
            {
                return this.URL;
            }
            private set
            {
                value = new Uri(this._url);
            }
        }
    }

Test case so far:

[TestClass]
    public class UnitTest1
    {

        private readonly IFixture builders;
        private readonly string _apiKey;
        private readonly string _url;
        private readonly IAPIConfiguration apiConfiguration;
        private readonly IConfiguration configuration;

        public UnitTest1()
        {
            builders = new Fixture().Customize(new AutoMoqCustomization());
            _apiKey = builders.Create<string>();
            _url = builders.Create<string>();
            configuration = builders.Freeze<IConfiguration>();
            configuration["API:Key"] = "testKey";
            configuration["API:URL"] = "testUrl";

            apiConfiguration = builders.Build<IAPIConfiguration>().Create();
        }

        [TestMethod]
        public void TestMethod1()
        {
            Assert.AreSame(configuration["API:Key"], apiConfiguration.API_KEY);
        }
    }

Test brakes in the constructor of the test on line

apiConfiguration = builders.Build<IAPIConfiguration>().Create();

Upvotes: 0

Views: 1029

Answers (1)

Nkosi
Nkosi

Reputation: 247413

All that does is create a mock. It does nothing in configuring the mock's behavior.

[TestMethod]
public void TestMethod1() {
    //Arrange
    //Freeze-Build-Create sequence
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    var apiKey = fixture.Create<string>();
    var url = "http://example.com";
    var configuration = fixture.Freeze<IConfiguration>();

    //Configure the expected behavior of the mock
    var keys = new Dictionary<string, string> {
        { "API:Key" , apiKey },
        { "API:URL", url }
    };
    var mock = Mock.Get(configuration);
    mock.Setup(_ => _[It.IsAny<string>()]).Returns((string key) => keys[key]);

    IAPIConfiguration apiConfiguration = fixture.Build<APIConfiguration>().Create();

    //Act
    var actual = apiConfiguration.API_KEY;

    //Assert
    Assert.AreEqual(apiKey, actual);
    Assert.AreEqual(new Uri(url), apiConfiguration.URL);
}

The above extracts the mock from the fixture and configures the expected behavior for the test case.

The test also exposed issues with the subject under test which had to be refactored to

public class APIConfiguration : IAPIConfiguration {
    public APIConfiguration(IConfiguration configuration) {
        this.API_KEY = configuration["API:Key"] ?? throw new NoNullAllowedException("API key wasn't found.");
        this._url = configuration["API:URL"] ?? throw new NoNullAllowedException("API url wasn't found.");
    }

    public string API_KEY { get; }

    private string _url;

    public Uri URL {
        get {
            return new Uri(this._url);
        }
    }
}

to fix issues related to its original design.

Upvotes: 1

Related Questions