技术开发 频道

单元测试和事先测试开发

  在此测试中,我选择同时测试两个操作:

  列表正确维护 Count 属性。
  列表可以包含两个项。

  某些测试驱动开发的倡议者提倡测试应尽可能只测试数目,但是如果只测试数目而不测试项目,这对于我而言有些不可思议,因此我所选择的是两者一起测试。

  编译这段代码时,由于 IntegerList 类中没有方法,因此编译失败,为此我加上以下代码进行编译:

  public int Count { get { return -1; } } public void Add(int value) { } public int this[int index] { get { return -1; } } 

  然后我返回并运行测试,这时它们显示为红色,表示测试失败。这很好,因为它意味着测试实际上已测试出程序错误。现在我可以执行此实现。我可以做些简单的工作,尽管这样做效率不是很高:

  public int Count { get { return elements.Length; } } public void Add(int value) { int newIndex; if (elements != null) { int[] newElements = new int[elements.Length + 1]; for (int index = 0; index < elements.Length; index++) { newElements[index] = elements[index]; } newIndex = elements.Length; elements = newElements; } else { elements = new int[1]; newIndex = 0; } elements[newIndex] = value; } public int this[int index] { get { return elements[index]; } } 

  我现在已经完成类的一小部分,并已经编写了可确保其正常工作的测试,但我仅仅测试了项目中很少的一部分。接下来,我要编写一个用于检查 1000 个项的测试:

  [Test] public void TestOneThousandItems() { list = new IntegerList(); for (int i = 0; i < 1000; i++) { list.Add(i); } Assertion.AssertEquals(1000, list.Count); for (int i = 0; i < 1000; i++) { Assertion.AssertEquals(i, list[i]); } } 

  此测试运行正常,因此无须进行任何更改。

  添加 ToString() 方法
  接下来,我将添加测试代码,以测试 ToString() 能否正常运行:

  [Test] public void TestToString() { IntegerList list = new IntegerList(); list.Add(5); list.Add(10); string t = list.ToString(); Assertion.AssertEquals("5, 10", t.ToString()); } 

  失败了,没关系。以下代码可以使其通过:

  public override string ToString() { string[] items = new string[elements.Length]; for (int index = 0; index < elements.Length; index++) { items[index] = elements[index].ToString(); } return String.Join(", ", items); } 

  启用 Foreach
  许多用户希望能够使用 foreach 遍历我的列表。为此,我需要在类中实现 Ienumerable ,并定义一个单独的用于实现 Ienumerable 的类。第一步,测试:

  [Test] public void TestForeach() { IntegerList list = new IntegerList(); list.Add(5); list.Add(10); list.Add(15); list.Add(20); ArrayList items = new ArrayList(); foreach (int value in list) { items.Add(value); } Assertion.AssertEquals("Count", 4, items.Count); Assertion.AssertEquals("index 0", 5, items[0]); Assertion.AssertEquals("index 1", 10, items[1]); Assertion.AssertEquals("index 2", 15, items[2]); Assertion.AssertEquals("index 3", 20, items[3]); } 

  我还通过 IntegerList 实现 IEnumerable :

  public IEnumerator GetEnumerator() { return null; } 

  运行测试时,此代码生成异常。为了正确地实现此功能,我将使用一个嵌套类作为枚举器。

  class IntegerListEnumerator: IEnumerator { IntegerList list; int index = -1; public IntegerListEnumerator(IntegerList list) { this.list = list; } public bool MoveNext() { index++; if (index == list.Count) return(false); else return(true); } public object Current { get { return(list[index]); } } public void Reset() { index = -1; } } 

  此类将一个指针传递给 IntegerList 对象,然后只返回此对象中的元素。

  这样,便可以对列表执行 foreach 操作,但遗憾的是 Current 属性属于对象类型,这意味着每个值将被装箱才能将其返回。此问题可采用一种基于模式的方法加以解决,此方法酷似当前方法,但它通过 GetEnumerator() 返回一个真正的类(而非 IEnumerator ),且此类中的 Current 属性为 int 类型。

  然而执行此操作后,我要确保在不支持该模式的语言中仍然可以使用这种基于接口的方法。我将复制编写的上一个测试并修改 foreach 以转换为接口:

  foreach (int value in (IEnumerable) list) 

  只需少许改动,列表即可在两种情况下正常运行。请查看代码样例以获取更多细节和更多测试。

  几点说明

  事先编写测试的优点就是您可以对在类中添加哪些内容以使测试通过有一个清楚的认识,从而简化代码的编写。

  如果要进行小型、递增的测试,则使用此方法最合适。我鼓励您在小型项目中使用此方法。事先测试开发是所谓的“敏捷方法”的一部分。

0
相关文章