(转)Part 2 - Basic of mocking with Moq

See also: Â Part 1Â - Part 3

As every mocking framework, except TypeMock which can perform differently, every mocked class can't be sealed and methods that need to be mocked need to be public. If  the class is not inheriting from an interface, the method that are being mocked need to be virtual.

Once this is cleared... let's show a simple example of a Product having it's price calculated with a Tax Calculator.

Here's what we are starting with:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Product
{
    public int ID { get ; set ; }
    public String Name { get ; set ; }
    public decimal RawPrice { get ; set ; }
    public decimal GetPriceWithTax(ITaxCalculator calculator)
    {
        return calculator.GetTax(RawPrice) + RawPrice;
    }
}
 
public interface ITaxCalculator
{
  decimal GetTax( decimal rawPrice);
}

The method we want to test here is Product.GetPriceWithTax(ITaxCalculator). At the same time, we don't want to instantiate a real tax calculator which gets it's data from a configuration or a database. Unit tests should never depend upon your application's configuration or a database. By "application's configuration", I mean "App.config" or "web.config" which are often changed during the life of an application and might inadvertently fail your tests.

So, we are going to simply mock our tax calculator like this:

?
1
2
3
4
5
6
7
8
//Initialize our product
        Product myProduct = new Product {ID = 1, Name = "Simple Product" , RawPrice = 25.0M};
 
        //Create a mock with Moq
        Mock<ITaxCalculator> fakeTaxCalculator = new Mock<ITaxCalculator>();
 
        // make sure to return 5$ of tax for a 25$ product
        fakeTaxCalculator.Expect(tax => tax.GetTax(25.0M)).Returns(5.0M);

Now It all depends on what you want to  test. Depending if you are a "State" (Classic) or "Behaviour verification" (Mockist), you will want to test different things. If you don't know the difference, don't bother now but you might want to look at this article by Martin Fowler.

So if we want to make sure that "GetTax" from our interface was called:

?
1
2
3
4
5
// Retrived the calculated tax
        decimal calculatedTax = myProduct.GetPriceWithTax(fakeTaxCalculator.Object);
 
        // Verify that the "GetTax" method was called from  the interface
        fakeTaxCalculator.Verify(tax => tax.GetTax(25.0M));

If you want to make sure that the calculated price equal your product price with your tax added (which confirm that the taxes were calculated):

?
1
2
3
4
5
// Retrived the calculated tax
        decimal calculatedTax = myProduct.GetPriceWithTax(fakeTaxCalculator.Object);
 
        // Make sure that the taxes were calculated
        Assert.AreEqual(calculatedTax, 30.0M);

What's the difference? The first example verify the behaviour by making sure that "GetTax" was called. It doesn't care about the value returned. It could return 100$ and it would care. All that mattered in this example was that GetTax was called. Once this is done, we can assume that the expected behaviour was confirmed.

The second example is a state verification. We throw 25$ inside the tax calculator and we expect the tax calculator to return 5$ for a total price of 30$. It wouldn't call GetTax and it wouldn't care. As long as the proper value is returned, it's valid.

Some people will argue that behaviour is better than state (or vice versa). Personally, I'm a fan of both. A good example is that I might want to verify that an invalid invoice will not be persisted to the database and a behaviour verification approach is perfect for this case. But if I'm verifying (like in this case) that the tax were properly calculated, state behaviour is more often than not quicker and more easier to understand.

Nothing prevent your from doing both and making sure that everything works. I'm still not a full fledged TDD developer but I'm trying as much as possible to make tests for my classes as often as possible.

If you found this article helpful, please leave a comment! They will be mostly helpful for my presentation on February 25th 2009 at www.dotnetmontreal.com.

Currently rated 4.9 by 16 people

 

Reference URL: http://blog.decayingcode.com/post/part-2-basic-of-mocking-with-moq.aspx

转载于:https://www.cnblogs.com/s021368/archive/2012/05/02/2479377.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值