2012年2月12日

Before You Refactor 重构之前

Rajith Attapattu
AT SOME POiNT, every programmer will need to refactor existing code. But before you do so, please think about the following, as this could save you and others a great deal of time (and pain):
在有些时候,每个程序员都需要重构现有的代码。但在你做之前,请想一下下面的事项,这能节省你和其他人的很多时间(和痛苦)。
The best approach for restructuring starts by taking stock of the existing codebase and the tests written against that code. This will help you under- stand the strengths and weaknesses of the code as it currently stands, so you can ensure that you retain the strong points while avoiding the mistakes. We all think we can do better than the existing system...until we end up with something no better—or even worse—than the previous incarnation because we failed to learn from the existing system’s mistakes.
重构最好的办法是先对现有代码库和相关测试代码归纳总结一下。这会帮你了解现在代码中的优点和缺点,这样你就可以保证能保持住优点而避免犯错误。我们都认为我们能比现有系统做的更好....直到我们以一些比原来不是那么好-或是更糟的代码而结束,因为我们没能从现有系统的错误中吸取教训。
Avoid the temptation to rewrite everything. It is best to reuse as much code as possible. No matter how ugly the code is, it has already been tested, reviewed, etc. Throwing away the old code—especially if it was in production—means that you are throwing away months (or years) of tested, battle-hardened code that may have had certain workarounds and bug fixes you aren’t aware of. If you don’t take this into account, the new code you write may end up showing the same mysterious bugs that were fixed in the old code. This will waste a lot of time, effort, and knowledge gained over the years.
抵挡住重写一切的诱惑。最好重用尽可能多的代码。不管代码多么丑陋,它都是已经测试的,检查过的。扔掉旧代码---尤其是产品代码,意味着你正在扔掉经过数月(或者数年)测试过的,久经沙场的代码,在其中有一些你不知道的临时解决方案和bug修复。如果你没有考虑这些,你写的新代码将会重现这些在旧代码中已经修复过的诡异的bug。这将浪费很多时间,努力及多年获得的知识。
Many incremental changes are better than one massive change. Incremental changes allows you to gauge the impact on the system more easily through feedback, such as from tests. It is no fun to see a hundred test failures after you make a change. This can lead to frustration and pressure that can in turn result in bad decisions. A couple of test failures at a time is easier to deal with, leading to a more manageable approach.
多次增量修改胜过一次重大的调整。增量修改能让你更容易的通过反馈,比如测试来判断对系统造成的影响。在你修改完之后看到百十个测试失败不是一件好事。这会导致挫败感和压力,从而做出错误的决定。一次一两个测试失败很容易对付,能很容易的处理。
After each development iteration, it is important to ensure that the existing tests pass. Add new tests if the existing tests are not sufficient to cover the changes you made. Do not throw away the tests from the old code without due consideration. On the surface, some of these tests may not appear to be applicable to your new design, but it would be well worth the effort to dig deep down into the reasons why this particular test was added.
在每个开发迭代之后,确保现有测试通过是很重要的。如果现有测试不足以覆盖你的修改就添加新的测试。不要在欠考虑的情况下扔掉原有代码的测试。表面上,一些测试可能看上去不适用于你的新设计,但是它值得你好好的去挖掘一下添加这个特定测试的原因。
Personal preferences and ego shouldn’t get in the way. If something isn’t broken, why fix it? That the style or the structure of the code does not meet your personal preference is not a valid reason for restructuring. Thinking you could do a better job than the previous programmer is not a valid reason, either.
抛掉自我和个人喜好。如果没有问题,为什么去修它呢?代码的结构或者风格不符合你的个人喜好不是一个正当的重构理由。同样,认为你能比原先的程序员干的更好也不是一个正当的理由。
New technology is an insufficient reason to refactor. One of the worst reasons to refactor is because the current code is way behind all the cool technology we have today, and we believe that a new language or framework can do things a lot more elegantly. Unless a cost-benefit analysis shows that a new language or framework will result in significant improvements in functionality, maintainability, or productivity, it is best to leave it as it is.
新技术不是重构的充分理由。最差的重构理由之一是现有代码对于我们今天所有酷的技术太古老了,我们相信新技术或者框架能更优雅的完成工作。除非有成本效益分析表明新技术或者框架能在功能性、可维护性或者生产力上有显著提升,否则最好让代码保持现状。
Remember that humans make mistakes. Restructuring will not always guarantee that the new code will be better—or even as good as—the previous attempt. I have seen and been a part of several failed restructuring attempts. It wasn’t pretty, but it was human.
记住人总会犯错。重构不能总保证新代码更好--或者和原代码一样好。我曾看到也参与过几个失败的重构尝试。人非圣贤,孰能无过。

