简体   繁体   English

如何先编写单元测试并稍后编写代码?

[英]How to write unit test first and code later?

I am new to unit testing and have read several times that we should write unit test first and then the actual code. 我是单元测试的新手,并且多次读过我们应该首先编写单元测试然后再写实际代码。 As of now , i am writing my methods and then unit test the code. 截至目前,我正在编写我的方法,然后对代码进行单元测试。

If you write the tests first... 如果你先写测试......

You tend to write the code to fit the tests. 您倾向于编写代码以适合测试。 This encourages the "simplest thing that solves the problem" type development and keeps you focused on solving the problem not working on meta-problems. 这鼓励了“解决问题的最简单的事情”类型开发,并使您专注于解决不能解决元问题的问题。

If you write the code first... 如果你先写代码......

You will be tempted to write the tests to fit the code. 您将很想编写测试以适应代码。 In effect this is the equivalent of writing the problem to fit your answer, which is kind of backwards and will quite often lead to tests that are of lesser value. 实际上,这相当于编写问题以适合您的答案,这是一种倒退,并且通常会导致测试价值较低。

Sounds good to me. 听起来不错。 However, How do i write unit tests even before having my code in place? 但是,即使在我的代码到位之前,我如何编写单元测试? Am i taking the advice literally ? 我从字面上接受了这个建议吗? Does it means that i should have my POCO classes and Interfaces in place and then write unit test ? 这是否意味着我应该有我的POCO类和接口,然后编写单元测试?

Can anyone explain me how this is done with a simple example of say adding two numbers? 任何人都可以用一个简单的例子说明如何添加两个数字来解释我是如何做到的?

It's simple really. 这真的很简单。 Red, Green, Refactor. 红色,绿色,重构。

Red means - your code is completely broken. 红色表示 - 您的代码完全被破坏。 The syntax highlighting shows red and the test doesn't pass. 语法高亮显示红色,测试未通过。 Why? 为什么? You haven't written any code yet. 您尚未编写任何代码。

Green means - Your application builds and the test passes. 绿色表示 - 您的应用程序构建和测试通过。 You've added the required code. 您已添加所需的代码。

Refactor means - clean it up and make sure the test passes. 重构意味着 - 清理它并确保测试通过。

You can start by writing a test somewhat like this: 您可以从编写测试开始,如下所示:

[TestMethod]
public void Can_Create_MathClass() {
    var math = new MathClass();
    Assert.IsNotNull(math);
}

This will fail ( RED ). 这将失败( RED )。 How do you fix it? 你是如何解决的? Create the class. 创建类。

public class MathClass {
}

That's it. 而已。 It now passes ( GREEN ). 它现在通过( 绿色 )。 Next test: 下一个测试:

[TestMethod]
public void Can_Add_Two_Numbers() {
    var math = new MathClass();
    var result = math.Add(1, 2);
    Assert.AreEqual(3, result);
}

This also fails ( RED ). 这也失败了( RED )。 Create the Add method: 创建Add方法:

public class MathClass {
    public int Add(int a, int b) {
        return a + b;
    }
}

Run the test. 运行测试。 This will pass ( GREEN ). 这将通过( 绿色 )。

Refactoring is a matter of cleaning up the code. 重构是清理代码的问题。 It also means you can remove redundant tests. 这也意味着您可以删除多余的测试。 We know we have the MathClass now.. so you can completely remove the Can_Create_MathClass test. 我们知道我们现在有了MathClass ..所以你可以完全删除Can_Create_MathClass测试。 Once that is done.. you've passed REFACTOR , and can continue on. 一旦完成......你已经通过了REFACTOR ,并且可以继续。

It is important to remember that the Refactor step doesn't just mean your normal code. 重要的是要记住,重构步骤并不仅仅意味着您的正常代码。 It also means tests. 它也意味着测试。 You cannot let your tests deteriorate over time. 你不能让你的测试随着时间的推移而恶化。 You must include them in the Refactor step. 您必须在重构步骤中包含它们。

