问题描述
我正在尝试创建一个以某种 AOP 方式工作的自定义属性(不幸的是,我无法访问 postsharp,而且我对 Unity 不太熟悉).它有 AttributeUsage.Method 并在其构造函数中配置测试环境的某些部分(从 app.config 中提取一些信息并调用一些配置环境的 exe).
I'm trying to create a custom attribute that will work in a sort-of AOP way (I don't have access to postsharp, unfortunately and I'm not very familiar with Unity). It has AttributeUsage.Method and in its constructor it configures some parts of the test environment (pulls some info out of app.config and calls some exes that config the environment).
它可以工作,除了现在,当我构建解决方案时,会执行该属性 - 这是不可取的.
It works, except that right now, when I build the solution, the attribute is executed - which is undesireable.
有没有办法创建一个不在编译时执行的自定义属性?
Is there a way to create a custom attribute that isn't executed at compile time?
edit> 我想一个示例用法可能会有所帮助:
edit> I guess an example usage might help:
public void Scenario1Tests
{
[Test]
[Environment(Environments.A)]
public void Scenario1TestA()
{
Assert.Something();
}
[Test]
[Environment(Environments.Any)]
public void Scenario1TestB()
{
Assert.SomethingElse();
}
}
// Most tests will be written environment independent, some must not
public enum Environments
{
A,
B,
Any
};
[AtrributeUsage(AttributeTargets.Method)]
public void Environment : Attribute
{
public Environment(Environments env)
{
// lots of test can have this attribute, requirement is
// that it is only configured once as it is a lengthy configuration
if (this.EnvironmentIsAlreadyConfigured())
return;
this.GetSettingsFromAppConfig();
Process.Start(/* ... */); // can take 20 mins+
}
public Environment()
: this(Environments.Any)
{
}
}
推荐答案
通常的方法是将属性严格用作标记.您在构造函数中配置它,但不执行任何操作.然后,在运行时,您通过反射检查方法,从属性中提取配置信息,并根据该信息采取适当的操作.
The usual method is to use the attribute strictly as a marker. You configure it in the constructor, but take no actions. Then, at runtime, you inspect the methods via reflection, extract the configuration information from the attribute, and take the appropriate actions based on that information.
例如,如果您想要一个在执行方法之前检查参数是否为空的属性,您可以像这样创建它:
For example, if you wanted an attribute that checked parameters for null prior to executing the method, you could create it like so:
[AttributeUsage(AttributeTargets.Method)]
public class CheckArgumentsNullAttribute : Attribute
{
public CheckArgumentsNullAttribute() { }
}
然后,将您的方法标记为:
Then, mark your method as such:
[CheckArgumentsNull]
public Foo(object o) { Console.WriteLine(o.ToString()); }
然后,在你的代码中,通过反射获取Foo
方法,并检查它的属性:
Then, in your code, get the Foo
method via reflection, and check it for the attribute:
MethodInfo m = typeof(FooClass).GetMethod("Foo");
if (m.GetCustomAttributes(typeof(CheckArgumentsNullAttribute), false).Length > 0)
{
// Check parameters for null here
}
更新:由于在 MSTest 运行该方法的场景中需要此功能,您应该查看 这篇文章,讨论了如何挂钩到测试过程.基本上,你需要从ContextBoundObject
进行扩展,拦截方法调用来进行你想要的属性处理.
Update: Since you need this in a scenario where MSTest is running the method, you should take a look at this article, which discusses how to hook into the testing process. Basically, you need to extended from ContextBoundObject
, and intercept the method calls to perform the attribute processing you want.
更新 2:考虑到属性的作用,我可能只是让环境设置一个方法,并从适当的测试开始就调用它.我不认为你通过拥有一个属性获得那么多.或者,您可以按环境划分夹具,并在夹具设置/拆卸中执行环境设置/拆卸.无论哪种方式都可能比在这里尝试使 AOP 工作容易得多,特别是考虑到该属性的只做一次"性质.
Update 2: Given what the attribute does, I would probably just make the environment setup a method, and call it from the beginning of the appropriate tests. I don't think you gain all that much by having an attribute. Alternatively, you could divide your fixtures by environment, and have the environment setup/teardown performed in the fixture setup/teardown. Either way is probably a lot easier than trying to make AOP work here, especially given the "do it once" nature of the attribute.
这篇关于自定义属性在编译时执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!