创建具有AutoFixture递归树 [英] Creating recursive tree with AutoFixture

查看:117
本文介绍了创建具有AutoFixture递归树的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚开始使用AutoFixture和有这种半复杂的数据结构,我想为创造一些标本。
在我与我的工作测试没太在意数据结构的内容。我只是想合理的默认值。

这个数据结构的一部分是递归的树。更具体而言,一个类认为包含本身孩子的列表的一些其它类的集合。
一个类似于:

 大众A级
{
   私人的IEnumerable< B> bNodes;
   大众A(IEnumerable的< B> bNodes)
   {
      this.bNodes = bNodes;
   }
}大众B级
{
   私人的IEnumerable< B>儿童;
   大众B(IEnumerable的< B>儿童)
   {
      this.children =儿童;
   }
}

让我们假设我不能轻易改变这个结构因各种原因。

如果我问我的夹具,共创ThrowingRecursionBehavior将开始狂叫关于B是递归的。

如果我OmitOnRecursionBehavior取代ThrowingRecursionBehavior我得到一个ObjectCreateException。

如果我尝试类似:fixture.Inject(Enumerable.Empty());我得到具有相同键的项已被添加,从DictionaryFiller。如果我NullRecursionBehavior取代ThrowingRecursionBehavior同样的事情发生。

有几件事情,我想。


  • 什么是创建A的样品符合BS的空列表?
  • 的最佳方法
  • 什么是创建A的一个样本含有一些B-儿童几个孩子一些烧烤(小树)的最好方法?

