繁体   English   中英

如何使用反射获取实例的名称

[英]How to get the name of the instance disposed by using reflection

我正在为我的数据库存储过程构建一些集成测试。 我已经设置了一个 xUnit 项目并实现了 Fixture 模式。 给你看:

public class MyTableTest : IClassFixture<DatabaseFixture>
{

    public MyTableTest()
    {
        //DO SOMETHING
    }

    [Fact]
    public void Test()
    {
        //DO SOMETHING
    }
}

和:

public class DatabaseFixture : IDisposable
{
    public void Dispose()
    {
        // ... clean up test data from the database ...
    }
}

这个DatabaseFixture将在我的所有测试类中共享。 为什么? 因为我希望在每次测试结束时发生一些常见的逻辑,例如清理。

重点是我需要知道要清理哪个表,在我的示例中是MyTable 当 Dispose 方法将针对正在处理的MyTableTest实例运行时,我将使用反射检索此类信息。 我怎样才能做到这一点? 是否有可能(并且正确)尝试实现这一目标? 提前致谢。

您可以在DatabaseFixture类中有一个TableName属性。 然后在测试类的构造函数中接收该类的实例并设置该TableName属性。 稍后您可以在 dispose 中使用它进行一些清理。

public class MyTableTest : IClassFixture<DatabaseFixture>
{
    DatabaseFixture databaseFixture;
    public MyTableTest(DatabaseFixture databaseFixture)
    {
        this.databaseFixture = databaseFixture;
        databaseFixture.TableName = "MyTable";
    }

    [Fact]
    public void Test()
    {
    }
}
public class DatabaseFixture : IDisposable
{
    //...

    public string TableName { get; set; }

    //...
    public void Dispose()
    {
        // Cleanup based on TableName
    }
}

要了解有关在 xUnit 中共享上下文的更多信息,请查看:

您可以使用自定义属性将任意数据附加到您的派生 Fixture 类。

例如

  1. 您可以像这样创建一个TableNameAttribute
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class TableNameAttribute : Attribute
{
    public string Name { get; }
    public TableNameAttribute(string name)
    {
        this.Name = name;
    } 
}
  1. 您可以将此属性应用于派生的夹具类:
[TableName("MyTable")]
public class MyTableFixture : DatabaseFixture { }
  1. 您可以在测试中使用该夹具类
public class MyTableTest : IClassFixture<MyTableFixture>
{
    [Fact]
    public void Test()
    {
        //DO SOMETHING
    }
}

最后,这是从Dispose方法中检索Name的方法:

public abstract class DatabaseFixture : IDisposable
{
    ...
    public void Dispose()
    {
        var attribute = this.GetType().GetCustomAttribute(typeof(TableNameAttribute));
        if (attribute is TableNameAttribute tableNameAttr)
            Console.WriteLine(tableNameAttr.Name);
    }
}

是否有可能(并且正确)尝试实现这一目标?

不。反射不能告诉类型T在什么上下文中使用T 反射只看到T的声明。
更具体到你的情况,反映不能告诉类型DatabaseFixture ,它被用作泛型接口的类型参数IClassFixture在声明MyTableTest 换句话说,对于这组声明,

class A { }
class B <T> {  }
class C : B<A> { }

A不能反射性地确定它在C的声明中使用,但C可以知道它对A用法:

typeof(C)
    .BaseType // B
    .GetGenericArguments()[0] // A

我怎样才能做到这一点?

根据您使用DatabaseFixture ,您可以使用StackTrace获取调用测试类(如果您真的很想使用反射)。 这是一个简单的例子:

    public class DisposableObject : System.IDisposable
    {
        public void Dispose()
        {
            var stack = new System.Diagnostics.StackTrace();

            // This will log the name of the class that instantiated and disposed this. 
            System.Console.WriteLine(stack.GetFrame(1).GetMethod().DeclaringType.Name);
            return;
        }
    }

如果您DatabaseFixture不直接从您的测试类调用,你将不得不知道的偏移量传递到GetFrame(int)或者你需要搜索每个帧,直到找到第一个DeclaringType符合您的要求(例如, BaseType是带有通用参数DatabaseFixture IClassFixture ),如下所示:

System.Type testClassType = new StackTrace()
    .GetFrames()
    .Where(f =>
    {
        System.Type baseType = f.GetMethod().DeclaringType.BaseType;
        return typeof(IClassFixture<DatabaseFixture>).IsAssignableFrom(baseType);
    })
    .FirstOrDefault() // First matching result (assuming you found any)
    ?.GetMethod() // Get the reflected Method
    .DeclaringType; // Get the type (e.g. class) that declares this method.
string tableName = testClassType.Name.Replace("Test", "");

否则,您将需要按照RezaPeter 的建议手动设置表名。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM