惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
H
Heimdal Security Blog
K
Kaspersky official blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
Schneier on Security
G
GRAHAM CLULEY
U
Unit 42
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
CERT Recently Published Vulnerability Notes
Google DeepMind News
Google DeepMind News
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
C
Cisco Blogs
Cyberwarzone
Cyberwarzone
T
The Exploit Database - CXSecurity.com
Project Zero
Project Zero
Security Archives - TechRepublic
Security Archives - TechRepublic
www.infosecurity-magazine.com
www.infosecurity-magazine.com
博客园 - 司徒正美
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
V
Visual Studio Blog
博客园 - Franky
Engineering at Meta
Engineering at Meta
WordPress大学
WordPress大学
Jina AI
Jina AI
P
Proofpoint News Feed
P
Proofpoint News Feed
有赞技术团队
有赞技术团队
L
LINUX DO - 最新话题
宝玉的分享
宝玉的分享
N
News and Events Feed by Topic
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
博客园 - 聂微东
T
The Blog of Author Tim Ferriss
Spread Privacy
Spread Privacy
Application and Cybersecurity Blog
Application and Cybersecurity Blog
IT之家
IT之家
S
Security Affairs
博客园 - 叶小钗
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
小众软件
小众软件
N
News | PayPal Newsroom
Cloudbric
Cloudbric
AWS News Blog
AWS News Blog
W
WeLiveSecurity
The Last Watchdog
The Last Watchdog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
NISL@THU
NISL@THU

博客园 - 阿龍

FW:Software Testing What is the difference between modified duration, effective duration and duration? Netezza External Tables --How to use local files in external table How to push master to QA branch in GIT FTPS Firewall Query performance optimization of Vertica (Forward)5 Public Speaking Tips That'll Prepare You for Any Interview (转)The remote certificate is invalid according to the validation procedure Change - Why we need coding standards (转)测试用例编写规范 (转)测试用例模板(Test Case Template) (转)Facebook如何提高软件质量? 敏捷问题小结 (转)RACI模型(RACI Model) (转)MongoDB——安装篇(windows) (转)MongoDB 增加用户 删除用户 修改用户 读写权限 只读权限, (转)MongoDB设置访问权限、设置用户 赎回收益率(Yield to Call,YTC) YTC, YTM, YTW
(转)Part 2 - Basic of mocking with Moq
阿龍 · 2012-05-02 · via 博客园 - 阿龍

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

        Product myProduct = new Product {ID = 1, Name = "Simple Product", RawPrice = 25.0M};

        Mock<ITaxCalculator> fakeTaxCalculator = new Mock<ITaxCalculator>();

        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

        decimal calculatedTax = myProduct.GetPriceWithTax(fakeTaxCalculator.Object);

        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

        decimal calculatedTax = myProduct.GetPriceWithTax(fakeTaxCalculator.Object);

        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.