Reputation: 1175
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
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