【问题标题】:Should I test that a certain system under test implements IFoo, IBar and MyBaseClass?我是否应该测试某个被测系统是否实现了 IFoo、IBar 和 MyBaseClass?
【发布时间】:2011-05-04 14:41:14
【问题描述】:

我的基类构造函数在提供无效参数时抛出异常。
由于我不想一遍又一遍地复制/粘贴相同的测试,我想确保被测系统实际上实现了 IFoo、IBar 和 MyBaseClass。
一方面,我的类将实现 IFoo、IBar 和 MyBaseClass 是有道理的,但另一方面,如果我的实现发生变化,我将需要维护更多的测试,如果我要为此制定一个约定,可能会有一些开销重新排列继承层次结构时。

【问题讨论】:

  • 我最近也一直在想解决这个问题的最佳方法。测试它是否实现了特定的接口/基类似乎是错误的,因为这肯定是一个实现细节。正如你所说,你将来可能会改变这一点。您要测试的是行为是您所期望的。这种对我来说意味着应该有一个测试基类,特定实现的所有测试都可以扩展并用于测试基本功能,以便每个派生类都通过基本测试运行,在你的情况下将测试无效的参数。
  • 对this question有帮助
  • @SamHolder:是的,但另一方面,您从 MyBaseClass 继承这一事实为被测系统提供了一些保证。

标签: unit-testing tdd


【解决方案1】:

通常,我更喜欢测试行为,而不是实现。 例如,我会确保我的 SUT 可以执行 IFoo 和 IBar 提供的功能。从业务角度来看,MyClass 是否实现 IFoo 并不重要。 重要的是 objectUnderTest 实现了 DoThis()。

阿萨夫。

【讨论】:

  • @Assaf 我认为我们同意这一点,但问题是如何我们如何编写测试,这将允许我们为公共部分编写一次测试并拥有所有实现通过这些测试进行测试,而不是为每个实现重复所有相同的测试。
  • @Sam - 我不确定我是否听懂了你的问题。我的意思是我实际上并没有编写验证 SUT 是否具有接口或实现方法的测试。我调用所需的方法并测试结果。如果您希望测试一些常用功能,您应该将其提取到一个单独的类中并进行测试(例如,提取一个具有 Execute() 方法的 WidgetCalculator 类)。这有帮助吗?
  • @Assaf。如果您通过提取到不同的类来测试通用功能并进行测试,那么您需要确保实现使用通用类(这是 the_drow 通过测试从基类继承来尝试做的事情)。如果您不这样做,那么如果您将实现更改为不使用公共类,您将不会有涵盖基类测试的行为的测试。否则您需要对每个派生类重复测试,以确保您正在测试每个实现的行为。
  • 想象一下我有一个方法 DoThis() 我想验证它做某事。所以我创建了一个测试来检查它。现在成像 DoThis 来自一个接口,我有 100 个类都实现了 DoThis()。我必须为此编写 100 个测试。如果我将 DoThis 放在基类中,我可以对其进行一次测试,但我现在遇到的问题是,如果我更改其中一个派生类的实现以使其不从基类继承,那么现在不会测试 DoThis。我们想要一种方法,其中对 DoThis 的测试编写一次,并且每个实现都可以通过测试运行。
  • 好的。假设我们有两个类:Service 和 Engine。 Service.DoThis() 调用 Engine.Execute()。我将有一个测试来检查 Service.DoThis() 的行为,它存根(但不检查)Engine.Execute() 的实现。我将也进行检查Engine.Execute() 行为的测试。这应该可以为您提供全面的覆盖。
【解决方案2】:

我认为您可以通过使用包含一些测试的抽象基类以及获取特定实现的抽象方法来解决此问题。

这仍然需要在每个具体的实现测试类中编写一些代码,但应该确保如果您从基类更改实现,您的构造函数仍然会被测试。

我刚刚做了一些类似的事情来测试一个方法,该方法对于许多实现来说是无操作的(它只返回给定的内容),但对于某些实现它会做一些事情,因此无操作代码测试在具有抽象方法的单个基类来获取要测试的对象(获取无操作方法所在的接口实例),以及应该只是无操作方法的具体实现扩展该类并覆盖抽象方法以返回我们要测试方法的类的实例。

这帮助我解决了我的问题,希望它也能帮助你。

public abstract class BaseClass
{
    public abstract void CallConstructor(SomeType arg1, SomeType2 arg2);

    [Test]
    public void Constructor_WhenArg1IsNull_ArgumentNullExceptionIsThrown()
    {
        Assert.Throws<ArgumentNullException>(()=>CallConstructor(null, new SomeType2()));       
    }

    [Test]
    public void Constructor_WhenArg2IsNull_ArgumentNullExceptionIsThrown()
    {
        Assert.Throws<ArgumentNullException>(()=>CallConstructor(new SomeType(), null));       
    }
}

[TestFixture]
public class ConcreteImplementation1Tests : BaseClass
{
    public override void CallConstructor(SomeType arg1, SomeType2 arg2)
    {
    new ConcreteImplementation1(arg1,arg2);
    }
}

[TestFixture]
public class ConcreteImplementation2Tests : BaseClass
{
    public override void CallConstructor(SomeType arg1, SomeType2 arg2)
    {
    new ConcreteImplementation2(arg1,arg2);
    }
}

除此之外,同样的技术可用于测试常见的

实现接口的所有类的功能。以 IList 为例:

public abstract class ListImplementationsCommonTests
{
    public abstract IList GetListImplementation();

    [Test]
    public void Add_WhenValidItemIsAdded_ItCanBeFound()
    {
        IList list = GetListImplementation();
        object item = new Object();
        list.Add(item);
        Assert.That(list.Contains(item));      
    }

    [Test]
    public void Add_WhenNullIsAdded_ArgumentNullExceptionIsThrown()
    {
        IList list = GetListImplementation();
        Assert.Throws<ArgumentNullException>(()=>list.Add(null));              
    }

    ...
    //more common tests
    ...
}

然后可以使用相同的代码测试 IList 的每个实现(如 ArrayList、LinkedList、DoubleLinkedList、SortedList 等),而无需一次又一次地重复相同的测试:

[TestFixture]
public class ArrayListTests: ListImplementationCommonTests
{
    public override IList GetListImplementation()
    {
    return new ArrayList();
    }

   ...
   //Extra specific tests for the ArrayList

}

[TestFixture]
public class LinkedListTests: ListImplementationCommonTests
{
    public override IList GetListImplementation()
    {
    return new LinkedList();
    }

   ...
   //Extra specific tests for the LinkedList

}

[TestFixture]
public class SortedListTests: ListImplementationCommonTests
{
    public override IList GetListImplementation()
    {
    return new SortedList();
    }

   ...
   //Extra specific tests for the SortedList
}

【讨论】:

    【解决方案3】:

    将 IFoo 和 IBar 的实现与实现它们的类分开测试。因此,当您测试实现类时,您不必对其进行测试。

    对于 MyBaseClass(如果它被声明为 MustInherit),我将创建一个测试模拟(继承自 MyBaseClass 并位于测试项目中)并测试在 MyBaseClass 中完成的所有操作。

    【讨论】:

    • 这与我的要求相差甚远。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-17
    • 2012-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多