用于测试私有方法的非代码生成的转发垫片
一般来说,我设计类的方式不需要为了测试目的而访问私有变量。 InternalsVisibleTo
也可以提供帮助。
然而,我目前正在处理的代码库有一些区域已经变得依赖于[VSTS中的私有访问器机制](http://msdn.microsoft.com/en-us/library/ms184807(VS.80).aspx) (即,使用VSCodeGenAccessors
用于生成 *_Accessor
类,这些类具有使用反射调用 private
成员(也可以选择使用 internal
成员)的转发功能)
所以我有这样的代码:(
ClassUnderTest target = new ClassUnderTest();
var accessor = ClassUnderTest_Accessor.AttachShadow( target );
accessor.PrivateMethod();
Assert.True( accessor._privateMethodWasCalled);
accessor.PrivateProperty = 5;
Assert.Equal( accessor.PrivateProperty, 5);
是的,充满了反模式 - 但请不要射击信使)
我对此有很多问题:
- 我希望能够澄清我需要哪些私人信息
- 我不想调用对话框(是的,我是一个 CRaholic)
- 我不想在图片中涉及代码生成
所以我希望能够将上面的代码转换为类似的东西
var target = new ClassUnderTest();
IClassUnderTestInterface accessor = Shadow.Create<IClassUnderTestInterface>( target );
accessor.PrivateMethod();
Assert.True( accessor._privateMethodWasCalled);
accessor.PrivateProperty = 5;
Assert.Equal( accessor.PrivateProperty, 5);
:以下接口位于我的测试程序集中,并且没有生成代码或自定义构建步骤:-
interface IClassUnderTestInterface
{
int PrivateProperty {get; set;}
bool _privateMethodWasCalled {get; }
void PrivateMethod();
}
从那里,我只需击键即可使用 CodeRush 或 Ctrl KM 在接口上生成新的影子方法。
缺少的一点是有一个方法 I Shadow.Create(Object o)
,它会 1.生成实现接口的动态代理 1. 验证要包装的对象o
是否具有接口规定的所有成员 1. bnous:正确管理表示字段的属性的转发(即“_privateMethodWasCalled”情况)
所以,有人知道实现这样的东西的库(或者觉得写它很无聊吗?)
一个明显的缺点是你不知道直到运行时才知道接口是否与 ClassUnderTest 兼容,但这没关系,因为这仅用于测试。 此外,AIUI(私有访问器机制)也需要触发重新编译以不时同步内容。
或者我缺少更好的方法吗? (记住,我不想将所有私有的升级为内部或公共,也不想重写工作代码)
使用 xUnit.net、.NET 3.5; 开放使用任何动态代理库或其他
In general, I design classes in such a manner as to not require access to privates for testing purposes. An InternalsVisibleTo
can also assist.
However, I'm currently dealing with a codebase that has a few area which have become reliant on the [private accessors mechanism in VSTS](http://msdn.microsoft.com/en-us/library/ms184807(VS.80).aspx) (i.e., using VSCodeGenAccessors
to generate *_Accessor
classes which have forwarding that use reflection to invoke private
members (and optionally internal
ones too) on a class.
So I've got code like:
ClassUnderTest target = new ClassUnderTest();
var accessor = ClassUnderTest_Accessor.AttachShadow( target );
accessor.PrivateMethod();
Assert.True( accessor._privateMethodWasCalled);
accessor.PrivateProperty = 5;
Assert.Equal( accessor.PrivateProperty, 5);
(Yes, riddled with antipatterns - but please dont shoot the messenger)
I have a number of problems with this:
- I'd like to be able to clarify which I privates I need
- I'd prefer not to be invoking dialogs (Yes, I'm a CRaholic)
- I'd prefer not to involve code generation in the picture
So I'd like to be able to transform the above code to something like:
var target = new ClassUnderTest();
IClassUnderTestInterface accessor = Shadow.Create<IClassUnderTestInterface>( target );
accessor.PrivateMethod();
Assert.True( accessor._privateMethodWasCalled);
accessor.PrivateProperty = 5;
Assert.Equal( accessor.PrivateProperty, 5);
With only the following interface sitting in my test assembly, and no generated code or custom build steps :-
interface IClassUnderTestInterface
{
int PrivateProperty {get; set;}
bool _privateMethodWasCalled {get; }
void PrivateMethod();
}
From there, I'd be able to use CodeRush or Ctrl K M to generate new shadow methods onto the interface with only a keystroke.
The missing bit would be having a method I Shadow.Create<I>( Object o)
which would
1. generate a dynamic proxy that implements the interface
1. verify that the object o
to be wrapped has all the members dictated by the interface
1. bnous: manage the forwarding of properties representing fields (i.e., the `_privateMethodWasCalled' case) correctly
So, does anyone know a library that implements something like this (or feel bored enough to write it?)
One obvious drawback is that youo dont know if the interface isnt commpatible with the ClassUnderTest until runtime, but that's OK as this would only be for tests. Also AIUI, the private accessors mechanism also requires the triggering of a recompile to sync stuff up from time to time.
Or is there a way better approach I'm missing? (Remembering I dont want to go blanket ugrading al privates to internal or public and dont want to have to rewrite working code)
Using xUnit.net, .NET 3.5; Open to using any dynamic proxy library or other
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你看过像 Moq 或 Rhino 这样的模拟框架吗? 就您而言,如果您愿意将需要测试的私有更改为“受保护的虚拟”(这并不像公开内部那么糟糕),他们可以提供帮助。 基本上,如果成员是虚拟的,那么模拟框架可以生成一个子类来记录调用的成员。
Have you looked at mocking frameworks like Moq or Rhino? In your case they could help IF you are willing to change the privates that your need to test to 'protected virtual' (which is not as bad a going public of internal). Basically, if the member is virtual, then mocking framework can generate a subclass that records what members are called.
我最终通过合并以下内容来绕过所有这些内容(
InternalsVisibleTo
、单独的测试项目、测试引用、私有访问器、MSBuild Shadow 任务/publicize.exe 在构建中随机失败、使用反射访问私有)测试到主项目中,并制作需要从测试中访问的任何内容内部
)。 另请参阅 xUnit。 net Test Stripper [在部署/发布之前删除嵌入在二进制文件中的测试代码]I ended up routing around all this stuff (
InternalsVisibleTo
, separate test projects, Test References, private accessors, the MSBuild Shadow task / publicize.exe failing randomly in builds, using reflection to access privates) by merging the tests into the main projects and making anything that needed to be accessed from testsinternal
). See also xUnit.net Test Stripper [to remove test code embedded in binaries prior to deployment/shipping]这个巧妙的技巧可以使转发功能更加简洁,尽管本质上仍然是手动的
另外,这个答案涵盖了如何处理
静态
成员(但需要CLR4,在我询问时它还没有运行,并且在不带InternalsVisibleTo<的情况下不处理私有方法/code> 混合)。
This neat trick that can make forwarding functions more succint, although its still manual in nature
Also, this answer covers how to handle
static
members (but requires CLR4 which wasnt in play at the time of my asking and doesn't handle private methods without bringingInternalsVisibleTo
into the mix).