使用 AutoFixture 创建递归数据结构的固定装置

2024-01-22

我正在开发一个项目,其中有一些递归数据结构,我想为其创建一个固定装置。

数据结构是XmlCommandElement,它有一个单一的方法ToCommand转换XmlCommandElement to Command.

树上的每个节点都可以是XmlCommandElement and/or XmlCommandPropertyElement.

现在,为了测试该方法的行为ToCommand我想取XmlCommandElement一些任意数据。

我想控制树的深度和实例的数量XmlCommandElement and/or XmlCommandPropertyElement每个节点。

这是我用于夹具的代码:

public class XmlCommandElementFixture : ICustomization
{
    private static readonly Fixture _fixture = new Fixture();

    private XmlCommandElement _xmlCommandElement;

    public int MaxCommandsPerDepth { get; set; }

    public int MaxDepth { get; set; }

    public int MaxPropertiesPerCommand { get; set; }

    public XmlCommandElementFixture BuildCommandTree()
    {
        _xmlCommandElement = new XmlCommandElement();

        var tree = new Stack<XmlCommandElementNode>();

        tree.Push(new XmlCommandElementNode(0, _xmlCommandElement));

        while (tree.Count > 0) {
            var node = tree.Pop();
            node.Command.Key = CreateRandomString();
            node.Command.Properties = CreateProperties();

            if (MaxDepth > node.Depth) {
                var commands = new List<XmlCommandElement>();

                for (var i = 0; i < MaxCommandsPerDepth; i++) {
                    var command = new XmlCommandElement();
                    tree.Push(new XmlCommandElementNode(node.Depth + 1, command));
                    commands.Add(command);
                }

                node.Command.Commands = commands.ToArray();
            }
        }

        return this;
    }

    public void Customize(IFixture fixture)
    {
        fixture.Customize<XmlCommandElement>(c => c.FromFactory(() => _xmlCommandElement)
                                                   .OmitAutoProperties());
    }

    private static string CreateRandomString()
    {
        return _fixture.Create<Generator<string>>().First();
    }

    private XmlCommandPropertyElement[] CreateProperties()
    {
        var properties = new List<XmlCommandPropertyElement>();

        for (var i = 0; i < MaxPropertiesPerCommand; i++) {
            properties.Add(new XmlCommandPropertyElement {
                Key = CreateRandomString(),
                Value = CreateRandomString()
            });
        }

        return properties.ToArray();
    }

    private struct XmlCommandElementNode
    {
        public XmlCommandElementNode(int depth, XmlCommandElement xmlCommandElement)
        {
            Depth = depth;

            Command = xmlCommandElement;
        }

        public XmlCommandElement Command { get; }

        public int Depth { get; }
    }
}

这就是我使用它的方式:

xmlCommandElement = new Fixture().Customize(new XmlCommandElementFixture {
    MaxDepth = 2,
    MaxCommandsPerDepth = 3,
    MaxPropertiesPerCommand = 4
}.BuildCommandTree()).Create<XmlCommandElement>();

这工作得很好!但我的问题是它不是generic,至少据我所知,AutoFixture 的全部目的是避免制作特定的装置。

所以我真正想做的是这样的事情(发现它here https://stackoverflow.com/questions/31855842/how-do-i-customize-autofixture-behaviours-for-specific-classes但这对我不起作用。):

var fixture = new Fixture();
fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
       .ToList()
       .ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new DepthThrowingRecursionBehavior(2));
fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(XmlCommandElement), 3));
fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(XmlCommandPropertyElement), 4));

xmlCommandElement = fixture.Create<XmlCommandElement>();

以下是全部代码供参考:

接口:

public interface ICommandCollection : IEnumerable<ICommand>
{
    ICommand this[string commandName] { get; }

    void Add(ICommand command);
}

public interface ICommandPropertyCollection : IEnumerable<ICommandProperty>
{
    string this[string key] { get; }

    void Add(ICommandProperty property);
}

public interface ICommandProperty
{
    string Key { get; }

    string Value { get; }
}

public interface ICommand
{
    ICommandCollection Children { get; set; }

    string Key { get; }

    ICommandPropertyCollection Properties { get; }
}

public interface ICommandConvertible
{
    ICommand ToCommand();
}

Classes:

