需要帮助改进功能命令系统的体系结构

时间:2017-06-07 17:39:15

标签: c# concurrency async-await

给定一组可以同时调用Execute()方法的命令,有没有办法对命令类进行单元测试,以确保代码的其他维护者不会添加成员数据(除了只传入成员数据的readonly成员)构造函数)?

实施例: 代码启动时,在脚本操作和可以为操作提供服务的命令之间创建映射:

actionCommandTable = 
[“Translate”, new TranslateCommand(appcontext)],
[“Scale”, new ScaleCommand(appcontext)],
[“Assignment” new AssignmentCommand(appcontext, expressionEvaluator)]

然后在运行时:

actionCommandTable[“Translate”].Execute(actionDataContext);

翻译命令:

public class TranslateAction : ActionCommand {
        //read-only interface
        private readonly AliasProvider aliasMap;
        //bad - need to prevent this instance from being added - will be overwritten unexpectedly
        private int transientValue; 
        public TranslateAction(IAppContext appContext) {
            aliasMap = appContext.AliasMap;
        }
        public override async Task Execute(ActionDataContext actionDataContext) {
            //assign to transientValue
            //do some work
            //await an animation
            //do more work
            //read from transientValue
        }

    }

在此示例中,如果Execute中的所有工作对共享成员不起作用,则该工作正常。

actionDataContext旨在成为瞬态有状态数据的目标。

1 个答案:

答案 0 :(得分:2)

您可以使用反射来确保所有字段和属性都是只读的。但是,为了验证分配是否正确,您可能必须根据您的要求验证命令值分配

    public void ValidateAllFieldsAreInitOnly(Type sut)
    {
        foreach(var field in sut.GetFields(BindingFlags.NonPublic |
                                     BindingFlags.Instance |
                                     BindingFlags.Static |
                                     BindingFlags.Public))
        {
            Assert.IsTrue(field.IsInitOnly);
        }

        foreach (var property in sut.GetProperties(BindingFlags.NonPublic |
                                     BindingFlags.Instance |
                                     BindingFlags.Static |
                                     BindingFlags.Public))
        {
            // returns true if the property has a set accessor, even if the accessor is private, internal 
            Assert.IsFalse(property.CanWrite);

            // OR can use depending on requirement

            // The MethodInfo object representing the Set method for this property if the set accessor is public, or null if the set accessor is not public.
            Assert.IsNull(property.GetSetMethod());
        }
    }

请注意,当有人将属性声明为只读时,这将不会捕获情况,但是会以错误的方式初始化它 -

的测试绿色
public class GoodCommand
{

    private readonly string privateField1;

    private readonly string privateField2;
    public GoodCommand(string field1, string field2)
    {
        privateField1 = field1;
        privateField2 = field2;
    }
}

测试将捕获(红色)

public class BadCommand
{

    private readonly string privateField1;

    private string privateField2;
    public BadCommand(string field1)
    {
        privateField1 = field1;
        privateField2 = "testingbadCommand";
    }
}

对于此案例,测试为绿色,因为正确的分配检查可能会根据要求而有所不同。您也可以通过验证分配的值是否符合预期来捕获此方案。

public class SmartBadCommand
{
    private readonly string privateField1;

    private readonly string privateField2;
    public SmartBadCommand(string field1)
    {
        privateField1 = field1;
        privateField2 = "testingbadCommand";
    }
}

希望得到这个帮助。