我是否应该测试某个被测系统是否实现了 IFoo、IBar 和 MyBaseClass?

发布于 2024-11-04 21:15:46 字数 210 浏览 0 评论 0原文

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

I have a case where my base class' constructor throws exceptions when providing invalid arguments.
Since I don't want to copy/paste the same test over and over again I'd like to ensure that the system under test actually implements IFoo, IBar and MyBaseClass.
On one hand it makes sense that my class will implement IFoo, IBar and MyBaseClass but on the other hand if my implementation changes I'll have one more test to maintain and if I'll make a convention out of this there might be some overhead when rearranging the inheritance hierarchy.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

女中豪杰 2024-11-11 21:15:46

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

阿萨夫。

As a rule, I prefer to test behavior, not implementation.
For example, I would make sure that I my SUT can do what IFoo and IBar offer. From a business perspective, it doesn't matter if MyClass implements IFoo or not. What is important, is that the objectUnderTest implements DoThis().

Assaf.

忆沫 2024-11-11 21:15:46

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

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

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

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

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
}

I think you can solve this by using an abstract base class which contains some of the tests, with an abstract method which gets the specific implementation.

This still require a little code in each concrete implementation test class but should ensure that if you change the implementation away from a base class your constructors are still tested.

i have done something similar just now to test a method which for many implementations is a no op (it just returns what it was given), but for some implementations it does something, and so the no op code tests are in a single base class with an abstract method to get the object to test (gets an instance of the interface that the no op method lives in), and the concrete implementations which should just be a no op extend that class and override the abstract method to return an instance of the class which we want to test the methods on.

this helped me solve my problem, hopefully it will help you too.

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);
    }
}

in addition to this the same technique can be used for testing common

functionality for all classes that implement an interface. Using IList as an example:

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
    ...
}

Then each implementation of IList (so ArrayList, LinkedList, DoubleLinkedList, SortedList etc etc) could then be tested with the same code without needing to repeat the same tests again and again:

[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
}
滿滿的愛 2024-11-11 21:15:46

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

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

Test the implementations of IFoo and IBar separate from the Class that implements them. So you don't have to test it, when you test the implementing class.

For MyBaseClass (if it is declared as MustInherit) I would create a test mock (that inherits from MyBaseClass and is located in the test project) and test all what is done in MyBaseClass.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文