posted @ 2012-02-12 00:26 蚂蚁蚂蚁 阅读(14) 评论(0) 编辑

Beauty Is in Simplicity 美在于简单

Jørn Ølmheim
 

THERE iS ONE qUOTE, from Plato, that I think is particularly good for all software developers to know and keep close to their hearts:
Beauty of style and harmony and grace and good rhythm depends on simplicity.
引用柏拉图的一句话,我认为尤其对软件开发人员有用且要铭记于心的。
美丽的风格与和谐,优雅和良好的节奏取决于简单。
In one sentence, this sums up the values that we as software developers should
aspire to.
一句话中,它总结了作为程序员的我们应该追求的价值。
There are a number of things we strive for in our code:
Readability
 Maintainability
Speed of development
The elusive quality of beauty
有一些事情是在我们代码中应该追求的:
可读性
可维护性
开发速度
美感
Plato is telling us that the enabling factor for all of these qualities is simplicity. 
What is beautiful code? This is potentially a very subjective question. Per- ception of beauty depends heavily on individual background, just as much of our perception of anything depends on our background. People educated in the arts have a different perception of (or at least approach to) beauty than people educated in the sciences. Arts majors tend to approach beauty in software by comparing software to works of art, while science majors tend to talk about symmetry and the golden ratio, trying to reduce things to formulae. In my experience, simplicity is the foundation of most of the arguments from both sides.
柏拉图告诉我们,所有这些品质的最有利因素就是简单。怎样才是美的代码?这可能是一个非常主观的问题。对美的认知严重依赖于个人的背景。文科的人与理科的人对美有不同的认识。文科生倾向于拿软件和艺术作品相比来寻找软件中的美,而理科生倾向于谈论对称和黄金比例,尝试把事情简化为公式。依我的经验来看,简单是双方多数考量因素的基础。
Think about source code that you have studied. If you haven’t spent time studying other people’s code, stop reading this right now and find some open source code to study. Seriously! I mean it! Go search the Web for some code in your language of choice, written by some well-known, acknowledged expert.
想想你学过的源码。如果你还没花时间学习过其他人的代码,先别往下看了,找一些开源代码学一下。不开玩笑,真的。在网上搜一些你偏好的编程语言的,由公认的有名的专家写的代码。
You’re back? Good. Where were we? Ah, yes...I have found that code that resonates with me, and that I consider beautiful, has a number of properties in common. Chief among these is simplicity. I find that no matter how complex the total application or system is, the individual parts have to be kept simple: simple objects with a single responsibility containing similarly simple, focused methods with descriptive names. Some people think the idea of having short methods of 5–10 lines of code is extreme, and some languages make it very hard to do, but I think that such brevity is a desirable goal nonetheless.
看完了?很好。我们到哪了?啊,是的...我发现能引起我的共鸣的,美的代码有一些共同的特性。主要的一点就是简单。我发现不管整个应用或是系统多么复杂,每一个独立的部分都很简单:每个对象都职责单一,并带有一些简单内聚并有描述性名字的方法。一些人认为方法5-10行为宜。一些编程语言很难做到这一点,但我仍认为这种简洁程度是合理的。
The bottom line is that beautiful code is simple code. Each individual part is kept simple with simple responsibilities and simple relationships with the other parts of the system. This is the way we can keep our systems maintain- able over time, with clean, simple, testable code, ensuring a high speed of development throughout the lifetime of the system.
美的代码就是简单的代码。每个独立的部分都保持简单的职责,与系统其他部分简单的关系。这就是我们能保持我们的系统长期可维护性,拥有干净,简单可测试的代码,在整个系统生命周期保证高速的开发的方法。
Beauty is born of and found in simplicity.
美源于简单。

