HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpModerate

What's the right granularity for test-driven development?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thewhatdevelopmentfortestgranularitydrivenright

Problem

I'm not very experienced in test-driven development (TDD), but I'm trying to get into the groove. Here I am making a console app that creates records in a database. I bootstrapped by creating a console app and then let MSTest make a unit test for Main(string [] args).

I know I'm doing at least something right, because the tests didn't pass until I did the development -- they "drove" me to "develop".

But I feel like maybe I'm still missing the point, as I didn't really test my code - I tested the database the code acted on. How would an experienced TDDer approach this?

[TestClass]
public class ProgramTest
{
    private MyEntities _model;

    [TestInitialize()]
    public void MyTestInitialize()
    {
        _model = new MyEntities();
    }

    [TestCleanup()]
    public void MyTestCleanup()
    {
        _model.Dispose();
    }

    [TestMethod()]
    public void MainTest_01_Create_PayTerms()
    {
        string[] args = new[]  { "/Operation", "Insert", "/RecordType", "PayTerms" };
        DirectAgentsService.Program.Main(args);

        _model.PayTerms.SelectValue("it.Name").ShouldContain("Biweekly", "Retainer", "Net 7", "Net 15", "Net 30");
    }
}
static public class Extensions
{
    static public void ShouldContain(this string[] source, params string[] targets)
    {
        foreach (var target in targets)
        {
            Assert.IsTrue(targets.Any(c => c == target), "{0} is missing from collection", target);
        }
    }

    static public void ShouldContain(this T source, params string[] targets) where T : ObjectQuery
    {
        source.ToArray().ShouldContain(targets);
    }
}

Solution

Assuming you are speaking of Unit test driven development there are two things you need to get your head around first (or should I say, things I had to get my head around).
Both of them are actually about writing unit testable code (as compared to actually writing test first, etc)

  • Dependency injection: Avoid constucting objects inside other objects; instead, pass already constructed ones in through the constructor, or properties if appropriate. Look out for the new keyword and avoid it (exceptions apply, such as for classes that are pure data). Of course at some point objects need to be constructed. This should be done at high level classes (say somewhere near your console app's main()) at such a point that is simple enough to need no (unit) testing (since you won't be able to unit test it).


Dependency injection will allow you to inject mock or fake versions of the other classes you are dependent on.

  • Mocking/Stubbing/Faking/Test Doubles: Your unit tests should test only one thing, not all the things that one this is dependent on (they have their own tests). This allows you to localise (as in know the location of) any errors that show up. You can write your own stubs, but there are great libraries out there, and using them introduces a bit more consistency about how your tests work/look. NSubstitute is a good choice becuase it uses extension methods to be really easy to read.



As to the granularity for Unit Tests, it is as granular as you can go. That way you can localise errors (also the less granular/more integration test like, the harder is it to write tests, they become long). There is some argument about whether private methods should be tested or not. I would say it comes down to whether the private method is just a part of some public method (no) or it has its own well defined purpose (yes).

However, Unit tests driven development isn't the only kind: BDD (behavour driven development) focuses on Functional tests.

Context

StackExchange Code Review Q#5065, answer score: 10

Revisions (0)

No revisions yet.