The classical solution if you have a concrete implementation of a dependency in a class you want to test is: add a layer of indirection where you have full control.
So it comes to adding more and more of indirection layers (interfaces that can be stubs/mocks in unit tests).
But: somewhere in my tests I must have the "real" implementation of my dependency. So what about this? Test? Don't test?
Take this example:
I had some dependencies on paths that I need in my application. So I extracted an interface, IPathProvider
(that I can fake in my tests). Here is the implementation:
public interface IPathProvider
{
string AppInstallationFolder { get; }
string SystemCommonApplicationDataFolder { get; }
}
The concrete implementation PathProvider
looks like this:
public class PathProvider : IPathProvider
{
private string _appInstallationFolder;
private string _systemCommonApplicationDataFolder;
public string AppInstallationFolder
{
get
{
if (_appInstallationFolder == null)
{
try
{
_appInstallationFolder =
Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
}
catch (Exception ex)
{
throw new MyException("Error reading Application-Installation-Path.", ex);
}
}
return _appInstallationFolder;
}
}
public string SystemCommonApplicationDataFolder
{
get
{
if (_systemCommonApplicationDataFolder == null)
{
try
{
_systemCommonApplicationDataFolder =
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
}
catch (Exception ex)
{
throw new MyException("Error reading System Common Application Data Path.", ex);
}
}
return _systemCommonApplicationDataFolder;
}
}
}
Do you test such code and if, how?
That PathProvider
class seems a lot like a database repository class - it integrates with an external system (e.g., filesystem, database, etc).
These classes live at the boundaries of your system - other classes depend on them, and they depend solely on an external system. They connect your system to the external world.
As such, they're subject to integration testing - not unit testing.
I usually just tag my tests (e.g. with xUnit's Trait
attribute) to keep them separate from regular tests, and so I can disable them when running in isolation (e.g. without a database). An alternative would be to separate them into a whole new project in the same solution, but I personally think that's overkill.
[Fact, Trait("Category", "Integration")]
public void ReturnsCorrectAppInstallationFolder()
{
// Arrange
var assemblyFilename = Path.GetFilename(typeof(SomeType).Assembly.Location);
var provider = new PathProvider();
// Act
var location = provider.AppInstallationFolder;
// Assert
Assert.NotEmpty(Directory.GetFiles(location, assemblyFilename))
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With