测试驱动开发TDD系列

引言

  这个系列来自我读《Test-Driven Development in Microsoft .NET》这本书的一些内容,以及一些自己的体会。

正文

  1、什么是测试驱动开发?

  可以用下面的两句话来定义。

  •   在你已经写好一个测试失败的自动化测试用例之前,绝不写一行代码。
  •   消除代码重复。

  第一句话很好理解,就是在写代码之前,先写一些测试失败的自动化脚本,测试肯定是失败的,因为没有任何实现。因为测试包含代码必须满足和实现的需求。如果没有需求,就不需要实现任何功能。这条可以防止我们实现没有测试和不需要实现的功能。

  但是我还是有一点疑问,就是如果一行代码都不写,连接口没有,自动化脚本又测试什么呢?也只能写一写手动测试脚本,功能性的测试脚本。针对具体实现的自动化脚本,也还是要等接口出来才可以写吧,你们说呢?

  第二点是说在应用中不应该有重复的代码。代码重复是一种典型的不良软件设计,会导致矛盾问题。如果有代码重复,在看见的时候,程序员就应该消除它。

  2、简单的设计

  因为测试覆盖了需求,所以在写代码的时候,你的工作就是不多不少的满足需求。每个人都理解“不少”(因为少了软件没有办法工作了),但是不是每个人都理解“不多”。

  “不多”是什么意思呢?想象回到有人让你在现有系统上添加功能的时候,然后你说“没有问题,在之前我就已经想到会发生这个了,我已经加入了这个功能的代码”。你被看做是英雄,因为你预料到了这个需求,并且在解决方案中已经实现了它。

  想一下在你为了额外的功能增加复杂性的时候,抽象类,等等,那些没有人让你实现的功能。这些额外的代码必须随着那些使用中的代码一起维护。事实上,维护这个软件的负担在加重,因为它超过了实际的适用范围。因此你需要为不多、不少而奋斗,可以参考下面:

  •   代码满足了当前客户的要求。
  •   代码通过了所有的测试。
  •   代码做了需要做的每一件事。
  •   代码的类数量最少。
  •   代码保持了最少数量的方法。

  优先级最高的就是:代码满足了当前客户的要求。在你满足了适当的需求之后,下一个高优先级的是:代码通过了全部的测试。其次就是其他的了。

  有一句话,原文是:

  You might think that achieving simplicity is an easy process. Think again—it’s often very difficult. However, the simpler the code is, the more resilient it is and the easier it is to modify.

  我是这么理解的:

  你一定认为完成这些简单的事情是一个非常容易的过程。自己想想,通常它很困难。但是,代码越简单,它越有弹性,越容易修改。

  我不知道是不是我理解错了,还是这句话本身就是错误的呢?我不认为代码越简单越好,应该保持适度,甚至是应该相对应该复杂一点。如果类最少就是好的,那就写几个类好了,每个类几个方法,方法要少吗,那就每个方法写上几千行。这样的代码还能有弹性吗?还能容易修改吗?这么说来的话,分层和设计模式是最扯蛋的了,他们会导致很多的类,很多的方法。还有就是设计原则:SRP,单一职责原则,更是要求类的职责要单一,每一个类只应该有一个改变它的理由。这不都是矛盾的吗?如何理解呢?还是我太极端的理解了书中的意思呢?

  3、重构

  重构可以理解为,改进代码本身,但是不影响功能。重构是TDD的关键环节,因为在增加测试的时候,需要重新定义代码的设计。例如:你在代码中看到了重复,你就要移除它。如果需要引入复杂性来移除重复,也是正确的,因为是实际需要的,而不是预期设计的。

  4、红/绿/重构

  红、绿、重构,定义了实现每一个测试的过程。这个过程的目标就是工作在一个小的,可验证的过程,可以提供及时的反馈。

  •   编写测试代码。
  •   编译测试代码,它肯定会失败,因为你还没有写任何实现的代码。
  •   编写实现的代码。
  •   运行测试,观察测试结果,可能是红色的。
  •   使得测试通过。
  •   运行测试,观察测试结果,直到变绿。
  •   重构代码,消除重复代码。
  •   重复上面的过程。

  这就是著名的红绿条,不断的修改代码,直到进度条变成了绿色。因为,红色代表没有通过测试,绿色代表通过了测试。然后再对代码进行重构,消除代码中的重复。

 