When you create your tests first, before the code, you will find it much easier and faster to create your code. 当您首先在代码之前创建测试时,您会发现创建代码更加容易和快捷。 The combined time it takes to create a unit test and create some code to make it pass is about the same as just coding it up straight away. 创建单元测试并创建一些代码以使其通过所需的组合时间与直接编码相同。 But, if you already have the unit tests you don't need to create them after the code saving you some time now and lots later. 但是,如果您已经进行了单元测试,那么在代码之后您不需要创建它们,现在可以节省一些时间。

Creating a unit test helps a developer to really consider what needs to be done. 创建单元测试有助于开发人员真正考虑需要完成的工作。 Requirements are nailed down firmly by tests. 通过测试坚定地确定要求。 There can be no misunderstanding a specification written in the form of executable code. 对可执行代码形式的规范不会有任何误解。

The code you will create is simple and concise, implementing only the features you wanted. 您将创建的代码简单明了,仅实现您想要的功能。 Other developers can see how to use this new code by browsing the tests. 其他开发人员可以通过浏览测试来了解如何使用此新代码。 Input whose results are undefined will be conspicuously absent from the test suite 测试套件中明显缺少输入结果未定义的输入

There is also a benefit to system design. 系统设计也有好处。 It is often very difficult to unit test some software systems. 对某些软件系统进行单元测试通常非常困难。 These systems are typically built code first and testing second, often by a different team entirely. 这些系统通常首先构建代码并进行第二次测试,通常由不同的团队完成。 By creating tests first your design will be influenced by a desire to test everything of value to your customer. 通过首先创建测试,您的设计将受到为客户测试所有有价值物品的愿望的影响。 Your design will reflect this by being easier to test. 您的设计将通过更容易测试来反映这一点。

Let's take a slightly more advanced example: You want to write a method that returns the largest number from a sequence. 让我们来看一个更高级的例子:你想编写一个从序列中返回最大数字的方法。

Firstly, write one or more units test for the method to be tested: 首先,为要测试的方法编写一个或多个单元测试:

int[] testSeq1 = {1, 4, 8, 120, 34, 56, -1, 3, -13};

Assert.That(MaxOf(testSeq1) == 120);

And repeat for some more sequences. 并重复一些序列。 Also include a null parameter, a sequence with one element and an empty sequence and decide if an empty sequence or null parameter should throw an exception (and ensure that the unit test expects an exception for an empty sequence if that's the case). 还包括一个null参数,一个带有一个元素和一个空序列的序列,并确定一个空序列或null参数是否应该抛出一个异常(并确保单元测试需要一个空序列的异常,如果是这种情况)。

It is during this process that you need to decide the name of the method and the type of its parameters. 在此过程中,您需要确定方法的名称及其参数的类型。

At this point, it won't compile. 此时,它将无法编译。

Then write a stub for the method: 然后为该方法编写一个存根:

public int MaxOf(IEnumerable<int> sequence)
{
    return 0;
}

At this point it compiles, but the unit tests fail. 此时它会编译,但单元测试失败。

Then implement MaxOf() so that those unit tests now pass. 然后实现MaxOf()以便现在通过那些单元测试。

Doing it this way around ensures that you immediately focus on the usability of the method, since the very first thing you try to do is to use it - before even beginning to write it. 这样做可以确保您立即专注于方法的可用性,因为您尝试做的第一件事就是使用它 - 甚至在开始编写之前。 You might well decide to change the method's declaration slightly at this point, based on the usage pattern. 您可能会根据使用模式决定稍微更改方法的声明。

A real world example would apply this approach to using an entire class rather than just one method. 一个真实世界的例子将使用这种方法来使用整个类而不仅仅是一个方法。 For the sake of brevity I have omitted the class from the example above. 为简洁起见,我省略了上面例子中的类。

It is possible to write the unit tests before you write any code - Visual Studio does have features to generate method stubs from the code you've written in your unit test. 在编写任何代码之前可以编写单元测试 - Visual Studio确实具有从您在单元测试中编写的代码生成方法存根的功能。 doing it this way around can also help understand the methods that the object will need to support - sometimes this can aid later enhancements (If you had a save to disk, that you also overload to save to Stream , this is more testable and aids spooling over the network if required later on) 这样做也可以帮助理解对象需要支持的方法 - 有时这可以帮助以后的增强(如果你有一个保存到磁盘,你也重载以保存到Stream ,这是更可测试的并且有助于假脱机通过网络,如果以后需要)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM