views:

46

answers:

2

When working with Domain Objects, how do you typically unit test a method that calls another method in the object? For example:

public class Invoice
{
  public IList<InvoiceLine> InvoiceLines;
  public string Company;
  public bool IsDiscounted;
  public DateTime InvoiceDate;
  //...
  public GetTotalAmt();
  public GetExtendedTotalAmt();

  public decimal GetTotalAmt()
  {
    decimal total;
    foreach (InvoiceLine il in InvoiceLines)
    {
      total += il.Qty * il.Price;
    }
    return total;
  }

  public decimal GetExtendedTotalAmt()
  {
    decimal discount;
    if (IsDiscounted)
      discount = .05M; 
    return GetTotalAmt() * discount;
  }
}

Unit testing GetTotalAmt() is easy, but with GetExtendedTotalAmt() I'd have to use stub/mock InvoiceLine objects to make it work, when all I really want to do is test that a discount is applied if the IsDiscounted flag is true.

How do other people handle this? I don't think it makes sense to split up the domain object since these methods are both considered part of the core Invoice functionality (and splitting it would likely cause developers to call the wrong method more often).

Thanks!

+3  A: 

You could make the GetTotalAmt method virtual and then:

var sut = new MockRepository().PartialMock<Invoice>();
sut.Expect(x => x.GetTotalAmt()).Return(10);
sut.Replay();

var result = sut.GetExtendedTotalAmt();
Darin Dimitrov
Is that rhino mocks?
Arnis L.
Yes, rhino mocks, sorry, I should have mentioned that.
Darin Dimitrov
It's quite a intrusive way because many methods need to be virtual, classes not sealed etc.
Stefan Steinegger
You could use `TypeMock` to mock non-virtual methods but it is not free.
Darin Dimitrov
+1  A: 

I would build up a situation which is as simple as possible: only one InvoiceLine with a Quantity and Price of 1.

Something like this:

invoice.Add(new InvoiceLine(new Article("blah", 1M), 1));

Assert.AreEqual(0.95M, invoice.GetExtendedTotalAmt());

When you find that this stuff gets quite complicated, finding errors gets hard etc, then it is a sign that you should split the class (making the calculations on the invoice a strategy or something similar). But as long as it is as simple as you piece of code here, I wouldn't worry about it.

Stefan Steinegger