I want to mock a Logger using NSubstitute. But instead of using Substitute.For, I want to use Substitute.ForPartsOf to:
- both call the real implementation (to continue logging to the console)
- and check the result using .Received, i.e. check whether something was logged.
The issue is, my Logger does have a factory method (Create), which returns a new instance of the logger.
Now, can I use this with NSubstitute to substitute it?
Here a full MWE.
I tried:
- For and ForPartsOf, which always fail due to the private constructor
- I switched to CallBase, but this also does not work, because I need to have a concrete instance
- in the last try, ForCasting_UsingCallBase, I even tried casting the successfully created For substitute to the concrete type, to have a concrete type for the base method, which it complained, was missing; but in this case the casting fails
public interface ILogSettings { } public interface ISomeLogger { public void LogSomething(string anything); } public class SomeLogger : ISomeLogger { private SomeLogger() => throw new NotSupportedException("let's assume this constructor is not good to call"); private SomeLogger(ILogSettings logSettings) => CreateLogger(logSettings); private static void CreateLogger(ILogSettings logSettings) { Console.WriteLine(); Console.WriteLine("Logger created."); // does something with logSettings, irrelevant here } /// /// Factory method here for creating logger! /// [PublicAPI] public static SomeLogger Create(ILogSettings? logSettings) => logSettings != null ? new SomeLogger(logSettings) : throw new ArgumentNullException(nameof(logSettings)); public void LogSomething(string anything) => Console.WriteLine(anything); } [TestFixture] public class SubstituteReproducerTest { [Test] public void For_WorksButShowsNoLogging() { var logger = Substitute.For(); // This here is mocked, so it cannot/does not actually call the "real" method. // Thus, no logging is shown in the test. logger.LogSomething("sth"); // I can, however, check the received call, of course. logger.ReceivedWithAnyArgs(1).LogSomething(default!); } [Test] public void ForPartsOf_OnInterface() { var logger = Substitute.ForPartsOf(); // This DOES NOT work! // Can only substitute for parts of classes, not interfaces or delegates. Try `Substitute.For instead. } [Test] public void ForPartsOf_WithPrivateConstructor() { var logger = Substitute.ForPartsOf(); // This DOES NOT work! // Could not find a parameterless constructor. (Parameter 'constructorArguments') } [Test] public void For_WithPrivateConstructor() { var logger = Substitute.For(); // This DOES NOT work! // Could not find a parameterless constructor. (Parameter 'constructorArguments') } [Test] public void For_WithFactoryMethod() { var settings = Substitute.For(); // This DOES NOT work! // The constructor args are supposed to be there. var logger = Substitute.For(() => SomeLogger.Create(settings)); // Could not find a parameterless constructor. (Parameter 'constructorArguments') } [Test] public void ForPartsOf_WithFactoryMethod() { var settings = Substitute.For(); // This DOES NOT work! // The constructor args are supposed to be there. var logger = Substitute.ForPartsOf(() => SomeLogger.Create(settings)); // Could not find a parameterless constructor. (Parameter 'constructorArguments') } [Test] public void For_UsingCallBase() { var logger = Substitute.For(); logger.WhenForAnyArgs(x => x.LogSomething(default!)).CallBase(); // This DOES NOT work! // NSubstitute.Exceptions.CouldNotConfigureCallBaseException : Cannot configure the base method call as base method implementation is missing. You can call base method only if you create a class substitute and the method is not abstract. } [Test] public void For_UsingCallBaseWithConcreteClass() { var logger = Substitute.For(); // This DOES NOT work! // Could not find a parameterless constructor. (Parameter 'constructorArguments') // And ForPartsOf would fail to, as given and tried in ForPartsOf_WithPrivateConstructor. } [Test] public void ForCasting_UsingCallBase() { var logger = Substitute.For(); var loggerConcrete = logger as SomeLogger; // --> results in 'null' loggerConcrete.WhenForAnyArgs(x => x.LogSomething(default!)).CallBase(); // This DOES NOT work! // NSubstitute.Exceptions.CouldNotConfigureCallBaseException : Cannot configure the base method call as base method implementation is missing. You can call base method only if you create a class substitute and the method is not abstract. } } I know I can make methods virtual to be substitut'able, but this does not work here, as the Logger/class is from a library I use.
Источник: https://stackoverflow.com/questions/781 ... partsof-to