测试驱动开发TDD系列(二)

2010-08-01 13:02 by virus, 761 visits, 网摘, 收藏, 编辑

引言

  今天我们来做一个TDD的小例子。通过一个栈的实现来体验一下TDD的过程。在本系列的代码示例中,使用VS2010作为IDE工具,NUnit作为测试辅助工具。关于NUnit的使用,在园子中已经有很多的文章。可以搜索参考一下。推荐几篇不错的:

  NUnit详细使用方法

  实践单元测试(3)-Using NUnit

正文

 

1、简介

  今天的任务就是实现一个栈,一个没有边界的栈。就是数据结构中的栈,对栈中的元素的数量没有限制。栈是一个LIFO(Last-Input-First-Out,后进先出)的数据结构,先进入栈的元素要最后出来,最后

进入栈的元素可以现出来。

  栈的操作包括:Push、Pop、Top、IsEmpty。

  • Push,入栈,将元素放入栈中。
  • Pop,出栈,从栈顶返回一个元素,并且在栈中删除它。
  • Top,获取栈顶元素,不从栈中删除元素。
  • IsEmpty,返回栈是否为空,是否没有元素。

2、任务列表

根据上面的功能,写出一个测试的列表

  • 创建一个栈,验证IsEmpty是否为true。
  • Push一个元素,验证IsEmpty是否为false。
  • Push一个元素,然后进行Pop操作,验证IsEmpty是否为true。
  • Push一个元素,记录这个元素,进行Pop操作,看返回的元素和刚才记录的元素是否一致。
  • Push三个元素,记录他们,一个一个的进行Pop,验证他们Pop的顺序是否正确。
  • 对一个没有元素的栈进行Pop操作。
  • Push一个元素,进行Top操作,验证IsEmpty是否为false。
  • Push一个元素,进行Top操作,看看返回的元素是否就是Push的那个元素。
  • 对一个没有元素的栈进行Top操作。

3、创建测试用例

  针对任务列表中的每一条,建立一个测试用例。

  • 创建一个栈,验证IsEmpty是否为true。
  • Push一个元素,验证IsEmpty是否为false。
  • Push一个元素,然后进行Pop操作,验证IsEmpty是否为true。
  • Push一个元素,记录这个元素,进行Pop操作,看返回的元素和刚才记录的元素是否一致。
  • Push三个元素,记录他们,一个一个的进行Pop,验证他们Pop的顺序是否正确。
  • 对一个没有元素的栈进行Pop操作。
  • Push一个元素,进行Top操作,验证IsEmpty是否为false。
  • Push一个元素,进行Top操作,看看返回的元素是否就是Push的那个元素。
  • 对一个没有元素的栈进行Top操作。

 

 

结论

   下面是用来测试的Stack类和测试类的源码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BeautyCode.TDD.ConApp
{
   
public  class MyStack<T>
    {
       
private List<T> elements = new List<T>();

        
public bool IsEmpty
        {
            
get
            {
                
return (elements.Count == 0);
            }
        }

        
public void Push(T element)
        {
            elements.Insert(
0, element);
        }

        
public T Pop()
        {
            T top 
= Top();
            elements.RemoveAt(
0);
            
return top;
        }

        
public T Top()
        {
            
if (IsEmpty)
                
throw new InvalidOperationException("Stack is Empty");

            
return elements[0];
        }

    }
}

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using BeautyCode.TDD.ConApp;

