0

My C# classes are generally structured this way:

public class MyDummyService
{
    private readonly MyConfigClass _config;

    public MyDummyService(IOptions<MyConfigClass> options)
    {
        _config = options.Value;
    }

    public string DoSomethingWithTheNumber()
    {
        if (_config.SomeValue % 2 == 0)
            return "foo";
        return "bar";
    }

}
public class MyConfigClass
{
    public int SomeValue{get; set;}
    public string SomeName {get; set;}
}

Clearly, IOptions<MyConfigClass> is used to map configurations from the appSettings.json file.

Then, I can test such classes like this:

class MyDummyServiceTests
{
    protected AutoFixture.Fixture _fixture;
    protected MyDummyService _sut;
    protected MyConfigClass _simpleConfig;
    protected Mock<IOptions<MyConfigClass>> _mockConfig;

    public MyDummyServiceTests()
    {
        _fixture = new AutoFixture.Fixture();
        _mockConfig = new Mock<IOptions<MyConfigClass>>();

        _simpleConfig = _fixture.Build<MyConfigClass>()
          .With(i => i.SomeValue, 4)
          .With(i => i.SomeName, "Pippo")
          .Create();
    }

    [SetUp]
    public void Setup()
    {
        _mockConfig.SetupGet(m => m.Value).Returns(_simpleConfig);

        _sut = new MyDummyService(_mockConfig.Object);
    }

    [TearDown]
    public void TearDown()
    {
        _mockConfig.Reset();
    }

    public class DoSomethingWithTheNumber : MyDummyServiceTests
    {
        [Test]
        public void Should_ReturnFoo_WhenNumberIsEven()
        {
            _simpleConfig = _fixture.Build<MyConfigClass>()
              .With(_ => _.SomeValue, 10)
              .Create();

            var s = _sut.DoSomethingWithTheNumber();

            Assert.AreEqual("foo", s);
        }

        [Test]
        public void Should_ReturnBar_WhenNumberIsOdd()
        {
        
            _simpleConfig = _fixture.Build<MyConfigClass>()
              .With(_ => _.SomeValue, 69)
              .Create();

            var s = _sut.DoSomethingWithTheNumber();

            Assert.AreEqual("bar", s);
        }
    }
}

Yes, I know, I should use [TestCase] - you got the point

Now, when I set up tests for such classes, and I want to test a specific behavior that depends on the MyConfigClass.SomeValue value, I don't want to re-initialize everything. I can clearly just add _mockConfig.SetupGet(m => m.Value).Returns(_simpleConfig); or _sut = new MyDummyService( Options.Create(_simpleConfig)); but I want to keep my tests as small as possible, and only set the configuration value I need.

If I run the tests as such, DoSomethingWithTheNumber() does not see MyConfigClass.SomeValue with the correct value, because it is initialized and assigned to _config during StartUp phase, so before I set the correct value in my tests.

How can I improve my approach and set only the values necessary to have the specific test pass?

Note: I do not expect my configurations to change at runtime. Therefore, I can use IOptions<T>, IOptionsMonitor<T>, or IOptionsSnapshot<T> - even if they are different meanings, as explained here.

Davide Bellone
  • 89
  • 2
  • 10
  • Why not just have a method that returns an appropriately initalized `MyConfigClass` instance, that you then tweak as necessary in specific tests? Moq doesn't even seem relevant here (you're free to use it, of course, but instantiating and supply options doesn't need mocks, and whether you feed them to mocks or concrete classes is irrelevant). It seems you're going overboard with fixtures and mocks here for the one case where creating concrete instances is not just possible but by far the easiest option. – Jeroen Mostert Feb 01 '23 at 16:23
  • Just create the instance in every test method and use it in the mock setup in-place - there is no need to share it. – Guru Stron Feb 01 '23 at 16:25
  • I'm not in favor of sharing data between tests, I would much rather arrange everything within the test. Moq does allow to chain multiple `Returns` that will return each value in multiple calls, but how do you know it will be called in the right order? – Dave Van den Eynde Feb 02 '23 at 16:03

0 Answers0