For the normal IOptions interface, you can manually build an instance e.g. this SO question.
Is there any equivalent way to make an IOptionsMonitor instance without using DI?
For the normal IOptions interface, you can manually build an instance e.g. this SO question.
Is there any equivalent way to make an IOptionsMonitor instance without using DI?
You can do something like below and and then use that for testing:
public class TestOptionsMonitor : IOptionsMonitor<MyOptions>
{
public TestOptionsMonitor(MyOptions currentValue)
{
CurrentValue = currentValue;
}
public MyOptions Get(string name)
{
return CurrentValue;
}
public IDisposable OnChange(Action<MyOptions, string> listener)
{
throw new NotImplementedException();
}
public MyOptions CurrentValue { get; }
}
nice answer from Hananiel
Here the generic version of it:
public class TestOptionsMonitor<T> : IOptionsMonitor<T>
where T : class, new()
{
public TestOptionsMonitor(T currentValue)
{
CurrentValue = currentValue;
}
public T Get(string name)
{
return CurrentValue;
}
public IDisposable OnChange(Action<T, string> listener)
{
throw new NotImplementedException();
}
public T CurrentValue { get; }
}
and simply create an instance with your object!
Disclaimer: If you're avoiding external libraries the previous answers will do the trick! Consider this one if you'd like to leverage an existing library to Mock stuff for you!
If you're using the NSubstitute
library for Mocking you can mock out an IOptionsMonitor
by doing the following:
// Arrange
var mockedOptions = Substitute.For<IOptionsMonitor<BasicCredentialSchemaOptions>>();
mockedOptions.CurrentValue.Returns(new BasicCredentialSchemaOptions(/*snip*/));
// Act
var result = mockOptions.CurrentValue; // Does not throw and fires off NSubstitue's Returns
// Assert
Assert.NotNull(result);
Because iOptionsMonitor is not the subject of your test, you can replace it by a mock that simulate the behaviour of the real object.
Using moq:
var iOptionsMonitor =
Mock
.Of<IOptionsMonitor<MyOptions>>(
x =>
x.CurrentValue.YourOption1 == "some value" &&
x.CurrentValue.YourOption2 == 21 &&
x.CurrentValue.YourOption3 == true
);
Remember to install Moq package:
dotnet add package Moq
Be free to setup your mock for your custom needs. For example, to set behaviour for other interface methods (Get, OnChange)
Might be a bit late, but here is a full implementation of a test options monitor you can use.
public class OptionsMonitorTestImplementation<TOptions> : IOptionsMonitor<TOptions>
{
public OptionsMonitorTestImplementation(TOptions initialValue) => CurrentValue = initialValue;
readonly List<Action<TOptions, string>> _listeners = new();
public TOptions CurrentValue { get; private set; }
public TOptions Get(string name) => throw new NotImplementedException();
public IDisposable OnChange(Action<TOptions, string> listener)
{
_listeners.Add(listener);
return new ActionDisposable(() => _listeners.Remove(listener));
}
public void UpdateOptions(TOptions options)
{
CurrentValue = options;
_listeners.ForEach(listener => listener(options, string.Empty));
}
public sealed class ActionDisposable : IDisposable
{
readonly Action _action;
public ActionDisposable(Action action) => _action = action;
public void Dispose() => _action();
}
}
None of the samples above worked for me because the code calls OnChange and it throws NotImplementedException
.
Here is the code I ended up using.
public class TestOptionsMonitor<TOptions> : IOptionsMonitor<TOptions>
{
private Action<TOptions, string>? _listener;
public TestOptionsMonitor(TOptions currentValue) => CurrentValue = currentValue;
public TOptions CurrentValue { get; private set; }
public TOptions Get(string name) => CurrentValue;
public void Set(TOptions value)
{
CurrentValue = value;
_listener?.Invoke(value, string.Empty);
}
public IDisposable OnChange(Action<TOptions, string> listener)
{
_listener = listener;
return Mock.Of<IDisposable>();
}
}
Which is based on this blog. https://benfoster.io/blog/20200610-testing-ioptionsmonitor/
I was going crazy trying to mock the object, this was much easier. Just new up a TestOptionsMonitor
var options = new TestOptionsMonitor(new MyOptions{ Option1 = "Test" });
using the above TestOptionsMonitor and you'll be good to go