TDD: Why is there only one test per function?

2019-03-26 00:33发布

I'm having a hard time understanding why there is only one test per function in most professional TDD code that I have seen. When I approached TDD initially I tended to group 4-5 tests per function if they were related but I see that doesn't seem to be the standard. I know that it is more descriptive to have just one test per function because you can more easily narrow down what the problem is, but I find myself struggling to come up with function names to differentiate the different tests since many are so similar.

So my question is: Is it truly a bad practice to put multiple tests in one function and if so why? Is there a consensus out there? Thanks

Edit: Wow tons of great answers. I'm convinced. You need to really separate them all out. I went through some recent tests I had written and separated them all and lo and behold it was way more easier to read and helped my understand MUCH better what I was testing. Also by giving the tests their own long verbose names it gave me ideas like "Oh wait I didn't test this other thing", so all around I think it's the way to go.

Great Answers. Gonna be hard to pick a winner

标签: oop tdd
8条回答
乱世女痞
2楼-- · 2019-03-26 01:12

Consider this straw man (in C#)

void FooTest()
{
    C c = new C();
    c.Foo();
    Assert(c.X == 7);
    Assert(c.Y == -7);
}

While "one assertion per test function" is good TDD advice, it's incomplete. Applying it alone would give:

void FooTestX()
{
    C c = new C();
    c.Foo();
    Assert(c.X == 7);
}

void FooTestY()
{
    C c = new C();
    c.Foo();
    Assert(c.X == 7);
}

It's missing two things: Once-and-only-once (aka DRY), and "one test class per scenario". The latter is the less-known one: instead of one test class / test fixture that holds all test methods, have nested classes for non-trivial scenarios. Like this:

class CTests
{
    class FooTests
    {
        readonly C c;

        void Setup()
        {
            c = new C();
            c.Foo();
        }

        void XTest()
        {
            Assert(c.X == 7);
        }

        void YTest()
        {
            Assert(c.Y == -7);
        }
    }
}

Now you don't have duplication, and each test method asserts exactly one thing about the code under test.

If it wasn't so verbose, I would consider writing all my tests this way, such that test methods are always trivial single-line methods with only an assertion. However, it seems too clumsy when a test doesn't share "setup" code with another test.

(I have avoided details that are specific to unit test technology, e.g. NUnit or MSTest. You will have to adjust to fit whatever you are using, but the principles are sound.)

查看更多
时光不老,我们不散
3楼-- · 2019-03-26 01:13

It seems like a single failure in a multi-test function would have to result in a failure for all, right? Generally test framework tests just pass fail, which with a multi-test method would mean you'd have to manually figure out which of the multiple tests would be failing, since if you're running a huge list of tests the first executed failure would result in an overall failure for the function and further tests wouldn't get to fail.

Granularity in tests is good. If you're going to write 5 tests, having them each in their own function seems no more difficult than having them all in the same location, apart from the minor overhead of creating new boilerplate function each time. With the right IDE, even that may be simpler than copying & pasting.

查看更多
登录 后发表回答