Do I need to mock .net libraries?
Just getting into unit testing and mocking and just wondering if its necessary to mock .net libraries like File or FileStream?
11 comments · 71% upvoted · other discussions
Just getting into unit testing and mocking and just wondering if its necessary to mock .net libraries like File or FileStream?
11 comments · 71% upvoted · other discussions
Only if you want to waste a lot of your time ;)
I don't understand the sentiment here. Many classes can't be properly unit tested without dependency inversion, and if you are applying the dependency inversion pattern to enable testing, your options are to write concrete implementations that exhibit the behavior you want to test, or use mocks. Mocks are objectively faster and more maintainable than writing concrete implementations.
Can elaborate on 'waste a lot of your time'? Or are you suggesting that unit testing is a waste of time?
I think it depends on the scenario, and the API that you're trying to test.
If all I needed to do was verify a file exists, or read from it (ie File.ReadAllBytes), then I'd leave it out and just create the files as required (especially if the path was an input parameter).
The problem is that, if there is no defined interface to mock, then you need to create one, write the wrapper for it. But then you still need to unit test your implementation that actually uses the FileStream provider.
Sometimes purity can be set aside for usability.
You don't need to test the wrapper class as long as you make it a simple passthrough. You use the interface to make the consuming code testable, but the wrapper can just be marked with [IgnoreDuringTestCoverage] since you can rely on the assumption that the framework code is tested/works reliably.
This is a fairly common pattern for "older" stuff in the .NET BCL that doesn't have interfaces. Here's an example over TcpClient; there's a couple of others on top of NetworkStream and TcpListener in there too.
Essentially you make a new interface that matches the interface-less class you want to test, expose only the methods and properties you need, and then make a class that implements the new interface while "passing through" the method calls. This would be considered the adapter or facade design pattern.
mocking? no thank you! https://youtu.be/9BaDj8SSsdM
[deleted]
sometimes you want to test how your SUT (system under test) behaves given the output of a 3rd party.
so for /u/NetFutility's benefit, here's a trivial example: e.g., my SUT greets you depending on the time of day. My SUT should return 1 of 3 greetings: "Good morning", "Good afternoon", or "Good evening"
My SUT could have easily determined the greeting like this:
if (DateTime.Now.Hour < 12)
{
return "Good morning";
}
But that's not easily testable unless you run your tests at different times of the day. So we refactor the SUT to accept a provider that returns the current datetime.
public class Greeter
{
private readonly IDateTimeProvider _dateTimeProvider = null;
public Greeter(IDateTimeProvider dateTimeProvider)
{
_dateTimeProvider = dateTimeProvider;
}
public string Greet()
{
var hour = _dateTimeProvider.Now.Hour;
if (hour < 12)
return "Good morning";
if (hour < 18)
return "Good afternoon";
return "Good evening";
}
}
Here's the interface and default implementation that uses DateTime.Now:
public interface IDateTimeProvider
{
public DateTime Now { get; }
}
public class DefaultDateTimeProvider : IDateTimeProvider
{
public DateTime Now => DateTime.Now;
}
Then, you just need 3 tests (using xUnit and Moq) for 100% code coverage:
[Fact]
public void GreeterTest_Morning()
{
Mock<IDateTimeProvider> mockDtp = new Mock<IDateTimeProvider>();
mockDtp.SetupGet(it => it.Now).Returns(new DateTime(1, 1, 1, 8, 0, 0)); //8:00 a.m.
string expectedGreeting = "Good morning";
Greeter greeter = new Greeter(mockDtp.Object);
string actualGreeting = greeter.Greet();
Assert.Equal(expectedGreeting, actualGreeting);
}
[Fact]
public void GreeterTest_Afternoon()
{
Mock<IDateTimeProvider> mockDtp = new Mock<IDateTimeProvider>();
mockDtp.SetupGet(it => it.Now).Returns(new DateTime(1, 1, 1, 14, 0, 0)); //2:00 p.m.
string expectedGreeting = "Good afternoon";
Greeter greeter = new Greeter(mockDtp.Object);
string actualGreeting = greeter.Greet();
Assert.Equal(expectedGreeting, actualGreeting);
}
[Fact]
public void GreeterTest_Evening()
{
Mock<IDateTimeProvider> mockDtp = new Mock<IDateTimeProvider>();
mockDtp.SetupGet(it => it.Now).Returns(new DateTime(1, 1, 1, 22, 0, 0)); //10:00 p.m.
string expectedGreeting = "Good evening";
Greeter greeter = new Greeter(mockDtp.Object);
string actualGreeting = greeter.Greet();
Assert.Equal(expectedGreeting, actualGreeting);
}
You hide access to the File class, which has a lot of static methods that directly interact with files behind a facade that implements an interface. Same for DateTime.
If you want a painful testing experience, try writing meaningful tests against a bit of code that uses DateTime.Now , triggering an event if it's between 9pm and 10pm.
So you create a helper class which implements an interface and define it as a singleton. This way you can mock it with something that returns 9.30pm when it's 8.30 in the morning.
The key with mocking (imo, and there are a few schools of thought here) is you only mock code at the boundary of the system. DateTime.Now is a boundary operation, it asks your computer what time it thinks it is. File.* will create/read/delete actual files, which is not something you want unit tests to do.
An aside to that is you may want to test code that uses File to delete a file, to see how it handles exceptions being raised. Here especially it is much easier to use a facade and have a mock throw an exception rather than try to arrange for an actual file to be in a state where it can't be deleted.
I usually have an IFileHelper interface which exposes methods that either mirror the methods on File and Directory, or wrap them up into common operations. This is then registered as a singleton and injected into things that need file access.
WestDiscGolf 3 points · Dec 2019
Completely agree about the static nature. Static items are painful in unit tests. Which is why, as you suggest, I have an interface for time values which can be mocked in unit tests. More info https://adamstorr.azurewebsites.net/blog/mocking-the-clock