posted @ 2012-02-12 00:24 蚂蚁蚂蚁 阅读(15) 评论(0) 编辑

Automate Your Coding Standard 自动化你的编码标准

Automate Your Coding Standard
自动化你的编码标准
Filip van Laenen
YOU’VE PROBABLY BEEN THERE, TOO. At the beginning of a project, every- body has lots of good intentions—call them “new project’s resolutions.” Quite often, many of these resolutions are written down in documents. The ones about code end up in the project’s coding standard. During the kick-off meeting, the lead developer goes through the document and, in the best case, everybody agrees that they will try to follow them. Once the project gets underway, though, these good intentions are abandoned, one at a time. When the project is finally delivered, the code looks like a mess, and nobody seems to know how it came to be that way.
你一定也遇到过这种情况。在项目开始的时候,每个人都有许多好的想法---称为“新项目决议书”。往往,很多决议记录在文档中。关于代码的就是编码规范。在启动会议上,开发经理过一遍文档,最好的情况下,每个人都同意并遵守。然而,一旦项目启动,这些好的想法就一个一个被废弃了。当项目最终交付时,代码看上去一团糟,没有人知道为什么会变成那个样子。
When did things go wrong? Probably already at the kick-off meeting. Some of the project members didn’t pay attention. Others didn’t understand the point. Worse, some disagreed and were already planning their coding standard rebellion. Finally, some got the point and agreed, but when the pressure in the project got too high, they had to let something go. Well-formatted code doesn’t earn you points with a customer that wants more functionality. Furthermore, following a coding standard can be quite a boring task if it isn’t automated. Just try to indent a messy class by hand to find out for yourself.
事情在什么时候出的错?大概在启动会议时就已经错了。一些项目组成员没当回事。另一些不明白为什么这么干。更有甚者,一些人不同意并且已经在计划他们自己的编码规范。最终,一些人明白并且赞同,但当项目压力太大时,他们不得不舍掉一些东西。组织良好的代码并不能给你在一个想要更多功能的客户那为你加分。进一步讲,如果没有自动化,遵循一种编码规范是一件相当烦的事。试试手工去调整一个乱糟糟的类的缩进你就知道了。
But if it’s such a problem, why is it that we want a coding standard in the first place? One reason to format the code in a uniform way is so that nobody can “own” a piece of code just by formatting it in his or her private way. We may want to prevent developers from using certain antipatterns in order to avoid some common bugs. In all, a coding standard should make it easier to work in the project, and maintain development speed from the beginning to the end. It follows, then, that everybody should agree on the coding standard, too—it does not help if one developer uses three spaces to indent code, and another uses four.
但如果这是个问题,为什么我们在开始想要一个编码规范呢?以统一方式规范代码的一个原因是没有人能以他自己私有的方式来“占有”一段代码。我们可能想阻止程序员用一些反模式来避免一些常规的错误。总的说来,一个编码规范应该能很容易在项目中应用起来,自始至终保持开发速度。然后,每个人也都应当遵守编码规范--不要一个程序员用3个空格缩进代码,其他人用4个。
There exists a wealth of tools that can be used to produce code quality reports and to document and maintain the coding standard, but that isn’t the whole solution. It should be automated and enforced where possible. Here are a few examples:
有很多工具可以生成代码质量报告并记录维护编码规范,但并不能解决所有问题。编码规范应该是尽可能规范化并强制执行的。这有一些例子:
Make sure code formatting is part of the build process, so that everybody runs it automatically every time they compile the code.
确保代码格式成为构建过程的一部分,这样每个人在每次编译代码时都会自动运行它。
Use static code analysis tools to scan the code for unwanted antipatterns. If any are found, break the build.
用静态的代码分析工具扫描代码中的不必要的反模式。如果有,让构建挂掉。
Learn to configure those tools so that you can scan for your own, project- specific antipatterns.
学会配置这些工具以便你可以自己扫描特定项目的反模式。
Do not only measure test coverage, but automatically check the results, too. Again, break the build if test coverage is too low.
不要只衡量测试覆盖率,也要自动检查结果。同样,如果测试覆盖率太低就让构建挂掉。
Try to do this for everything that you consider important. You won’t be able to automate everything you really care about. As for the things that you can’t automatically flag or fix, consider them a set of guidelines supplementary to the coding standard that is automated, but accept that you and your colleagues may not follow them as diligently.
试着对你认为重要的每件事这样做。你不可能自动化你真正关心的所有事。对于那些你不能自动标示或修复的事,考虑把它们作为自动化编码规范的补充指导,允许你和你的同事不统统遵循它。
Finally, the coding standard should be dynamic rather than static. As the proj- ect evolves, the needs of the project change, and what may have seemed smart in the beginning isn’t necessarily smart a few months later.
最后,编码规范应当动态而非静态。随着项目进行,项目要求也发生变化,一些在开始看上去很漂亮讲究的规范在几个月后就没那么有必要了。

