2015-05-27 75 views
2

我想将xUnit理论与AutoFixture一起使用来生成匿名对象,但具有某些显式属性。使用AutoFixture定制明确设置标本的多个属性

这是我现在有:

系统下测试

public class Task 
{ 
    public TaskState TaskState { get; set;} 
    public int Progress { get; set; } 
} 

通用定制

public class PropertyCustomization<T> : ICustomization 
{ 
    private readonly string propertyName; 

    private readonly object value; 

    public PropertyCustomization(string propertyName, object value) 
    { 
     this.propertyName = propertyName; 
     this.value = value; 
    } 

    public void Customize(IFixture fixture) 
    { 
     fixture.Customize<T>(cmp => cmp.Do(obj => obj.SetProperty(this.propertyName, this.value))); 
    } 
} 

..

public static void SetProperty(this object instance, string propertyName, object value) 
{ 
    var propertyInfo = instance.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 
    propertyInfo.SetValue(instance, value); 
} 

和属性使用它

[AttributeUsage(AttributeTargets.Parameter)] 
public sealed class AutoTaskAttribute : CustomizeAttribute 
{ 
    private readonly int progress; 

    private readonly TaskState taskState; 

    public AutoTaskAttribute(TaskState taskState, int progress = -1) 
    { 
     this.taskState = taskState; 
     this.progress = progress; 
    } 

    public override ICustomization GetCustomization(ParameterInfo parameter) 
    { 
     if (parameter == null) 
     { 
      throw new ArgumentNullException("parameter"); 
     } 

     var result = new List<ICustomization> { new PropertyCustomization<Task>("TaskState", this.taskState) }; 

     if (this.progress > -1) 
     { 
      result.Add(new PropertyCustomization<Task>("Progress", this.progress)); 
     } 

     return new CompositeCustomization(result); 
    } 
} 

所以,如果我用它来指定像有唯一的国家,它运作良好,并建立匿名任务

[Theory, AutoMoqData] 
public void TestSomething([AutoTask(TaskState.InProgress)]Task task) 
{...} 

但是,如果我想设置州和进步,它才成立第二属性出于某种原因,尽管两个“Do”代表都被调用,但在第二次调用中,它再次接收到具有默认状态的任务。

[Theory, AutoMoqData] 
public void TestSomething([AutoTask(TaskState.InProgress, 50)]Task task) 
{...} 

我怀疑CompositeCustomization多“Do'基于定制的原因,但不知道为什么。

+0

你能包括'TaskState'源代码和'SetProperty'呢? –

+0

“TestSomething”方法如何装饰? –

+0

我更新了初始文章。 SetProperty只是使用反射来简单地设置对象的属性,AutoMoqData属性应用默认AutoMoqCustomization –

回答

1

它不起作用,因为每次下一次通过自定义类型自定义覆盖以前的一个(感谢标记的解释)。

所以我改变的定制配置类型,而不是它的属性:

public class TypeCustomization<T> : ICustomization 
{ 
    private List<Action<T>> actions; 

    public TypeCustomization() 
    { 
     this.actions = new List<Action<T>>(); 
    } 

    public void Customize(IFixture fixture) 
    { 
     fixture.Customize<T>(
      cmp => 
       { 
        return this.actions.Aggregate<Action<T>, IPostprocessComposer<T>>(cmp, (current, next) => current.Do(next)); 
       }); 
    } 

    public TypeCustomization<T> With(string propertyName, object value) 
    { 
     this.actions.Add(obj => obj.SetProperty(propertyName, value)); 
     return this; 
    } 
} 

,它可以在属性定义中使用这样的:

[AttributeUsage(AttributeTargets.Parameter)] 
public sealed class AutoTaskAttribute : CustomizeAttribute 
{ 
    private readonly int progress; 

    private readonly TaskState taskState; 

    public AutoTaskAttribute(TaskState taskState, int progress = -1) 
    { 
     this.taskState = taskState; 
     this.progress = progress; 
    } 

    public override ICustomization GetCustomization(ParameterInfo parameter) 
    { 
     var customization = new TypeCustomization<Task>().With("TaskState", this.taskState); 

     if (this.progress > -1) 
     { 
      customization.With("Progress", this.progress); 
     } 

     return customization; 
    } 
} 
4

你为什么不只是做以下?

[Theory, AutoMoqData] 
public void TestSomething(Task task) 
{ 
    task.TaskState = TaskState.InProgress; 

    // The rest of the test... 
} 

or this?

[Theory, AutoMoqData] 
public void TestSomething(Task task) 
{ 
    task.TaskState = TaskState.InProgress; 
    task.Progress = 50; 

    // The rest of the test... 
} 

这是简单得多,而且类型安全的太...

+0

主要思想是完全(或多或少)在方法参数中移动排列语句。另外我相信[Task(InProgress)]任务通常更具可读性/智能性,可以分解声明和作业。当然,如果有超过2-3个参数设置,值得使用这种方法:) –

+1

...您选择的编程语言*非常好*为属性赋值。为什么比这更难呢? –

+0

使测试更小。当然,如果测试足够简单并且只需要一个任务来分配这两个属性,我会明确地做到这一点。但想象一下,如果您需要以4-5次不同状态和进度的操作,以适当的状态测试任务。当然,你可以在安排阶段明确地做到这一点,但我相信通过属性来做到这一点可能会真正简化进一步的测试阅读和理解。毕竟,C#语法足够好,完全可以在没有xUnit属性的情况下使用,只需要使用显式的fixture实例即可。但我想尝试这种方式 –