Click here to Skip to main content
Click here to Skip to main content

Using Multiple Asserts in One Test Considered Helpful

, 1 Mar 2009
Rate this:
Please Sign up or sign in to vote.
One TDD mantra is that there should be only one "assert" per test. Here is a finance/trading example where that doesn't work very well.

Introduction

One TDD mantra is that there should be only one "assert" per test. This is only a guideline and as a general rule, it is a pretty good one. Often times however, I find myself wanting to assert on a set of data.

So if you look at it in a certain way you could say that I want a single assert, but on a set of data, not just a single component of that data. Another way to look at is that I am in fact testing too many things in one test. I think the below method reads very well and communicates the intent of the test much better than either manually doing multiple Assert.AreEqual calls or trying to create a bunch of tests to test each particular piece of the set of data.

Background

You should be familiar with a unit testing framework and TDD to get the most out of this article. The examples here are in C# but I think the general idea applies to any language/testing framework.

Using the Code

Let's say we wanted to test a set of data and in our test, we were checking this:

Assert.AreEqual(75, allocations["FUND1"].Quantity);
Assert.AreEqual(15, allocations["FUND2"].Quantity);
Assert.AreEqual(10, allocations["FUND3"].Quantity);       

Since I haven't yet provided the context in which I'm doing this, I don't think you can yet make the determination of whether you think it is a good idea or not inside a single test. So for now, bear with me and look at the following which does the same thing (and more since you don't have to check for nulls before accessing the Quantity property in the above example).

ExpectFundAllocations(allocations, @"
    FUND1   75
    FUND2   15
    FUND3   10
");

Here is the code for the helper method ExpectFundAllocations:

public void ExpectFundAllocations(Allocations allocs, string matchLinesOneString)
{
	//get values - word, whitespace, possibly negative number
	Regex regex = new Regex(@"^(\w+)\s+(-?\d+)$");

	string[] matchLines = Regex.Split(Environment.NewLine, matchLinesOneString);

	foreach (string inputRaw in matchLines)
	{
		//trim, skip blank lines
		string input = inputRaw.Trim();
		if (String.IsNullOrEmpty(input)) continue;

		Match match = regex.Match(input);
		if (!match.Success) throw new Exception("Unable to match " + input);
		string FundName = match.Groups[1].Value;
		decimal Quantity = Convert.ToDecimal(match.Groups[2].Value);

		Allocation alloc = allocs[FundName];
		Assert.IsNotNull(alloc, "No allocation for " + FundName);

		Assert.AreEqual(FundName, alloc.Fund.Name);
		Assert.AreEqual(Quantity, alloc.Quantity);
	}
}

The full test:

[Test]
public void TradeAllocatesEvenlyToMultipleFunds()
{
    Trade trade = new Trade
    {
        Investment = new Investment { Symbol = "IBM" },
        Type = TradeType.Buy,
        FilledQuantity = 100
    };

    Funds fundsToAllocateTo = new Funds { 
        new Fund { Name = "FUND1", RelativeWeighting = .75M },
        new Fund { Name = "FUND2", RelativeWeighting = .15M },
        new Fund { Name = "FUND3", RelativeWeighting = .10M },
    };

    FundAllocationCalculator calculator = 
		new FundAllocationCalculator(fundsToAllocateTo);

    Allocations allocations = calculator.CreateAllocationsForTrade(trade);

    ExpectFundAllocations(allocations, @"
        FUND1     75
        FUND2     15
        FUND3     10
    ");
}

Business Context

I don't like having arguments over abstract concepts, so I've used a very specific business example for this article, one in which I have used this method myself. Let's say you work for some form of finance company which engages in trades. In this particular example, we are buying 100 shares of IBM. This particular place you happen to work at executes those trades for multiple funds, with some of those funds being larger than others. So if you had three funds, you would not execute three trades, but would execute one and then allocate those shares to the funds based on various criteria. Here I have simplified the criteria so it is just a percentage and all the percentages for the funds add up to 100%.

The idea in the above test is that we have ratios for each of the funds and when we allocate, we want all of those to correspond to the ratio of the fund.

In this particular example, you could argue that I should be testing just one fund times the ratio equals the number of shares for that fund. But that would be a test for simple multiplication and wouldn't really communicate the business scenario I was trying to test. Also, there are additional complications such as rounding and making sure that all the shares were allocated that I am not showing here that would make testing for a single fund not very useful.

For another business example in which you need to look at the set, there could be another test in which we were disposing shares instead of acquiring them and one of the funds didn't have enough shares in that particular trade to participate at its normal fund percentage. Other funds would then pick up the extra shares since the shares have already been traded.

Points of Interest

In the above example, I am demonstrating the usage of regular expressions to test sets. The same concept could be used for the portion of the test where the fund allocations are built. It would be more readable and easier to specify for multiple tests. Likewise if we were involving the position held for the trade, we could specify that using the same method, again making it more readable.

History

  • Initial version - 01-Mar-2009

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Paul B.

United States United States
I've been a software developer since 1996 and have enjoyed C# since 2003. I have a Bachelor's degree in Computer Science and for some reason, a Master's degree in Business Administration. I currently do software development contracting/consulting.

Comments and Discussions

 
GeneralMy vote of 4 PinmvpKanasz Robert28-Sep-12 5:55 
QuestionMultiple asserts? Pinmemberptmcomp3-Mar-09 9:26 
AnswerRe: Multiple asserts? PinmemberPaul B.3-Mar-09 14:08 
GeneralRe: Multiple asserts? Pinmemberptmcomp15-Mar-09 3:17 
GeneralGross Statements that multiple asserts are bad is missing the forest for the trees PinmemberTaylorMichaelL2-Mar-09 3:27 
GeneralRe: Gross Statements that multiple asserts are bad is missing the forest for the trees PinmemberPaul B.2-Mar-09 6:59 
GeneralOne assert should be enough, you just need to know which assert to use PinmemberOmer Mor1-Mar-09 7:25 
GeneralRe: One assert should be enough, you just need to know which assert to write PinmemberPaul B.1-Mar-09 7:47 
GeneralRe: One assert should be enough, you just need to know which assert to write PinmemberOmer Mor1-Mar-09 8:45 
GeneralRe: One assert should be enough, you just need to know which assert to write PinmemberPaul B.1-Mar-09 10:19 
GeneralRe: One assert should be enough, you just need to know which assert to write PinmemberOmer Mor2-Mar-09 6:48 
GeneralRe: One assert should be enough, you just need to know which assert to write PinmemberPaul B.2-Mar-09 7:01 
GeneralRe: One assert should be enough, you just need to know which assert to write PinmemberOmer Mor18-Mar-09 6:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140821.2 | Last Updated 1 Mar 2009
Article Copyright 2009 by Paul B.
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid