Rebourne07
Rebourne07

Reputation: 47

How to unit test a method or constructor which creates a new instance of another class

I've done extensive research on this topic and have yet to find a reliable answer so here goes.

I have a constructor I'd like to test. This constructor initializes another class in order to set a global variable which is necessary throughout the rest of the class. However, the constructor of the class which the constructor initializes has dependencies on things like web session and other variables being set which are not set when the any tests run. How can I properly mock this to make sure I'm just testing the constructor?

Here's the constructor to be tested:

public CheckoutManager()
{
    _orderController = new OrderController();
    _orderController.PropertyChanged += (sender, args) => NotifyPropertyChanged(args.PropertyName);
}

The problem lies in the OrderController constructor:

public OrderController()
{
    _customer = WebUserSession.Current.Profile.Customer;
    _srd = ContextManager.GetStandardRequestByCulture( CultureInfo.CurrentUICulture.Name );
    WarehouseData data = new WarehouseData();
    data.WarehouseID = 0;   // TODO: Convert the 0 warehouse to a web warehouse type

    // Grab the attention items from the session
    AttentionItems = WebUserSession.Current.OrderAttentionItems;

    // Load the shopping cart
    _cart = ShoppingCartManager.Cart;
}

Here is my attempted test:

[TestMethod]
public void Constructor_ExpectInstantiation()
{
    var checkoutManager = new CheckoutManager();

    Assert.IsNotNull( checkoutManager );
}

It bombs out at the _customer = WebUserSession.Current.Profile.Customer; line. How do you mock this using Moq? If it cannot be mocked, is there a good explanation as to why? How could the original constructor be modified to have the same functionality in a more testable way? Thank you in advance.

Upvotes: 0

Views: 5169

Answers (2)

Matthew K
Matthew K

Reputation: 993

You won't be able to use Moq test to this code. You could

  • Modify the code to make it more testable by adding a constructor that accepts an OrderController
  • Use a more "advanced" testing technique such as shims.

Upvotes: 0

Pierre-Luc Pineault
Pierre-Luc Pineault

Reputation: 9201

You can use Dependency Injection, which is a form of Inversion of Control, to solve these kind of problems.

Instead of instantiating your OrderController in the constructor, pass it as an argument:

public CheckoutManager(OrderController orderController)
{
    _orderController = orderController;
    _orderController.PropertyChanged += (sender, args) => NotifyPropertyChanged(args.PropertyName);
}

You can then either instantiate the OrderController either manually somewhere in a Main-like method, or with a library like Autofac which will do all the wiring for you.

So now you can mock your interface in your test:

[TestMethod]
public void Constructor_ExpectInstantiation()
{
    Mock<OrderController> mockOrderController = new Mock<OrderControler>();
    var checkoutManager = new CheckoutManager(mockOrderController.Object);

    Assert.IsNotNull( checkoutManager );
}

That way, the constructor of OrderController will never be called (it's a mock) and it won't interfere with your tests of CheckoutManager. If you need to test some interactions with it, you can Setup and Verify specific methods using Moq.

Upvotes: 4

Related Questions