namespace BeautyCode.TDD.Test1
{
    [TestFixture]
    
public class MyStackTest
    {
        
private MyStack<string> _myStack = null;

        [SetUp]
        
public void Init()
        {
             _myStack  
= new MyStack<string >();
        }

        [Test]
        
public void Empty()
        {
            Assert.IsTrue(_myStack .IsEmpty);
        }

        [Test]
        
public void PushOne()
        {
            _myStack.Push(
"first element");
            Assert.IsFalse(_myStack.IsEmpty,
                
"After Push, IsEmpty should be false");
        }

        [Test]
        
public void Pop()
        {
            _myStack.Push(
"first element");
            _myStack.Pop();
            Assert.IsTrue(_myStack.IsEmpty,
                
"After Push - Pop, IsEmpty should be true");
        }

        [Test]
        
public void PushPopContentCheck()
        {
            
string  expected = "1234";
            _myStack.Push(expected);
            
string  actual = _myStack.Pop();
            Assert.AreEqual(expected, actual);
        }

        [Test]
        
public void PushPopMultipleElements()
        {
            
string pushed1 = "1";
            _myStack.Push(pushed1);
            
string pushed2 = "2";
            _myStack.Push(pushed2);
            
string pushed3 = "3";
            _myStack.Push(pushed3);

            
string popped = (string)_myStack.Pop();
            Assert.AreEqual(pushed3, popped);
            popped 
= (string)_myStack.Pop();
            Assert.AreEqual(pushed2, popped);
            popped 
= (string)_myStack.Pop();
            Assert.AreEqual(pushed1, popped);
        }

        [Test]
        [ExpectedException(
typeof(InvalidOperationException))]
        
public void PopEmpty_myStack()
        {
            _myStack.Pop();
        }

        [Test]
        
public void PushTop()
        {
            _myStack.Push(
"42");
            _myStack.Top();
            Assert.IsFalse(_myStack.IsEmpty);
        }

        [Test]
        
public void PushTopContentCheckOneElement()
        {
            
string pushed = "42";
            _myStack.Push(pushed);
            
string topped = (string)_myStack.Top();
            Assert.AreEqual(pushed, topped);
        }

        [Test]
        
public void PushTopContentCheckMultiples()
        {
            
string pushed3 = "3";
            _myStack.Push(pushed3);
            
string pushed4 = "4";
            _myStack.Push(pushed4);
            
string pushed5 = "5";
            _myStack.Push(pushed5);

            
string topped = (string)_myStack.Top();
            Assert.AreEqual(pushed5, topped);
        }

        [Test]
        
public void PushTopNo_myStackStateChange()
        {
            
string pushed = "44";
            _myStack.Push(pushed);

            
for (int index = 0; index < 10; index++)
            {
                
string topped = (string)_myStack.Top();
                Assert.AreEqual(pushed, topped);
            }
        }

        [Test]
        [ExpectedException(
typeof(InvalidOperationException))]
        
public void TopEmpty_myStack()
        {
            _myStack.Top();
        }

        [Test]
        
public void PushNull()
        {
            _myStack.Push(
null);
            Assert.IsFalse(_myStack.IsEmpty);
        }

        [Test]
        
public void PushNullCheckPop()
        {
            _myStack.Push(
null);
            Assert.IsNull(_myStack.Pop());
            Assert.IsTrue(_myStack.IsEmpty);
        }

        [Test]
        
public void PushNullCheckTop()
        {
            _myStack.Push(
null);
            Assert.IsNull(_myStack.Top());
            Assert.IsFalse(_myStack.IsEmpty);
        }


    }
}


 

Technorati 标签: TDD,测试驱动开发

【Blog】http://virusswb.cnblogs.com/

【MSN】jorden008@hotmail.com

【说明】转载请标明出处,谢谢

原文地址:https://www.cnblogs.com/Leo_wl/p/1792794.html