posted @ 2012-02-12 00:23 蚂蚁蚂蚁 阅读(10) 评论(0) 编辑

Act with Prudence 三思而后行


Seb Rose
Whatever you undertake, act with prudence and consider the consequences.
—Anon
无论做什么事情,都要三思而后行。-Anon
NO MATTER HOW COMFORTABLE A SCHEDULE LOOKS at the beginning of an iteration, you can’t avoid being under pressure some of the time. If you find yourself having to choose between “doing it right” and “doing it quick,” it is often appealing to “do it quick” with the understanding that you’ll come back and fix it later. When you make this promise to yourself, your team, and your customer, you mean it. But all too often, the next iteration brings new prob- lems and you become focused on them. This sort of deferred work is known as technical debt, and it is not your friend. Specifically, Martin Fowler calls this deliberate technical debt in his taxonomy of technical debt,* and it should not be confused with inadvertent technical debt.
在一个迭代之初,不论你计划的如何有条不紊,有时你仍然会感到压力重重。如果要你在“干好”和“快点交工”之间权衡,人们往往会倾向于“快点交工”,然后回头有时间把它修好。当你向自己,你的团队和你的客户许下承诺时,你也是这么想的。但往往是,下一个迭代你又忙于出现的一些新问题。这些推迟的工作被称为技术债,并不是什么好事。Martin Fowler在他的技术债分类学中将其称为deliberate technical debt(故意技术债)*,以便和inadvertent technical debt(无意技术债)区分。
* http://martinfowler.com/bliki/TechnicalDebtQuadrant.html
Technical debt is like a loan: you benefit from it in the short term, but you have to pay interest on it until it is fully paid off. Shortcuts in the code make it harder to add features or refactor your code. They are breeding grounds for defects and brittle test cases. The longer you leave it, the worse it gets. By the time you get around to undertaking the original fix, there may be a whole stack of not-quite-right design choices layered on top of the original problem, making the code much harder to refactor and correct. In fact, it is often only when things have got so bad that you must fix the original problem, that you actually do go back to fix it. And by then, it is often so hard to fix that you really can’t afford the time or the risk.
技术债就像债务:你短期会从中获益,但在偿清前你要付利息。满足一时之需的代码使你难于添加新功能或者进行重构。它们是滋生缺陷和脆弱的测试用例的土壤。留它越久,危害越大。当你抽得时间来解决最初的那个问题时,将会有一堆不那么正确的设计方案掩盖于原来的问题之上,使代码愈发难于重构和修正。事实上,也仅当事情坏到你必须要修掉原来那个问题时,你才会真去修它。到那时因为你负担不起高昂的时间或者风险 ,修复它往往非常困难。
There are times when you must incur technical debt to meet a deadline or implement a thin slice of a feature. Try not to be in this position, but if the situ- ation absolutely demands it, then go ahead. But (and this is a big but) you must track technical debt and pay it back quickly, or things go rapidly downhill. As soon as you make the decision to compromise, write a task card or log it in your issue-tracking system to ensure that it does not get forgotten.
有时候为了应付最后期限或者实现一个小功能,你不得不引入技术债。尽量不要这么干,除非事态紧急。如果这么干了,你必须跟踪技术债并尽快偿清,否则事态会急转直下。一旦你决定妥协时,写一张任务卡片或者把它记录在你的问题跟踪系统,确保不要忘掉。
If you schedule repayment of the debt in the next iteration, the cost will be minimal. Leaving the debt unpaid will accrue interest, and that interest should be tracked to make the cost visible. This will emphasize the effect on Busi-ness value of the project’s technical debt and enables appropriate prioritization of the repayment. The choice of how to calculate and track the interest will depend on the particular project, but track it you must.
如果你把债务偿还计划在下一个迭代,成本是最小的。不偿还债务会增加利息,必须跟踪利息使成本可见。这样会突出项目的技术债对商业价值的影响,促使安排合适的优先级来偿还债务。选择怎样计算并跟踪利息取决于特定的项目,但你必须去做。
Pay off technical debt as soon as possible. It would be imprudent to do otherwise.
尽快的偿清技术债,否则是不明智的。

