Hol*_*roe 13 c# unit-testing autofixture
我刚刚开始使用AutoFixture并拥有这个半复杂的数据结构,我想创建一些标本.在我正在使用的测试中,我不太关心数据结构的内容.我只想要合理的默认值.
此数据结构的一部分是递归树.更具体地说,一个类包含一些其他类的集合,其中包含自身的子列表.类似于:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
让我们假设我出于各种原因无法轻易改变这种结构.
如果我要求我的灯具创建一个ThrowingRecursionBehavior将开始咆哮B是递归的.
如果我用OmitOnRecursionBehavior替换ThrowingRecursionBehavior,我会得到一个ObjectCreateException.
如果我尝试类似:fixture.Inject(Enumerable.Empty()); 我从DictionaryFiller中得到"已添加相同键的项目".如果我用NullRecursionBehavior替换ThrowingRecursionBehavior,也会发生同样的事情.
我想要几件事.
对于我的遗愿,指定一些递归深度可能会很好,在此之后使用Enumerable.Empty(或零大小的数组/ List或甚至为null).我知道AutoFixture可以非常灵活地扩展.因此,我认为应该可以创建一些完全符合这一要求的样本构建器.事实上,我会尝试使用自定义的ISpecimenBuilder,但也许有人已经有了一个更智能的解决方案.例如,在RecursionGuard中修改此行是否有意义:
public object Create(object request, ISpecimenContext context)
{
if (this.monitoredRequests.Any(x => this.comparer.Equals(x, request)))
...
Run Code Online (Sandbox Code Playgroud)
至
public object Create(object request, ISpecimenContext context)
{
if (this.monitoredRequests.Count(x => this.comparer.Equals(x, request)) > maxAllowedRecursions)
...
Run Code Online (Sandbox Code Playgroud)
Mar*_*ann 11
使用空的B列表创建A.
使用空的B列表创建A的实例很容易:
var fixture = new Fixture();
fixture.Inject(Enumerable.Empty<B>());
var a = fixture.Create<A>();
Run Code Online (Sandbox Code Playgroud)
创建一个小树
创建一棵小树要困难得多,但这是可能的.你已经开始考虑你的想法了RecursionGuard.为了验证这是否可行,我复制了大部分代码RecursionGuard并将其创建DepthRecursionGuard为概念证明:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
注意方法的更改实现Create,以及IEnumerable<B>in 的具体处理HandleRecursiveRequest.
为了使这个Fixture实例可用,我还添加了这个DepthRecursionBehavior:
public class DepthRecursionBehavior : ISpecimenBuilderTransformation
{
public ISpecimenBuilder Transform(ISpecimenBuilder builder)
{
return new DepthRecursionGuard(builder);
}
}
Run Code Online (Sandbox Code Playgroud)
这使我能够创建一个小树:
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>();
Run Code Online (Sandbox Code Playgroud)
虽然这是可能的,但在我看来,这太难了,所以我创建了一个工作项,以便将来更容易.
更新2013.11.13:从AutoFixture 3.13.0,可以通过该API配置递归深度.
| 归档时间: |
|
| 查看次数: |
3364 次 |
| 最近记录: |