如何使用反射获取处置的实例名称
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 将在我的所有测试 类 中共享。为什么?因为我希望在每次测试结束时发生一些通用逻辑,例如清理。
重点是我需要知道要清理哪个 table,在我的示例中是 MyTable。当 Dispose 方法将 运行 对 MyTableTest 的实例进行处置时,我将通过使用反射来检索此类信息。我怎样才能做到这一点?是否有可能(并且正确地)尝试实现这一目标?提前致谢。
您可以在 DatabaseFixture
class 中有一个 TableName
属性。然后在测试 classes 的构造函数中接收 class 的实例并设置 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 class。
例如
- 您可以这样创建
TableNameAttribute
:
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class TableNameAttribute : Attribute
{
public string Name { get; }
public TableNameAttribute(string name)
{
this.Name = name;
}
}
- 您可以将此属性应用于您的派生夹具 class:
[TableName("MyTable")]
public class MyTableFixture : DatabaseFixture { }
- 您可以在测试中使用该夹具 class
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);
}
}
Is it even possible (and correct) trying to achieve this?
没有。反射不能告诉类型 T
在什么上下文中使用 T
;反射只看到T
的声明。
更具体到你的情况,反射不能告诉类型 DatabaseFixture
它在 MyTableTest
的声明中被用作泛型接口 IClassFixture
的类型参数。也就是说,对于这组声明,
class A { }
class B <T> { }
class C : B<A> { }
A
无法通过反射确定它在C
的声明中使用,但C
可以知道它在A
:
中的用法
typeof(C)
.BaseType // B
.GetGenericArguments()[0] // A
How can I achieve this?
根据您使用 DatabaseFixture
的方式,您可以使用 StackTrace
获得调用测试 class(如果您真的热衷于使用反射)。这是一个简单的例子:
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
不是直接从你的测试 class 调用的,你要么必须知道要传递给 GetFrame(int)
的偏移量,要么你需要搜索每一帧直到找到符合您要求的第一个 DeclaringType
(例如,BaseType
是 IClassFixture
,通用参数 DatabaseFixture
),像这样:
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", "");
否则,您将需要按照 and 的建议手动设置 table 名称。
我正在为我的数据库存储过程构建一些集成测试。 我已经设置了一个 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 将在我的所有测试 类 中共享。为什么?因为我希望在每次测试结束时发生一些通用逻辑,例如清理。
重点是我需要知道要清理哪个 table,在我的示例中是 MyTable。当 Dispose 方法将 运行 对 MyTableTest 的实例进行处置时,我将通过使用反射来检索此类信息。我怎样才能做到这一点?是否有可能(并且正确地)尝试实现这一目标?提前致谢。
您可以在 DatabaseFixture
class 中有一个 TableName
属性。然后在测试 classes 的构造函数中接收 class 的实例并设置 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 class。
例如
- 您可以这样创建
TableNameAttribute
:
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class TableNameAttribute : Attribute
{
public string Name { get; }
public TableNameAttribute(string name)
{
this.Name = name;
}
}
- 您可以将此属性应用于您的派生夹具 class:
[TableName("MyTable")]
public class MyTableFixture : DatabaseFixture { }
- 您可以在测试中使用该夹具 class
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);
}
}
Is it even possible (and correct) trying to achieve this?
没有。反射不能告诉类型 T
在什么上下文中使用 T
;反射只看到T
的声明。
更具体到你的情况,反射不能告诉类型 DatabaseFixture
它在 MyTableTest
的声明中被用作泛型接口 IClassFixture
的类型参数。也就是说,对于这组声明,
class A { }
class B <T> { }
class C : B<A> { }
A
无法通过反射确定它在C
的声明中使用,但C
可以知道它在A
:
typeof(C)
.BaseType // B
.GetGenericArguments()[0] // A
How can I achieve this?
根据您使用 DatabaseFixture
的方式,您可以使用 StackTrace
获得调用测试 class(如果您真的热衷于使用反射)。这是一个简单的例子:
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
不是直接从你的测试 class 调用的,你要么必须知道要传递给 GetFrame(int)
的偏移量,要么你需要搜索每一帧直到找到符合您要求的第一个 DeclaringType
(例如,BaseType
是 IClassFixture
,通用参数 DatabaseFixture
),像这样:
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", "");
否则,您将需要按照