posted @ 2012-02-12 00:21 蚂蚁蚂蚁 阅读(10) 评论(0) 编辑

Technical Debt Quadrant 技术债象限

摘要: 14 October 2009 Martin FowlerThere's been a few posts over the last couple of months about TechnicalDebt that's raised the question of what kinds of design flaws should or shouldn't be classified as Technical Debt.前几个月的一些关于技术债的帖子引出了这样一个问题,什么样的设计缺陷应该或者不应该归为技术债。A good example of this is Un阅读全文

posted @ 2012-02-12 00:19 蚂蚁蚂蚁 阅读(25) 评论(1) 编辑

2011年11月27日

xUnit Extension之DataTheories

通过为测试添加[Theory]属性,我们可以进行数据驱动的测试。测试数据分为以下几种:

1. InlineData, 由InlineDataAttribute提供

//Provides a data source for a data theory, with the data coming from inline values.
[Theory]
[InlineData("a", "b", "ab")]
[InlineData("1", "2", "12")]
public void TestViaInlineData(string a, string b, string result)
{
Assert.Equal(result, a+b);
}

2.Property Data
public static IEnumerable<object[]> MyTestData
{
get { yield return new object[] { new[]{1,2,3}, "hello world", 2.3 }; }
}

// Provides a data source for a data theory, with the data coming from a public static property on the test class.
// The property must return IEnumerable<object[]> with the test data.
[Theory, PropertyData("MyTestData")]
public void PropertyPassingTestData(object foo, string bar, double baz)
{
Assert.Equal(new[] { 1, 2, 3 }, foo);
Assert.Equal("hello world", bar);
Assert.Equal(2.3, baz);
}
3. ClassData Attribute
public class StubData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { new[] { 1, 2, 3 }, "hello world", 2.3 };
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

//Provides a data source for a data theory, with the data coming from a class
// which must implement IEnumerable<objec[]>.
[Theory, ClassData(typeof(StubData))]
public void ClassPassingTestData(object foo, string bar, double baz)
{
Assert.Equal(new[] { 1, 2, 3 }, foo);
Assert.Equal("hello world", bar);
Assert.Equal(2.3, baz);
}
 
