Question

I am new to unit testing and TDD and mocking in general, however the general idea I understand. My question is how do I mock a class so that I can call the instantiated method without having duplicate code in my unit test and in my Implementing class? Given the following:

//Simple Interface
public interface IProduct {
    double price { get; set; }
    double tax { get; set; }
    double calculateCost();
}

//Simple Implementation of IProduct
public class SimpleProduct : IProduct {
    private double _price;
    private double _tax;

    public double price {
        get { return _price; }
        set { _price = value; }
    }

    public double tax {
        get { return _tax; }
        set { _tax = value; }
    }

    public double calculateCost() {
        return _price + (_price * _tax);
    }
}
//Complex implementation of IProduct
public class MarylandProduct : IProduct {
    private double _price;
    private double _tax;

    public double price {
        get { return _price; }
        set { _price = value; }
    }

    public double tax {
        get { return _tax; }
        set { _tax = value; }
    }

    public double calculateCost() {
        if (_price <= 100) return _price + (_price * _tax);
        else {
            double returnValue = 100 + (100 * _tax); //use tax rate for first 100
            returnValue += (_price - 100) + ((_price - 100) * 0.05); //use a flat rate of 0.05 for everything over 100
            return returnValue;
        }
    }
}

I have started writing a unit test that has the following:

[TestMethod]
[HostType("Moles")]
public void molesCalculateCostforMarylandProduct() {
    //Assign
    MMarylandProduct marylandProduct = new MMarylandProduct();
    marylandProduct.priceGet = () => 1000;
    marylandProduct.taxGet = () => 0.07;

    const double EXPECTED = 1052;

    //Act
    double actual = marylandProduct.Instance.calculateCost();

    //Assert
    Assert.AreEqual(EXPECTED, actual);
}

I want to be able to call the calculate cost method for either a MarylandProduct or a SimpleProduct in my unit test. Typically getting the price and tax comes from the database, but instead I have made it so that these values are stubbed to avoid any coupling with a database or service or whatever else that provides these values. What it comes down to is that I want to write a unit test that will test the functionality of calculateCost() without having to stub that method in the unit test because I know that in 2 years the logic in the MarylandProduct will change.

So for example once I have this test running I should be able to go in and change the code for MarylandProduct.calculateCost() to add a "luxury tax" of adding 50 to any price over say 750. If I do this, I know that my unit test will fail because the expected value is 1052 and now the MarylandProduct is returning something other than what is expected.

Am I just going about this the wrong way? Am I just missing the spirit of TDD? Thanks for any help.

EDIT: (adding in other mocking frameworks that I have tried)

[TestMethod]
    public void rhinoMockCalculateCostForMarylandProduct() {
        //assign
        IProduct marylandProduct = MockRepository.GenerateMock<IProduct>();
        marylandProduct.Stub(price => price.price).Return(1000);
        marylandProduct.Stub(tax => tax.tax).Return(0.07);

        const double EXPECTED = 1052;

        //act
        double actual = marylandProduct.calculateCost();

        //assert
        Assert.AreEqual(EXPECTED, actual);
    }

    [TestMethod]
    public void moqCalculateCostForMarylandProduct() {
        //assign
        var marylandProduct = new Mock<IProduct>();
        marylandProduct.Setup(price => price.price).Returns(1000);
        marylandProduct.Setup(tax => tax.tax).Returns(0.07);

        const double EXPECTED = 1052;

        //act
        double actual = ((MarylandProduct)marylandProduct.Object).calculateCost();

        //assert
        Assert.AreEqual(EXPECTED, actual);
    }

I want to avoid putting duplicate code in the unit test and in the implementation of the class because if the code in the class changes then the unit test will still pass because it hasn't been changed. I know that it is expected to make that change in the unit test however when you have a TON of unit tests is it acceptable to have this sort of design? Having duplicate code in your unit test and in your implementation?

Was it helpful?

Solution

Ok so, what you seem to be misunderstanding is the purpose of a Mock. A mock is used to isolate the system under test (SUT). Therefore you mock the SUT's dependencies, so that you can test the SUT in isolation (e.g. remove dependency on the db or some service). You do not mock the object you are testing, otherwise, what are you testing?

If I understand your question correctly your domain model has an implementation of IProduct (why you need different impls of a Product is another question).

You want to test that implementations's CalculateCost method. I see no reason why you need to use mocks for this. From the surface, Product shouldn't have any dependencies, therefore there is nothing to mock.

e.g.

Let's say you have this product in your domain:

public class MyProduct : IProduct {
    private double _price;
    private double _tax;

    public double Price {
        get { return _price; }
        set { _price = value; }
    }

    public double Tax {
        get { return _tax; }
        set { _tax = value; }
    }

    public double CalculateCost() {
        return //some complex logic here.
    }
}

This object is already isolated, it has no dependency, thus there is no need to mock.

To test it you would simply use it directly:

[TestMethod]
public void CalculateCost() {
    //arrange
    var myProduct = new MyProduct();
    myProduct.Price = 1000;
    myProduct.Tax= 0.07;

    //act
    double actualCost = myProduct.CalculateCost();

    //assert
    double expectedCost = 1052;

    Assert.AreEqual(expectedCost, actualCost );
}

Recommended reading: http://www.manning.com/osherove/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top