public sealed class CommandPropertyCollection : ICommandPropertyCollection
{
    private readonly IDictionary<string, ICommandProperty> _properties;

    public CommandPropertyCollection()
    {
        _properties = new ConcurrentDictionary<string, ICommandProperty>();
    }

    public string this[string key]
    {
        get
        {
            ICommandProperty property = null;

            _properties.TryGetValue(key, out property);

            return property.Value;
        }
    }

    public void Add(ICommandProperty property)
    {
        _properties.Add(property.Key, property);
    }

    public IEnumerator<ICommandProperty> GetEnumerator()
    {
        return _properties.Values.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public sealed class CommandProperty : ICommandProperty
{
    public CommandProperty(string key, string value)
    {
        Key = key;

        Value = value;
    }

    public string Key { get; }

    public string Value { get; }
}

public sealed class Command : ICommand
{
    public Command(string key, ICommandPropertyCollection properties)
    {
        Key = key;

        Properties = properties;
    }

    public ICommandCollection Children { get; set; }

    public string Key { get; }

    public ICommandPropertyCollection Properties { get; }
}

public class XmlCommandPropertyElement : ICommandPropertyConvertible
{
    [XmlAttribute("key")]
    public string Key { get; set; }

    [XmlAttribute("value")]
    public string Value { get; set; }

    public ICommandProperty ToCommandProperty()
    {
        return new CommandProperty(Key, Value);
    }
}

最后,我要测试的课程如下:

public class XmlCommandElement : ICommandConvertible
{
    [XmlArray]
    [XmlArrayItem("Command", typeof(XmlCommandElement))]
    public XmlCommandElement[] Commands { get; set; }

    [XmlAttribute("key")]
    public string Key { get; set; }

    [XmlArray]
    [XmlArrayItem("Property", typeof(XmlCommandPropertyElement))]
    public XmlCommandPropertyElement[] Properties { get; set; }

    public ICommand ToCommand()
    {
        ICommandPropertyCollection properties = new CommandPropertyCollection();

        foreach (var property in Properties) {
            properties.Add(property.ToCommandProperty());
        }

        ICommand command = new Command(Key, properties);

        return command;
    }
}

测试本身如下所示:

namespace Yalla.Tests.Commands
{
    using Fixtures;

    using FluentAssertions;

    using Ploeh.AutoFixture;

    using Xbehave;

    using Yalla.Commands;
    using Yalla.Commands.Xml;

    public class XmlCommandElementTests
    {
        [Scenario]
        public void ConvertToCommand(XmlCommandElement xmlCommandElement, ICommand command)
        {
            $"Given an {nameof(XmlCommandElement)}"
                .x(() =>
                {
                    xmlCommandElement = new Fixture().Customize(new XmlCommandElementFixture {
                        MaxDepth = 2,
                        MaxCommandsPerDepth = 3,
                        MaxPropertiesPerCommand = 4
                    }.BuildCommandTree()).Create<XmlCommandElement>();
                });

            $"When the object is converted into {nameof(ICommand)}"
                .x(() => command = xmlCommandElement.ToCommand());

            "Then we need to have a root object with a key"
                .x(() => command.Key.Should().NotBeNullOrEmpty());

            "And 4 properties as its children"
                .x(() => command.Properties.Should().HaveCount(4));
        }
    }
}

感谢马克·西曼!最终的解决方案如下所示:

public class RecursiveCustomization : ICustomization
{
    public int MaxDepth { get; set; }

    public int MaxElements { get; set; }

    public void Customize(IFixture fixture)
    {
        fixture.Behaviors
               .OfType<ThrowingRecursionBehavior>()
               .ToList()
               .ForEach(b => fixture.Behaviors.Remove(b));
        fixture.Behaviors.Add(new OmitOnRecursionBehavior(MaxDepth));
        fixture.RepeatCount = MaxElements;
    }
}

并且可以像这样使用:

xmlCommandElement = new Fixture().Customize(new RecursiveCustomization {
    MaxDepth = 2,
    MaxElements = 3
}).Create<XmlCommandElement>();

您可以通过更改 Fixture 的递归行为相当轻松地创建一棵小树:

[Fact]
public void CreateSmallTree()
{
    var fixture = new Fixture();
    fixture.Behaviors
        .OfType<ThrowingRecursionBehavior>()
        .ToList()
        .ForEach(b => fixture.Behaviors.Remove(b));
    fixture.Behaviors.Add(new OmitOnRecursionBehavior(recursionDepth: 2));

    var xce = fixture.Create<XmlCommandElement>();

    Assert.NotEmpty(xce.Commands);
}

以上测试通过。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 AutoFixture 创建递归数据结构的固定装置 的相关文章

  • C++11 删除重写方法

    Preface 这是一个关于最佳实践的问题 涉及 C 11 中引入的删除运算符的新含义 当应用于覆盖继承父类的虚拟方法的子类时 背景 根据标准 引用的第一个用例是明确禁止调用某些类型的函数 否则转换将是隐式的 例如最新版本第 8 4 3 节
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • 如何在 C++ 中标记字符串?

    Java有一个方便的分割方法 String str The quick brown fox String results str split 在 C 中是否有一种简单的方法可以做到这一点 The 增强分词器 http www boost o
  • 重载 (c)begin/(c)end

    我试图超载 c begin c end类的函数 以便能够调用 C 11 基于范围的 for 循环 它在大多数情况下都有效 但我无法理解和解决其中一个问题 for auto const point fProjectData gt getPoi
  • C# 列表通用扩展方法与非通用扩展方法

    这是一个简单的问题 我希望 集合类中有通用和非通用方法 例如List
  • 使用 C# 中的 CsvHelper 将不同文化的 csv 解析为十进制

    C 中 CsvHelper 解析小数的问题 我创建了一个从 byte 而不是文件获取 csv 文件的类 并且它工作正常 public static List
  • 如何获取 EF 中与组合(键/值)列表匹配的记录?

    我有一个数据库表 其中包含每个用户 年份组合的记录 如何使用 EF 和用户 ID 年份组合列表从数据库获取数据 组合示例 UserId Year 1 2015 1 2016 1 2018 12 2016 12 2019 3 2015 91
  • 结构体的内存大小不同?

    为什么第一种情况不是12 测试环境 最新版本的 gcc 和 clang 64 位 Linux struct desc int parts int nr sizeof desc Output 16 struct desc int parts
  • 使用.Net/C# 计算集合的频率分布

    是否有一种快速 简单的方法来使用 Linq 或其他方式计算 Net 集合的频率分布 例如 任意长的 List 包含许多重复项 遍历列表并计算 跟踪重复次数的巧妙方法是什么 查找列表中重复项的最简单方法是将其分组 如下所示 var dups
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • VB.NET 中的静态方法实现

    我很困惑Static在 VB NET 中的实现 在 C 中 我们可以创建静态类和静态方法来为我们的应用程序编写实用方法 现在 VB NET 让我们创建Module代替静态类 如果我们在模块中创建一个方法 默认情况下它会变成静态的 但在我的应
  • 为什么 isnormal() 说一个值是正常的,而实际上不是?

    include
  • 有没有办法让 doxygen 自动处理未记录的 C 代码?

    通常它会忽略未记录的 C 文件 但我想测试 Callgraph 功能 例如 您知道在不更改 C 文件的情况下解决此问题的方法吗 设置变量EXTRACT ALL YES在你的 Doxyfile 中
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • C++ 继承的内存布局

    如果我有两个类 一个类继承另一个类 并且子类仅包含函数 那么这两个类的内存布局是否相同 e g class Base int a b c class Derived public Base only functions 我读过编译器无法对数
  • 使用特定参数从 SQL 数据库填充组合框

    我在使用参数从 sql server 获取特定值时遇到问题 任何人都可以解释一下为什么它在 winfom 上工作但在 wpf 上不起作用以及我如何修复它 我的代码 private void UpdateItems COMBOBOX1 Ite
  • 当文件流没有新数据时如何防止fgets阻塞

    我有一个popen 执行的函数tail f sometextfile 只要文件流中有数据显然我就可以通过fgets 现在 如果没有新数据来自尾部 fgets 挂起 我试过ferror and feof 无济于事 我怎样才能确定fgets 当
  • 单元测试时 Android Studio 2.0 中测试状态终止且没有任何失败消息

    Issue 我昨天在 Ubuntu 上从 1 5 升级到了 Android Studio 2 0 当我在 Android Studio 2 0 中进行单元测试时 即使所有测试都已通过 它也会显示 终止测试 状态 有时它只显示部分测试通过 我
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template

随机推荐