4.ExcelData, 数据从Excel中获得
// Provides a data source for a data theory, with the data coming a Microsoft Excel (.xls) spreadsheet.
[Theory]
[ExcelData(@"UnitTestData.xls", "SELECT x, y, z FROM Data")]
public void TestViaXls(double x,string y,string z)
{
//Data in excel
//x y z
//1 Foo Bar
//14 Biff Baz
Console.WriteLine("{0},{1},{2}", x, y, z);
}

5. OleDbData
// Provides a data source for a data theory, with the data coming from an OLEDB connection.
[Theory, OleDbData(
@"Provider=Microsoft.Jet.OleDb.4.0; Data Source=UnitTestData.xls; Extended Properties=Excel 8.0",
"SELECT x, y, z FROM Data")]
public void TestViaOleDb(double x,string y,string z)
{
//Data in excel
//x y z
//1 Foo Bar
//14 Biff Baz
Console.WriteLine("{0},{1},{2}", x, y, z);
}
还有一个属性叫做SqlServerDataAttribute,派生自OleDbDataAttribute,可以提供sqlserver来源的数据。
OleDb ConnectionString 可参见:http://www.connectionstrings.com/

posted @ 2011-11-27 00:49 蚂蚁蚂蚁 阅读(28) 评论(0) 编辑

2011年11月26日

xUnit 之 Equal(1)

Equal恐怕是写xUnit测试使用最多的方法。那么Equal方法到底能进行哪些类型参数的比较呢?

我们首先看一下Equal方法的几种重载形式:

1. 最常用到的一种形式:

/// <summary>
/// Verifies that two objects are equal, using a default comparer.
/// </summary>
/// <typeparam name="T">The type of the objects to be compared</typeparam>
/// <param name="expected">The expected value</param>
/// <param name="actual">The value to be compared against</param>
/// <exception cref="EqualException">Thrown when the objects are not equal</exception>
public static void Equal<T>(T expected, T actual)

2.带有比较器的Equal方法

/// <summary>
/// Verifies that two objects are equal, using a custom equatable comparer.
/// </summary>
/// <typeparam name="T">The type of the objects to be compared</typeparam>
/// <param name="expected">The expected value</param>
/// <param name="actual">The value to be compared against</param>
/// <param name="comparer">The comparer used to compare the two objects</param>
/// <exception cref="EqualException">Thrown when the objects are not equal</exception>
[SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "2", Justification = "If you pass null here, you deserve the NullReferenceException.")]
public static void Equal<T>(T expected, T actual, IEqualityComparer<T> comparer)
 
3.带精度的数值比较
/// <summary>
/// Verifies that two double values are equal, within the number of decimal
/// places given by precision.
/// </summary>
/// <param name="expected">The expected value</param>
/// <param name="actual">The value to be compared against</param>
/// <param name="precision">The number of decimal places (valid values: 0-15)</param>
/// <exception cref="EqualException">Thrown when the values are not equal</exception>
public static void Equal(double expected, double actual, int precision)
以及
/// <summary>
/// Verifies that two decimal values are equal, within the number of decimal
/// places given by precision.
/// </summary>
/// <param name="expected">The expected value</param>
/// <param name="actual">The value to be compared against</param>
/// <param name="precision">The number of decimal places (valid values: 0-15)</param>
/// <exception cref="EqualException">Thrown when the values are not equal</exception>
public static void Equal(decimal expected, decimal actual, int precision)
 
