如果我有接口IFoo,并有几个实现它的类,那么针对接口测试所有这些类的最佳/最优雅/最聪明的方法是什么?
我想减少测试代码重复,但仍然坚持单元测试的原则.
你认为最佳做法是什么?我正在使用NUnit,但我想任何单元测试框架中的示例都是有效的
如果你有类实现任何一个接口,那么他们都需要在该接口中实现方法.为了测试这些类,您需要为每个类创建一个单元测试类.
让我们选择一条更聪明的路线; 如果您的目标是避免代码和测试代码重复,您可能希望创建一个抽象类来处理重复代码.
例如,您有以下界面:
public interface IFoo { public void CommonCode(); public void SpecificCode(); }
您可能想要创建一个抽象类:
public abstract class AbstractFoo : IFoo { public void CommonCode() { SpecificCode(); } public abstract void SpecificCode(); }
测试很容易; 在测试类中实现抽象类作为内部类:
[TestFixture] public void TestClass { private class TestFoo : AbstractFoo { boolean hasCalledSpecificCode = false; public void SpecificCode() { hasCalledSpecificCode = true; } } [Test] public void testCommonCallsSpecificCode() { TestFoo fooFighter = new TestFoo(); fooFighter.CommonCode(); Assert.That(fooFighter.hasCalledSpecificCode, Is.True()); } }
...或者让测试类扩展抽象类本身,如果这符合你的想象.
[TestFixture] public void TestClass : AbstractFoo { boolean hasCalledSpecificCode; public void specificCode() { hasCalledSpecificCode = true; } [Test] public void testCommonCallsSpecificCode() { AbstractFoo fooFighter = this; hasCalledSpecificCode = false; fooFighter.CommonCode(); Assert.That(fooFighter.hasCalledSpecificCode, Is.True()); } }
使用抽象类来处理接口所暗示的公共代码可以提供更清晰的代码设计.
我希望这对你有意义.
作为旁注,这是一种常见的设计模式,称为模板方法模式.在上面的示例中,模板方法是CommonCode
方法,SpecificCode
称为存根或钩子.这个想法是任何人都可以扩展行为,而无需了解幕后的东西.
很多框架都依赖于这种行为模式,例如ASP.NET,你必须在页面中实现钩子,或者用户控件,例如事件Page_Load
调用的生成方法Load
,模板方法在后台调用钩子.还有更多的例子.基本上,您必须实现的任何使用单词"load","init"或"render"的内容都由模板方法调用.
当他说,我不同意Jon Limjap的意见,
它不是a.)方法应该如何实现和b.)该方法应该做什么(它只保证返回类型),我收集的两个原因是你想要这种方法的动机测试.
合同的许多部分可能未在退货类型中指定.与语言无关的例子:
public interface List { // adds o and returns the list public List add(Object o); // removed the first occurrence of o and returns the list public List remove(Object o); }
您对LinkedList,ArrayList,CircularlyLinkedList以及所有其他人的单元测试不仅应测试列表本身的返回,还应测试它们是否已正确修改.
之前有一个关于按合同设计的问题,它可以帮助您指明干燥这些测试的一种方法.
如果您不想要合同的开销,我建议使用Spoike建议的测试台:
abstract class BaseListTest { abstract public List newListInstance(); public void testAddToList() { // do some adding tests } public void testRemoveFromList() { // do some removing tests } } class ArrayListTest < BaseListTest { List newListInstance() { new ArrayList(); } public void arrayListSpecificTest1() { // test something about ArrayLists beyond the List requirements } }