5

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?

Community
  • 1
  • 1
Will Marcouiller
  • 23,773
  • 22
  • 96
  • 162

1 Answers1

6

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; }
   ...
Community
  • 1
  • 1
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • Yes, I know what setting to query for. After having done the changes suggested, because I was using `It.IsAny()`, it was always returning null, hence my dialect was always null, then causing the test to fail on `dialect.Should()...`. You definitely helped out, I love SO! =) – Will Marcouiller May 30 '14 at 02:08