在第一种形式中,提到用的是默认的比较器。那么,xUnit为我们提供的默认的比较器是什么样子的呢?
下面是xUnit相等比较器的代码,非常好懂:
class AssertEqualityComparer<T> : IEqualityComparer<T>
{
static AssertEqualityComparer<object> innerComparer = new AssertEqualityComparer<object>();

public bool Equals(T x, T y)
{
Type type = typeof(T);

// Null?
if (!type.IsValueType || (type.IsGenericType && type.GetGenericTypeDefinition().IsAssignableFrom(typeof(Nullable<>))))
{
if (Object.Equals(x, default(T)))
return Object.Equals(y, default(T));

if (Object.Equals(y, default(T)))
return false;
}

// Same type?
if (x.GetType() != y.GetType())
return false;

// Implements IEquatable<T>?
IEquatable<T> equatable = x as IEquatable<T>;
if (equatable != null)
return equatable.Equals(y);

// Implements IComparable<T>?
IComparable<T> comparable1 = x as IComparable<T>;
if (comparable1 != null)
return comparable1.CompareTo(y) == 0;

// Implements IComparable?
IComparable comparable2 = x as IComparable;
if (comparable2 != null)
return comparable2.CompareTo(y) == 0;

// Enumerable?
IEnumerable enumerableX = x as IEnumerable;
IEnumerable enumerableY = y as IEnumerable;

if (enumerableX != null && enumerableY != null)
{
IEnumerator enumeratorX = enumerableX.GetEnumerator();
IEnumerator enumeratorY = enumerableY.GetEnumerator();

while (true)
{
bool hasNextX = enumeratorX.MoveNext();
bool hasNextY = enumeratorY.MoveNext();

if (!hasNextX || !hasNextY)
return (hasNextX == hasNextY);

if (!innerComparer.Equals(enumeratorX.Current, enumeratorY.Current))
return false;
}
}

// Last case, rely on Object.Equals
return Object.Equals(x, y);
}

public int GetHashCode(T obj)
{
throw new NotImplementedException();
}
}
通过看这段代码,有两处比较有意思的事情:
1.几个相等比较的接口:

IComparable<(Of <(T>)>) 接口定义 CompareTo 方法,该方法确定实现类型的实例的排序顺序。 IEquatable<(Of <(T>)>) 接口定义 Equals 方法,该方法确定实现类型的实例的相等性。

2.关于Enumerable对象的比较:

会顺序的比较两序列相同位置元素的值。

 

当被比较的对象没有实现以上的任何接口时,调用Object类的Equals静态方法进行相等性比较。

另外,在两个Enumerable对象比较过程中,在比较其中单个元素时,用的是

innerComparer.Equals(enumeratorX.Current, enumeratorY.Current))
这样,如果每个元素又是Enumerable对象的话,会继续进行序列比较。

posted @ 2011-11-26 14:31 蚂蚁蚂蚁 阅读(51) 评论(0) 编辑

xUnit Assert之其他

1. Contains test

[Fact]
public void CanFindNullInContainer()
{
var list = new List<object> { 16, null, "Hi there" };

Assert.Contains(null, list);
}
[Fact]
public void ListInContainer()
{
var list = new List<object> {1, 2, 3};
var list2 = new List<object> {4, 5, list};
Assert.Contains(list, list2);
}

2. NotContains Test

[Fact]
public void CanSearchForNullInContainer()
{
var list = new List<object> { 16, "Hi there" };
Assert.DoesNotContain(null, list);
}
[Fact]
public void NullContainerDoesNotThrow()
{
Assert.DoesNotThrow(() => Assert.DoesNotContain(14, null));
}
 
