在不调用Add方法的情况下测试Remove方法

本文关键字:方法 情况下 测试 Remove 调用 Add | 更新日期: 2023-09-27 18:10:04

我正在为管理标签对象树的类编写测试:

public class Tag
{
    public virtual int Id { get; set; }
    public virtual string Description{ get; set; }
    private IList<Tag> children = new List<Tag>();
    public virtual IEnumerable<Tag> Children
    {
        get {return children .ToArray();}
    }
    public void AddChildTag(Tag child)
    {
        children.Add(child);
    }
    public void RemoveChildTag(Tag child)
    {
        children.Remove(child);
    }
}

正如你可以看到设置父属性的唯一模式是通过AddChildTag方法,这正是我想要的,我的问题是在单元测试:因为每个测试都应该是原子的,我怎么能测试RemoveChildTag方法?

我看到的唯一方法是调用add方法,然后再调用remove方法,但是这样如果add出现一些错误,即使是remove的测试也会失败,所以原子性丢失了。

怎么做呢?

编辑
我从标签对象中删除了父属性,因为我不再使用它根据解决方案使用NUnit和FluentAssertion进行一些测试

    [Test]
    public void AddChildTagAddsChildren()
    {
        //Arrange
        Tag parent = new Tag();
        Tag child = new Tag();
        //Act
        parent.AddChildTag(child);
        //Assert
        parent.Children.Should().Contain(child);
    }
    [Test]
    public void RemoveChildTagRemovesAddedChildren()
    {
        //Arrange
        Tag parent = new Tag();
        Tag child = new Tag();
        parent.AddChildTag(child);
        //Act
        parent.RemoveChildTag(child);
        //Assert
        parent.Children.Should().NotContain(child);
    }
    [Test]
    public void RemoveChildTagThrowsNothingWhenNoChild()
    {
        //Arrange
        Tag parent= new Tag();
        Tag child= new Tag();
        //Act
        Action RemoveChild = () => parent.RemoveChildTag(child);
        //Assert
        RemoveChild.ShouldNotThrow();
    }

在不调用Add方法的情况下测试Remove方法

您的单元测试应该反映您的类的实际用例。您的消费者将如何使用RemoveChildTag方法?哪个更有意义?你是怎么使用对象集合的?

var parent = new Tag();
// later
parent.RemoveChildTag(child);

var parent = new Tag();
parent.AddChildTag(child);
// later
parent.RemoveChildTag(child);

您的消费者将删除他们先前添加的对象。这是您的用例,"Remove Remove先前添加的元素"(注意它也产生了优秀的测试方法名)。

AddRemove方法通常是互补的——你不能只测试一个而不测试另一个。

有几种测试Remove方法:

  1. mock -模拟您的数据结构,并调用remove,以便您可以测试调用正确的方法
  2. 继承-使children受保护。您的测试类将继承Tag类。现在你可以初始化children成员,这样我们就可以测试remove
  3. 使用Add方法

我认为选项3是最好的,在你的单元测试中使用其他方法是可以的,如果Add有一些错误,那么不止一个测试将失败-你应该能够理解你需要修复什么。

同样,每个单元测试都应该测试一些基本行为,即使您需要在此之前做一些准备。如果这些准备工作失败,则使用相关注释使测试失败

选项1 -当你的代码到达第三方服务器,文件系统&更多。因为您不想在单元测试中调用这些调用-模拟响应。

选项2 -我能说的最好的,是当你想测试受保护/私有方法,而不需要做所有的调用"在路上",你的代码做(公共方法,使许多调用最终调用你想测试的方法),因为你只想测试一个特定的逻辑。当您的类有一些想要测试的状态时,也可以很容易地使用此选项,而不需要编写大量代码来将您的类带入该状态。

你可以使用PrivateObject类来安排你的测试对象

允许测试代码调用被测试代码的方法和属性这将是不可访问的,因为它们不是公共的。

编辑

,那么你可以通过PrivateObject获得对包装对象的完全访问。RealType和PrivateObject。目标属性

编辑

无论如何,每个打破类的隔离和封装的系统都会使TDD中单元测试的黑盒方法变得无用,应该作为毒药避免:)

单元测试的一个常用方法是安排-行动-断言范例。

我个人对remove方法有两个测试,一个是删除一个从未添加的子元素,应该发生什么?应该抛出异常吗?

这看起来像:

[Test]
public void RemoveChildTagThrowsExceptionWhenNoChildren()
{
    // Arrange
    var tag = new Tag();
    var tagToRemove = new Tag();
    // Act & Assert
    Expect(() => tag.RemoveChildTag(tagToRemove), Throws.ArgumentException);
}

然后我将有另一个测试,当添加的子节点被删除时应该发生什么:

[Test]
public void RemoveChildTagRemovesPreviouslyAddedChild()
{
    // Arrange
    var tag = new Tag();
    var childTag = new Tag();
    tag.AddChildTag(childTag);
    // Act
    tag.RemoveChildTag(childTag);
    // Assert
    Expect(tag.Children.Contains(childTag), Is.False);
}
值得注意的是,相当多的。net Remove实现返回一个bool结果,该结果表明是否实际执行了任何删除。看到这里。