Reputation: 793
Technology Stack: .NET 4, C#, NUnit
I am attempting to apply test driven development to a new project that performs image processing. I have a base class that contains shared file I/O methods and subclasses that perform various specific processing algorithms. As I understand it, unit tests do not touch the file system or other objects, and mock behavior where that occurs. My base class only contains simple accessors and straightforward file system I/O calls.
public class BaseFile
{
public String Path { get; set; }
public BaseFile()
{
Path = String.Empty;
}
public BaseFile(String path)
{
if (!File.Exists(path))
{
throw new FileNotFoundException("File not found.", path);
}
Path = path;
}
}
Is there any value in testing these methods? If so, how could I abstract away the calls to the file system?
My other question is how to test the subclass which is specific to a type of image file (~200 MB). I have searched the site and found similar questions, but none dealing with the file sizes I am working with on this project. Is it plausible for a class to have integration tests (using a "golden file"), but no unit tests? How could I strictly follow TDD methods and first write a failing test in this case?
Upvotes: 5
Views: 899
Reputation: 13335
In answer to your first question, yes there is value in testing these methods. I have published a library that facilitates doing exactly that without actually hitting the file system: https://bitbucket.org/mylesmcdonnell/mpm.io/wiki/Home
In (not an) answer to your second question I would need to see some code, but I suspect you may need to take a similar approach to the above lib. namely; define interface, define proxy to concrete, define factory to return proxy or mock.
Upvotes: 4
Reputation: 2387
Mocking simple file system calls using interfaces seems like an overkill. Same goes for things like mocking current time using ITimeService. I tend to use Func or Action because it is so much simpler:
public static Func<string, bool> FileExists = System.IO.File.Exists;
public static Func<DateTime> GetCurrentTime = () => DateTime.Now;
Since those are public I can mock the easily in the unit tests. Code stays simple, no need to inject various interfaces for simple things. For streams I usually use MemoryStream in unit tets.
Upvotes: 1
Reputation: 14409
There is value in testing these methods
While it may seem like extra work for trivial gain right now, adding tests like BaseFile should throw FileNotFoundException when the file does not exist accomplishes at least two goals:
Define a list of expected behaviors
Newcomers to the project can review the test names to determine how your classes are intended to operate. They will know what to expect in each situation -- an exception, a null, a default result, etc.
It also forces you to think through and define in plain English how you want things to operate, as opposed to just throwing in conditions and exceptions here and there. This should result in a very consistent philosophy applied across your project.
Grow a suite of automated regression tests
Consider that someone sees some code throwing an exception in a particular condition, but they think it is wiser to do something else (eat the error, but add a new IsValid property so that consumers can know whether the construction / initialization was successful). If they make such a change, the test will very rapidly draw attention to the change. There was a conscious and intentional decision behind the way things were, and people might have grown to rely on the existing behavior -- this change needs further discussion before it can be accepted.
As for the second part of your question, I think Josh and Myles have both provided sound advice already.
Upvotes: 1
Reputation: 171178
Integration tests have value on their own. If you mock the file system, like explained in Joshs answer, you really don't know for sure that your code will actually run in production. The file system has a lot of hidden contracts that are non-trivial to mock. If your mock/fake shows slightly different behavior your code might start to rely on it without you knowing.
Only an integration test can tell certain things for sure. (Integration tests have disadvantages as well!).
Upvotes: 0
Reputation: 44906
How could I strictly follow TDD methods and first write a failing test in this case?
Easy! You mock the file system :)
This may sound like a lot of work, but typically you only need to implement a few methods, and expand as necessary. In the case above... you only need one.
public interface IFileStore
{
Boolean FileExists(String path);
}
The idea is to delegate your file work behind the interface, and create a concrete implementation to do the heavy lifting. Basically an Adapter pattern.
I don't even object to "poor man's DI" for this kind of thing as you can implement a container later if your application calls for it. In this case... you are probably always going to be using the real file system.
public class BaseFile
{
private IFileStore _fileStore
public IFileStore FileStore
{
get
{
return _fileStore ?? (_fileStore = new ConcreteFileStore());
}
set
{
_fileStore = value;
}
}
//SNIP...
}
Now you have a testable implementation, and you won't have to rely on any "Golden" files.
Upvotes: 3