6

This is my solution's structure:

Storage.csproj
   > FileDownloader.cs
   > GitHubProvider.cs (implements IStorageProvider)
   > IStorageProvider.cs

Storage.Test.csproj
   > FileDownloaderFixture.cs

The idea is that I can use the class FileDownloader, inject it with a IStorageProvider, call myFileDownloader.Download(url), and it will download the file to the filesystem.

Now, the question is: how do I unit test this? I wrote the following test:

[TestClass]
    public class FileDownloaderFixture
    {
        [TestMethod]
        public void TestDownload()
        {
            //Arrange
            var storageProviderMock = new Mock<IStorageProvider>(MockBehavior.Strict);
            storageProviderMock.Setup(s => s.Download("http://host.com/file.txt")).Returns(Status.Success);
            var myFileDownloader = new FileDownloader(storageProviderMock.Object);

            //Act
            myFileDownloader.DownloadFile("http://host.com/file.txt");

            //Assert
            storageProviderMock.Verify(s => s.Download("http://host.com/file.txt"));
        }
    }

It looks nice and clean, but... it's useless. It doesn't prove the fact that the code in GitHubProvider.Download(url) works. I don't even use methods from GitHubProvider. So what's the point?

The only other idea I have is to set up a test GitHub account & repository, and work with that. But this solution won't work very well if, for instance, I had to pay for each access to the repository. What then?

3 Answers 3

16

The file downloader has two external dependencies: a connection to Git is where data comes in. A connection to the file system is where data goes out.

To be a unit test, your test should abstract from both these collaborators - you don't want to test the Git network protocol, and you don't want to test file system code either. That leaves the behaviour of your class simple and easily testable: it issues commands to the Git hub to obtain data and issues commands to the storage system to write them. That makes for a pretty primitive test, and this is good. A class should do one thing, and do it well. ideally, it should be so simple that it is obviously correct (and have a unit test as well, for cases when the obviousness is misleading).

You're right that this doesn't prove that your system successfully downloads stuff. That is the task of an integration, acceptance, or load test, and you need those as well, but as far as unit testing goes, there is nothing wrong with tests that seem almost trivial. Your project will grow in complexity soon enough, and without a safety net composed of almost trivial checks, you can't manage that growth responsibly.

5

Your test is not useless. A unit test FileDownloaderFixture tests the FileDownloader not more, not less. Don't expect a unit test to test more than one unit.

To test GitHubProvider.Download, you need a second, different test. Since there is an external resource involved which is almost completly out of control of your test environment, it probably does not make sense to implement this test as part of your "regular" unit test suite. So the better strategy for such a component is often to provide a manual or "semiautomatic" test, to be run manually whenever you change something in GitHubProvider. This test may involve the use of a GitHub account & repository. You can use your unit testing tool for this test, but if the manual test needs some additional manual steps, you can also implement something completely different (like a small test program with a simple UI or command line interface, whatever causes the smallest effort) That's a feasible approach as long as the internals of GitHubProvider is not too complex.

2
  • 3
    Implementing integration tests in the unit test framework can totally make sense, probably better done in a specific integration test project though.
    – Wilbert
    Sep 19, 2014 at 8:40
  • @Wilbert: see my edit
    – Doc Brown
    Sep 19, 2014 at 11:13
0

The only other idea I have is to set up a test GitHub account & repository, and work with that. But this solution won't work very well if, for instance, I had to pay for each access to the repository. What then?

Testing on a real account is called integration test and not unit test. You don't need a github repo to test a file download through HTTP. Every HTTP server can be enough. If you have some github specific problem, then you can create the repo and test on it. After that you can assume that the interface of the github won't change for a long time, so you can skip this integration test by running your tests, because it is slow and it does not (should not) depend on components you probably want to change. You'll probably run integration tests by version changes of the product integrated into your application. For example by a new database version, etc...

3
  • 1
    His integration test should most likely test that the file he expects exists at the exact location his program tries to download it.
    – Wilbert
    Sep 19, 2014 at 11:33
  • The terms are debatable. As long as "GitHubProvider" is tested as a single component, without other components like "FileDownloader", IMHO the term "integration test" does not fit well, too.
    – Doc Brown
    Sep 19, 2014 at 11:40
  • @DocBrown Yepp, I might overly simplified them... Btw. it depends on the perspective you use. You can write integration tests to a driver represented by a single class in your programming language. In the current case by testing the GithubProvider you cannot mock out the FileDownloader, because it is required to connect to github, but you can mock out for example a parser, because it is not a required component, just an optional. It is hard to create a solid theory from this, it really depends on what you want to test... :S
    – inf3rno
    Sep 19, 2014 at 12:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.