有关我最后的愿望可能是好的指定一些递归深度使用Enumerable.Empty后(或零大小的数组/列表甚至可以为null)。
我知道AutoFixture是非常灵活的扩展。所以我想应该可以创造一些样本生成器,正是这一点。
事实上,我会尽量打打闹闹与自定义ISpecimenBuilder,但也许有人有一个聪明的解决方案了。
例如,它将使意义修改此行RecursionGuard:

 公共对象创建(对象请求,ISpecimenContext上下文)
{
   如果(this.monitoredRequests.Any(X => this.comparer.Equals(X,请求)))
   ...

 公共对象创建(对象请求,ISpecimenContext上下文)
{
   如果(this.monitoredRequests.Count(X => this.comparer.Equals(X,请求))> maxAllowedRecursions)
   ...


解决方案

符合BS的一个空列表创建

这很容易与B的空列表创建一个实例:

  VAR夹具=新灯();
fixture.Inject(Enumerable.Empty< B>());VAR一个= fixture.Create< A>();

创建一棵小树上

它更难以创造了一棵小树,但它是可能的。你已经在轨道与你的 RecursionGuard 的思考。为了验证是否这可能是工作,我复制大多code从 RecursionGuard 并创造了这个 DepthRecursionGuard 作为概念的证明的:

 公共类DepthRecursionGuard:ISpecimenBuilderNode
{
    私人只读ISpecimenBuilder建设者;
    私人只读堆栈<对象> monitoredRequests;    公共DepthRecursionGuard(ISpecimenBuilder建设者)
    {
        如果(建设者== NULL)
        {
            抛出新的ArgumentNullException(助剂);
        }        this.monitoredRequests =新的堆栈<对象>();
        this.builder =建设者;
    }    公共对象创建(对象请求,ISpecimenContext上下文)
    {
        如果(this.monitoredRequests.Count(request.Equals)→1)
            返回this.HandleRecursiveRequest(请求);        this.monitoredRequests.Push(请求);
        VAR标本= this.builder.Create(请求上下文);
        this.monitoredRequests.Pop();
        返回标本;
    }    私有对象HandleRecursiveRequest(对象请求)
    {
        如果(typeof运算(IEnumerable的< B>)等于(申请)。)
            返回Enumerable.Empty< B>();        抛出新的InvalidOperationException异常(嘶嘘声!);
    }    公共ISpecimenBuilderNode撰写(IEnumerable的< ISpecimenBuilder>建设者)
    {
        VAR建设者= ComposeIfMultiple(建筑商);
        返回新DepthRecursionGuard(制造商);
    }    公共虚拟的IEnumerator< ISpecimenBuilder>的GetEnumerator()
    {
        产生回报this.builder;
    }    的IEnumerator IEnumerable.GetEnumerator()
    {
        返回this.GetEnumerator();
    }    私有静态ISpecimenBuilder ComposeIfMultiple(
        IEnumerable的< ISpecimenBuilder>建设者)
    {
        VAR isSingle = builders.Take(2).Count之间的()== 1;
        如果(isSingle)
            返回builders.Single();        返回新CompositeSpecimenBuilder(建筑商);
    }
}

注意改变实施创建方法,以及的IEnumerable&LT的具体处理; B> HandleRecursiveRequest

为了使从灯具实例此使用,我还添加了这个 DepthRecursionBehavior

 公共类DepthRecursionBehavior:ISpecimenBuilderTransformation
{
    公共ISpecimenBuilder变换(ISpecimenBuilder建设者)
    {
        返回新DepthRecursionGuard(制造商);
    }
}

这使我能够创建一个小的树:

  VAR夹具=新灯();
fixture.Behaviors.OfType< ThrowingRecursionBehavior>()
    。.ToList()的ForEach(B => fixture.Behaviors.Remove(B));
fixture.Behaviors.Add(新DepthRecursionBehavior());VAR一个= fixture.Create< A>();

虽然这是可能的,这是,在我看来,太辛苦了,所以我创建工作项目使其在未来更容易。


更新2013年11月13日:从AutoFixture 3.13.0,递归深度可以通过该API配置

I have just started using AutoFixture and have this semi-complex data structure that I would like to create some specimen for. In the tests I am working with I don't care too much about content of the data structure. I just want reasonable default values.

Part of this data structure is a recursive tree. More specific, one class holds a collection of some other class that contains a list of children of itself. Something akin to:

public class A
{
   private IEnumerable<B> bNodes;
   public A(IEnumerable<B> bNodes)
   {
      this.bNodes = bNodes;
   }
}

public class B
{
   private IEnumerable<B> children;
   public B(IEnumerable<B> children)
   {
      this.children = children;
   }
}

Lets assume I cannot easily change this structure for various reasons.

If I ask my fixture to create A ThrowingRecursionBehavior will start barking about B being recursive.

If I replace ThrowingRecursionBehavior with OmitOnRecursionBehavior I get an ObjectCreateException.

If I try something like: fixture.Inject(Enumerable.Empty()); I get "An item with the same key has already been added" from the DictionaryFiller. The same thing happens if I replace ThrowingRecursionBehavior with NullRecursionBehavior.

There are several things I would like to.

  • What would be the best way to create a specimen of A with an empty list of Bs?
  • What would be the best way to create a specimen of A with a few Bs containing a few B-children with a few children (a small tree)?

For my last wish it could be nice to specify some recursion depth after which Enumerable.Empty was used (or a zero sized array / List or even null). I know that AutoFixture is very flexible to extend. So I suppose it should be possible to create some specimen builder that does exactly this. In fact I will try fooling around with a custom ISpecimenBuilder, but perhaps someone has a smarter solution already. For example, would it make sense to modify this line in RecursionGuard:

public object Create(object request, ISpecimenContext context)
{
   if (this.monitoredRequests.Any(x => this.comparer.Equals(x, request)))
   ...

to

public object Create(object request, ISpecimenContext context)
{
   if (this.monitoredRequests.Count(x => this.comparer.Equals(x, request)) > maxAllowedRecursions)
   ...

解决方案

Creating A with an empty list of Bs

It's easy to create an instance of A with an empty list of Bs:

var fixture = new Fixture();
fixture.Inject(Enumerable.Empty<B>());

var a = fixture.Create<A>();

Creating a small tree

It's much more difficult to create a small tree, but it's possible. You're already on track with your thinking about RecursionGuard. In order to verify if this could work, I copied most of the code from RecursionGuard and created this DepthRecursionGuard as a proof of concept:

public class DepthRecursionGuard : ISpecimenBuilderNode
{
    private readonly ISpecimenBuilder builder;
    private readonly Stack<object> monitoredRequests;

    public DepthRecursionGuard(ISpecimenBuilder builder)
    {
        if (builder == null)
        {
            throw new ArgumentNullException("builder");
        }

        this.monitoredRequests = new Stack<object>();
        this.builder = builder;
    }

    public object Create(object request, ISpecimenContext context)
    {
        if (this.monitoredRequests.Count(request.Equals) > 1)
            return this.HandleRecursiveRequest(request);

        this.monitoredRequests.Push(request);
        var specimen = this.builder.Create(request, context);
        this.monitoredRequests.Pop();
        return specimen;
    }

    private object HandleRecursiveRequest(object request)
    {
        if (typeof(IEnumerable<B>).Equals(request))
            return Enumerable.Empty<B>();

        throw new InvalidOperationException("boo hiss!");
    }

    public ISpecimenBuilderNode Compose(IEnumerable<ISpecimenBuilder> builders)
    {
        var builder = ComposeIfMultiple(builders);
        return new DepthRecursionGuard(builder);
    }

    public virtual IEnumerator<ISpecimenBuilder> GetEnumerator()
    {
        yield return this.builder;
    }

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

    private static ISpecimenBuilder ComposeIfMultiple(
        IEnumerable<ISpecimenBuilder> builders)
    {
        var isSingle = builders.Take(2).Count() == 1;
        if (isSingle)
            return builders.Single();

        return new CompositeSpecimenBuilder(builders);
    }
}

Notice the changed implementation of the Create method, as well as the specific handling of IEnumerable<B> in HandleRecursiveRequest.

In order to make this usable from a Fixture instance, I also added this DepthRecursionBehavior:

public class DepthRecursionBehavior : ISpecimenBuilderTransformation
{
    public ISpecimenBuilder Transform(ISpecimenBuilder builder)
    {
        return new DepthRecursionGuard(builder);
    }
}

This enabled me to create a small tree:

var fixture = new Fixture();
fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
    .ToList().ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new DepthRecursionBehavior());

var a = fixture.Create<A>();

While this is possible, it's, in my opinion, too hard, so I've created a work item to make it easier in the future.


Update 2013.11.13: From AutoFixture 3.13.0, the recursion depth can be configured via that API.

这篇关于创建具有AutoFixture递归树的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