While unit testing using Moq, I get the following error:
Message: System.NotSupportedException :
Invalid setup on non-virtual (overridable in VB) member:
cm => cm.AppSettings[It.IsAny<string>()]
Per these findings, I understand that it's preferable to use abstract classes or interfaces with Moq.
In short, I've done my homework. =)
But what if I'm actually using an interface?
ConfigurationServiceTests
[TestFixture]
public class ConfigurationServiceTests {
[Test]
public void DialectShouldQueryConfigurationManagerAppSettings() {
// Given
configurationManagerMock
.Setup(cm => cm.AppSettings[It.IsAny<string>()])
.Returns(It.IsAny<string>());
// When
var dialect = configurationService.Dialect;
// Then
dialect.Should().BeOfType<string>();
configurationManagerMock.Verify(cm => cm.AppSettings[It.IsAny<string>()]);
}
[SetUp]
public void ConfigurationServiceSetUp() {
configurationManagerMock = new Mock<IConfigurationManager>();
configurationService =
new ConfigurationService(configurationManagerMock.Object);
}
private Mock<IConfigurationManager> configurationManagerMock;
private IConfigurationService configurationService;
}
IConfigurationManager
public interface IConfigurationManager {
NameValueCollection AppSettings { get; }
ConnectionStringSettingsCollection ConnectionStrings { get; }
}
IConfigurationService
public interface IConfigurationService {
string ConnectionDriver { get; }
string ConnectiongString { get; }
string Dialect { get; }
}
ConfigurationService
public class ConfigurationService : IConfigurationService {
public ConfigurationService(IConfigurationManager configurationManager) {
this.configurationManager = configurationManager;
}
public string ConnectionDriver {
get { return configurationManager.AppSettings["ConnectionDriver"]; }
}
public string ConnectionString {
get {
return configurationManager
.ConnectionStrings[ConnectionStringKey]
.ConnectionString;
}
}
public string Dialect {
get { return configurationManager.AppSettings[DialectKey]; }
}
private readonly IConfigurationManager configurationManager;
private const string ConnectionStringKey = "DefaultConnectionString";
private const string DialectKey = "Dialect";
}
Why have I created the IConfigurationManager
interface?
In addition to it, I want to bind it directly using Ninject, in my production code. So I need no concrete implementation of the interface, hence my big surprise with the above-mentioned exception.
kernel.Bind<IConfiguration>().To<ConfigurationManager>().InSingletonScope();
And doing so allows me to unit test my ConfigurationService
.
Any idea?
You are trying to mock indexer of NameValueCollection
instead of your property getter.
You need to do something like (note SetupGet instead of Setup
) :
.SetupGet(cm => cm.AppSettings).Returns(....)
If you want "mocked" collection that returns "anything for anything" you may need to override actual class (check if it is even possible to do).
If you know what settings you are looking for - returning populated collection from Mock may be an option:
configurationManagerMock
.SetupGet(cm => cm.AppSettings)
.Returns(new NameValueCollection { {"SettingName", "Value"}});
Another option would be to return some custom interface or IDictionary<string,string>
so you can mock indexing too:
interface IConfigurationManager
{
IDictionary<string,string> AppSettings { get; }
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With