J. Ed
J. Ed

Reputation: 6742

unit testing a factory method

suppose I have several OrderProcessors, each of them handles an order a little differently.
The decision about which OrderProcessor to use is done according to the properties of the Order object, and is done by a factory method, like so:

public IOrderProcessor CreateOrderProcessor(IOrdersRepository repository, Order order, DiscountPercentages discountPercentages)
{
    if (order.Amount > 5 && order.Unit.Price < 8)
    {
        return new DiscountOrderProcessor(repository, order, discountPercentages.FullDiscountPercentage);
    }

    if (order.Amount < 5)
    {
        // Offer a more modest discount
        return new DiscountOrderProcessor(repository, order, discountPercentages.ModestDiscountPercentage);
    }

    return new OutrageousPriceOrderProcessor(repository, order);
}

Now, my problem is that I want to verify that the returned OrderProcessor has received the correct parameters (for example- the correct discount percentage).
However, those properties are not public on the OrderProcessor entities.

How would you suggest I handle this scenario?

The only solution I was able to come up with is making the discount percentage property of the OrderProcessors public, but it seems like an overkill to do that just for the purpose of unit testing...

Upvotes: 5

Views: 946

Answers (3)

Sam Holder
Sam Holder

Reputation: 32936

You have a couple of choices as I see it. you could create specializations of DiscountOrderProcessor :

public class FullDiscountOrderProcessor : DiscountOrderProcessor
{
    public FullDiscountOrderProcessor(IOrdersRepository repository, Order order):base(repository,order,discountPercentages.FullDiscountPercentage)
    {}
}

public class ModestDiscountOrderProcessor : DiscountOrderProcessor
{
    public ModestDiscountOrderProcessor (IOrdersRepository repository, Order order):base(repository,order,discountPercentages.ModestDiscountPercentage)
    {}
}

and check for the correct type returned.

you could pass in a factory for creating the DiscountOrderProcessor which just takes an amount, then you could check this was called with the correct params.

You could provide a virtual method to create the DiscountOrderProcessor and check that is called with the correct params.

I quite like the first option personally, but all of these approaches suffer from the same problem that in the end you can't check the actual value and so someone could change your discount amounts and you wouldn't know. Even wioth the first approach you'd end up not being able to test what the value applied to FullDiscountOrderProcessor was.

You need to have someway to check the actual values which leaves you with:

you could make the properties public (or internal - using InternalsVisibleTo) so you can interrogate them.

you could take the returned object and check that it correctly applies the discount to some object which you pass in to it.

Personally I'd go for making the properties internal, but it depends on how the objects interact and if passing a mock object in to the discount order processor and verifying that it is acted on correctly is simple then this might be a better solution.

Upvotes: 0

Craig MacGregor
Craig MacGregor

Reputation: 4629

One way around this is to change the fields you want to test to internal instead of private and then set the project's internals visible to the testing project. You can read about this here: http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx

You would do something like this in your AssemblyInfo.cs file:

[assembly:InternalsVisibleTo("Orders.Tests")]

Although you could argue that your unit tests should not necessarily care about the private fields of you class. Maybe it's better to pass in the values to the factory method and write unit tests for the expected result when some method (assuming Calculate() or something similar) is called on the interface.

Or another approach would be to unit test the concrete types (DiscountOrderProcessor, etc.) and confirm their return values from the public methods/properties. Then write unit tests for the factory method that it correctly returns the correct type of interface implementation.

These are the approaches I usually take when writing similar code, however there are many different ways to tackle a problem like this. I would recommend figuring out where you would get the most value in unit tests and write according to that.

Upvotes: 3

PatrickSteele
PatrickSteele

Reputation: 14677

If discount percentage is not public, then it's not part of the IOrderProcessor contract and therefore doesn't need to be verified. Just have a set of unit tests for the DiscountOrderProcessor to verify it's properly computing your discounts based on the discount percent passed in via the constructor.

Upvotes: 1

Related Questions