开发者

When to use a unit testing framework (vs. just using asserts)?

Using a small (currently at 150 loc, probably less than 500 when finished) C project I'm working on, I'm teaching myself test driven development. Based on some stuff I've found on the web - especially these slides by Olve Maudal, I've just been using asserts in my unit tests.

Since I'm just开发者_JAVA百科 learning tdd, I have thus far avoided the overhead of also learning a unit testing framework such as cunit. At this point, my thinking is that the additional learning curve - even if shallow - of a framework is not worth the effort for such a small project. I'm wondering: Is this incorrect (i.e. I'd be better off learning the framework now)? At what point does learning a framework pay off?


The break-even for learning a unit test framework is not long to reach. Two main advantages one gets with a framework over using assert() function are

  • more detailed message for assertion failure: expected vs. actual value
  • execution of all test cases in lieu of stopping on first failed assertion

The two of those give hints as to where the error is located.


From my experience with learning .NET unit testing frameworks, I pretty much started with a small project which then gave me the advantage of being able apply what I'd learned to other projects later on, regardless of their size.

I guess you might be thinking what's the point, it's only a small bit of code, right now, but I think you would be doing well to get into a framework now rather then later. It's a bit like driving a car - I learned to drive when I was 17 years old, though I didn't get my own car until two years later. I could have just ignored taking lessons until I had got my car, but I passed and already had the skills to just go on and drive.

Bit of a weird analogy, but it's how I think when it comes to learning something like a framework, or library - you just never know what it will come in handy for you.


Learn an xUnit framework - when you get onto programming larger scale commercial projects you'll find they are used extensively.


Since I'm just learning tdd, I have thus far avoided the overhead of also learning a unit testing framework such as cunit. At this point, my thinking is that the additional learning curve - even if shallow - of a framework is not worth the effort for such a small project. I'm wondering: Is this incorrect (i.e. I'd be better off learning the framework now)? At what point does learning a framework pay off?

I agree with philippe, Jason and Paolo, but wanted to throw in yet another way of looking at it:

The so-called xUnit frameworks are a very simple and light, both as a library to download (binaries of e.g. latest JUnit or CUnit are around 200 kilobytes), and, more imporantly, conceptually.

If you're doing (or would like to do) any unit testing at all, you would definitely benefit from using a proven tool, and learning xUnit will start paying off right away. I think you may have overestimated the "overhead" involved somewhat – don't let the word "framework" scare you ;-) – you'll be up and running with xUnit in half an hour or less.

Disclaimer: Your question isn't tagged C, so I took it as language-agnostic one. I don't know if unit testing is very different on C side (compared to Java), but I doubt it. :-)


Following is a C# .Net example where a Moq framework is useful in a MSTest using Visual Studio.

Observe the difference between TestOld() and MoqTest(). Purpose of both the tests are same. But in TestOld(), I need to write MyMemberManagerForTest concrete implementation. Whereas in MoqTest(), the Moq framework will do the task for me.

  _mockIMemberManager = new Mock<IMemberManager>();
  _mockIMemberManager.Setup(x => x.GetMember(It.IsAny<int>())).Returns(member);

Reference: How to Use Mock Library For Your Unit Testing In C#

[TestClass()]
public class TestMembershipManager
{
    private LibraryCore _targetLibraryCore;

    #region Old Test withou Moq

    public class MyMemberManagerForTest : IMemberManager
    {
        public Member GetMember(int memberID)
        {
            Member member = new Member()
            {
                MemberID = 1,
                FirstName = "Lijo",
                MaximumBookCanBorrow = 4,
            };

            return member;
        }
    }

    private IMemberManager _memberManager;

    [TestMethod()]
    public void TestOld()
    {
        _memberManager = new MyMemberManagerForTest();
        _targetLibraryCore = new LibraryCore(_memberManager);

        int memberID = 1;
        double expected = 12;
        double actual;

        //TEST
        actual = _targetLibraryCore.CalculateMembershipCost(memberID);
        Assert.AreEqual(expected, actual);
    }


    #endregion

    #region Test with Moq
    private Mock<IMemberManager> _mockIMemberManager;

    [TestMethod()]
    public void MoqTest()
    {
        _mockIMemberManager = new Mock<IMemberManager>();
        _targetLibraryCore = new LibraryCore(_mockIMemberManager.Object);

        Member member = new Member()
        {
            MemberID = 1,
            FirstName = "Lijo",
            MaximumBookCanBorrow = 4,
        };

        _mockIMemberManager.Setup(x => x.GetMember(It.IsAny<int>())).Returns(member);

        int memberID = 1;
        double expected = 12;
        double actual;

        //TEST
        actual = _targetLibraryCore.CalculateMembershipCost(memberID);
        Assert.AreEqual(expected, actual);
    }

    #endregion

}

For this type of testing to work it is important for the code to utilize Dependency Injection as you can see in the overloaded LibraryCore constructor

public class LibraryCore
{
    private readonly IMemberManager _memberManager;

    public LibraryCore(IMemberManager memberManager)
    {
        this._memberManager = memberManager;
    }

    public double CalculateMembershipCost(int memberID)
    {
        double membershipCost = 0;
        Member member = _memberManager.GetMember(memberID);
        membershipCost = 10 + member.MaximumBookCanBorrow * 0.5;
        return membershipCost;
    }
}


One note: if you are following the normal pattern of red-green-refactor, then as you refactor your unit tests, the resultant code should evolve towards being a unit test framework. For instance, you shouldn't have two copies of code that does

if (expected != actual) {printf("%d != %d", actual, expected); return -1;}

That should be refactored into something like

void AssertNotEqual(int expected, int actual, char* message)
{
    if (expected != actual) {printf(message, actual, expected); return -1;}
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