[Fact]
public void CanSearchForSubstringsCaseInsensitive()
{
Assert.Throws<DoesNotContainException>(
() => Assert.DoesNotContain("WORLD", "Hello, world!", StringComparison.InvariantCultureIgnoreCase));
}
3. Empty Test
[Fact]
public void IsEmpty()
{
var list = new List<int>();
Assert.Empty(list);
}
[Fact]
public void NullIsNotEmpty()
{
Assert.Throws<ArgumentNullException>(() => Assert.Empty(null));
}
[Fact]
public void IsEmpty()
{
Assert.Empty("");
}
4. False Test
[Fact]
public void AssertFalse()
{
Assert.False(false);
}
5. True Test
[Fact]
public void AssertTrue()
{
Assert.True(true);
}
5. Single Test
[Fact]
public void SingleItemCollectionReturnsTheItem()
{
var collection = new ArrayList { "Hello" };
var result = Assert.Single(collection);

Assert.Equal("Hello", result);
}
[Fact]
public void ObjectSingleMatch()
{
IEnumerable collection = new[] { "Hello", "World!" };

Assert.Single(collection, "Hello");
}
6. Same Test
[Fact]
public void BoxedTypesDontWork()
{
int index = 0;

Assert.Throws<SameException>(() => Assert.Same(index, index));
}
[Fact]
public void ValuesAreNotTheSame()
{
Assert.Throws<SameException>(() => Assert.Same("bob", "jim"));
}
[Fact]
public void ValuesAreTheSame()
{
const string jim = "jim";

Assert.Same(jim, jim);
}
7. IsType Test
[Fact]
public void IsType()
{
var expected = new InvalidCastException();
Assert.IsType(typeof(InvalidCastException), expected);
Assert.IsType<InvalidCastException>(expected);
}
8. InRange Test
[Fact]
public void DoubleValueWithinRange()
{
Assert.InRange(1.0, .75, 1.25);
}
[Fact]
public void StringValueWithinRange()
{
Assert.InRange("bob", "adam", "scott");
}
9. Throw Test
[Fact]
public void DoesNotThrowException()
{
var methodCalled = false;
Assert.DoesNotThrow(() => methodCalled = true);
Assert.True(methodCalled);
}

posted @ 2011-11-26 14:13 蚂蚁蚂蚁 阅读(42) 评论(0) 编辑

xUnit之Equal(2)

继续上节,列举一下Equal测试的类型:

1. ArrayTest

[Fact]
public void Array()
{
string[] expected = { "@", "a", "ab", "b" };
string[] actual = { "@", "a", "ab", "b" };

Assert.Equal(expected, actual);
Assert.Throws<NotEqualException>(() => Assert.NotEqual(expected, actual));

Assert.Equal("a", "b");
}

2. Null

[Fact]
public void EqualsNull()
{
Assert.Equal<object>(null, null);
}
3. Numeric Test
[Fact]
public void DecimalEqualsFails()
{
decimal expected = 25;
decimal actual = 42;

Assert.Throws<EqualException>(() => Assert.Equal(expected, actual));
}
[Fact]
public void EqualsSByte()
{
const sbyte valueType = 35;
const sbyte referenceValue = 35;

Assert.True(valueType == referenceValue);
Assert.Equal(referenceValue, valueType);
Assert.Equal<sbyte>(valueType, 35);
Assert.Equal<sbyte>(referenceValue, 35);
}
4.Using a comparer
[Fact]
public void EqualsStringIgnoreCase()
{
string expected = "TestString";
string actual = "testString";

Assert.False(actual == expected);
Assert.NotEqual(expected, actual);
Assert.Equal(expected, actual, StringComparer.CurrentCultureIgnoreCase);
}
5.Null? Test
[Fact]
public void NullableValueTypesCanBeNull()
{
DateTime? dt1 = null;
DateTime? dt2 = null;

Assert.Equal(dt1, dt2);
}
6.Precision Test
[Fact]
public void AssertEqualWithDecimalWithPrecision()
{
Assert.Equal(0.11111M, 0.11444M, 2);
}

posted @ 2011-11-26 13:36 蚂蚁蚂蚁 阅读(44) 评论(0) 编辑

2011年11月24日

What is xUnit.net? Xunit 是什么 ?


xUnit.net is a unit testing tool for the .NET Framework.Written by the original inventor of NUnit, xUnit.net is the latest technology for unit testing C#, F#, VB.NET and other .NET languages. Works with ReSharper, CodeRush, and TestDriven.NET.

 

 

xUnit.net 是一个用于.NET框架的单元测试工具。

xUnit出自Nunit创始人之手。

可用于C#,F#,VB.NET以及其他.NET语言的单元测试。

 可与ReSharper, CodeRush 以及TestDriven.NET协同工作。

 

更多信息请移步http://xunit.codeplex.com/

 

在接下来的几节,主要总结一下怎么样使用xUnit编写单元测试。

 

posted @ 2011-11-24 00:36 蚂蚁蚂蚁 阅读(79) 评论(0) 编辑