Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invalid setup on non-virtual member - on an interface?

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.

  • Why does the property I want to mock need to be virtual?
  • Moq is throwing Invalid setup on a non-overridable member, when class is in Service Project
  • Invalid setup on a non-virtual (overridable in VB) member
  • Moq: Invalid setup on a non-overridable member: x => x.GetByTitle(“asdf”)

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?

  • Unit Testable Configuration Manager

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?

like image 597
Will Marcouiller Avatar asked May 30 '14 01:05

Will Marcouiller


1 Answers

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; }
   ...
like image 84
Alexei Levenkov Avatar answered Oct 13 '22 17:10

Alexei Levenkov