Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/279.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在不调用Add方法的情况下测试Remove方法_C#_Unit Testing_Tdd - Fatal编程技术网

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

C# 在不调用Add方法的情况下测试Remove方法,c#,unit-testing,tdd,C#,Unit Testing,Tdd,我正在为一个管理标记对象树的类编写测试: 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 {

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

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

有一些方法可以测试
删除
方法:

  • Mocking-模拟您的数据结构,并调用remove,这样您就可以测试调用正确的方法了
  • 继承-使子项受到保护。您的测试类将继承自
    标记
    类。现在您可以初始化
    子成员
    成员,这样我们就可以测试删除
  • 使用
    Add
    方法
  • 我认为选项3是最好的,在单元测试中使用其他方法也可以,如果
    Add
    出现一些错误,那么超过1个测试将失败-您应该能够理解需要修复的内容

    此外,每个单元测试都应该测试一些基本行为,即使之前需要做一些准备。 如果这些准备工作失败,请给出相关意见,使测试失败

    选项1在您的代码到达第三方(其他服务器、文件系统等)时效果最佳。因为您不想在单元测试中进行这些调用,所以模拟响应

    选项2我能说的最好的情况是,当您想要测试受保护/私有方法时,不需要像代码那样“在途中”进行所有调用(公共方法会进行多次调用,最终调用您想要测试的方法),因为您只想测试特定的逻辑。当您的类具有要测试的某些状态时,也可以很容易地使用此选项,而无需编写大量代码将您的类带入该状态

    您可以使用

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

    编辑

    然后您可以通过和属性获得对包装对象的完全访问权限

    编辑


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

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

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

    var parent = new Tag();
    parent.AddChildTag(child);
    // later
    parent.RemoveChildTag(child);
    
    您的消费者将删除他们以前添加的对象。这是您的用例,“Remove移除先前添加的元素”(注意,它还生成了优秀的测试方法名称)


    Add
    Remove
    方法通常是互补的-您不能在没有其他方法的情况下测试其中一种方法。

    单元测试的常见方法是Arrange-Act-Assert范式

    我个人对移除方法有两个测试,一个是移除一个从未添加的孩子,会发生什么?是否应该抛出异常

    这看起来像:

    [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结果,该结果指示是否实际执行了任何删除。请参阅。

    这可能导致应用程序在不同的流中运行。通过这种方式,未设置child.parent,并可能导致其他错误。(在这种情况下可能不是这样,但在其他情况下可能是这样)@RvdK您完全正确,无论如何,PrivateObject提供了执行所需操作的可能性,而无需复杂的反射模拟操作。我尝试一下:PrivateObject po=新PrivateObject(typeof(Tag));po.SetProperty(“母公司”,母公司);但是现在我如何将这个对象强制转换为一个标记,需要作为参数来调用remove方法呢?谢谢,我同意您(和其他用户)关于封装中断的说明。我试着学习一种新的东西(我不得不说,我不喜欢魔术弦的需要),但我会将mzf或jimmy_keen的答案标记为解决方案。我把+1加到yours@gt.guybrush我同意你的决定:)@gt.guybrush我认为只有这个答案才是正确的答案,为了测试目的打破束缚不是正确的方法mark jimmy_keen naswer作为解决方案只是为了命名方法测试的示例。如果我能标记两个答案,我也会标记你的答案(我可以简单地添加一个+1),我使用一个类似的sintax,但带有Nunit和FluentAssertion(sintax更可读:tag.Children.Should().NotContain(childTag))。bool回归很有趣,但为什么连add都没有呢?对于第一种情况,我不希望抛出异常,只是我不做任何简单的事情。请注意:还值得测试调用
    Remove
    而不调用
    Add
    的行为,以测试是否没有抛出异常,或者是否以正确的状态抛出了正确的异常。因为Remove依赖于
    Add
    ,因此,如果测试通过,您知道
    如果添加有效,则删除有效
    。一旦测试了
    add
    ,您就知道
    remove
    起作用了。@Jonny,更正请参阅我对Lukazoid回答问题的评论,该问题已通过一些测试更新。您的测试看起来不错。对于Fluent,我建议为
    act
    action变量(
    act.ShouldNotThrow()
    vs
    removeChildTag.ShouldNotThrow()
    )提供更多上下文名称。
    [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);
    }