0

In my MVC4 application almost every method does something with a database. Now I want to test these methods and I looked at this tutorial to do so. It is recommended there to use Moq to make mock objects.

In this particular case I want to test my GetTotal method of the ShoppingCart class (based on Microsoft Tutorial) which depends on the AddToCart method which look like this

public class ShoppingCart() {
    projectContext db = new projectContext ();

    public virtual void AddToCart(Product product)
    {
        var cartItem = db.Carts.SingleOrDefault(
            c => c.CartId == ShoppingCartId
            && c.ProductId == product.ProductId);

        if (cartItem == null)
        {  
            cartItem = new Cart
            {
                ProductId = product.ProductId,
                CartId = ShoppingCartId,
                Count = 1,
                DateCreated = DateTime.Now
            };
            db.Carts.Add(cartItem);
        }
        else
        {
            cartItem.Count++;
        }
        db.SaveChanges();
    }

    public virtual decimal GetTotal()
    {  
        decimal? total = (from cartItems in db.Carts
                          where cartItems.CartId == ShoppingCartId
                          select (int?)cartItems.Count *
                          cartItems.Product.Price).Sum();

        return total ?? decimal.Zero;
    }
}

As can be seen both methods depend on the projectContext instance db.

When testing these methods I wanted to use mock objects like this:

[TestClass]
public class ShoppingCartTest : DbContext
{

    [TestMethod]
    public void GetTotalTest()
    {
        // Arrange
        var _cart = new Mock<ShoppingCart>() { CallBase = true };
        var _mock = new Mock<Product>() { CallBase = true };
        _mock.SetupGet(m => m.Price).Returns(10.00M);

        // Act
        _cart.AddToCart() // cannot find method since no definiton can be found

        // Assert
       Assert.AreEqual(10.00M, _cart.GetTotal());// cannot find GetTotal method since no definiton can be found
    }

}

How can I possibibly test these methods? Is it possible to just create a "fake" database and use that? Or can I adjust the mock objects in a way that methods can be used on them (using "mock methods" seems to me like missing the point of testing the actual methods...)?

user2609980
  • 10,264
  • 15
  • 74
  • 143

2 Answers2

1

Testing this is hard, and I'm not sure it's possible to mock this. If you can use DI (Dependency Injection) you solve all your problems. By using DI you can mock the projectContext class and make it return whatever you need. Check out Autofac (http://www.codeproject.com/Articles/25380/Dependency-Injection-with-Autofac), it might already be installed in you mvc solution.

Zaphod
  • 1,412
  • 2
  • 13
  • 27
  • I will try to create an extra database just for testing and empty it after every method. – user2609980 Dec 02 '13 at 11:30
  • Be aware that tests often run in parallel so if you drop database after each method you might drop a database that another test method is currently working on. If you construct and drop database for each test project it will probably work better. However, I strongly recommend switching to a DI library such as AutoFac – Zaphod Dec 02 '13 at 11:46
  • Okay thanks I did not get it to work and will try autofac now. – user2609980 Dec 02 '13 at 12:03
1

Your problem is that your ShoppingCart class is directly coupled with the database layer.

Hide your dependency on the concrete projectContext behind an abstraction, for example an interface, so you can replace it when testing the production code that use it.

Depend on abstractions...

Chris Mantle
  • 6,595
  • 3
  • 34
  • 48
Mahol25
  • 3,131
  • 2
  • 22
  • 20
  • Could you elaborate on this? – user2609980 Dec 02 '13 at 12:36
  • To unit test this code in a natural way, you have to be able to create an instance of the ShoppingCartTest class, without being forced to implicitly create a class of the projectContext class. – Mahol25 Dec 02 '13 at 13:05
  • one way to do that is to create a IProjectContext interface, and make your ShoppingCartTest class depend on that interface instead. When creating your shopping cart instance, this would enable production code to pass on the real projectContex instance and test code to pass in a mock or a simple fake object. – Mahol25 Dec 02 '13 at 13:07
  • and if that was not clear, one way to do that is to add an additional code file with a partial class definition of the projectContext class where you let it inherit from the new IProjectContext interface. That's assuming that that projectContext is a generated type. If not and you wrote the code for that type yourself, just add the interface in that code file. – Mahol25 Dec 02 '13 at 13:26
  • To expand on what Mahol25 is saying, two design principles come to mind: [Program To an Interface, not an Implementation](http://stackoverflow.com/questions/383947/what-does-it-mean-to-program-to-an-interface), and [Favor Composition Over Inheritance](http://en.wikipedia.org/wiki/Composition_over_inheritance). The first link gives a good explanation of how to extract the interface out; following that approach, you can then have anything, including your mock during your tests, stand in for the interface. This gives you the dependency injection (DI) that Zaphod mentions. – Damon Dec 03 '13 at 16:54
  • Yes, great Damon. Thanks for expanding on my sketchy answer. – Mahol25 Dec 05 '13 at 09:45