7
\$\begingroup\$

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<string>("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<T>(this T source, params string[] targets) where T : ObjectQuery<string>
    {
        source.ToArray().ShouldContain(targets);
    }
}
\$\endgroup\$

2 Answers 2

10
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ +1 for mentioning NSubstitute. I didn't know about it, but it reaaly seems to have a clean, suggestive syntax so I think I'll give it a try. \$\endgroup\$ Commented Jan 8, 2012 at 21:32
4
\$\begingroup\$

You should not use MyEntities in your unit tests. Effectively you are doing integration testing here since each test will use the database and is dependent on the data stored there.

Instead mock out MyEntities or make MyEntities derive from an interface IMyEntities that is the contract for your persistence layer.

Now you can create a separate MyTestEntities the implements IMyEntities so you can verify your expectations independent of the data store. Then after you execute the class/method under test verify against the mock.

\$\endgroup\$

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.