我认为你是相当正确的轨道上,因为你从你的单元测试抽象掉数据库通信和ASP.NET的依赖。不要担心你无法在测试中测试所有的东西。在您的应用程序中始终存在不可测试的代码行。 GetIdentity
就是一个很好的例子。在您的应用程序的某处,您需要与特定于框架的API进行通信,并且您的单元测试无法涵盖此代码。
可能仍有改进的余地,但。虽然未经测试的GetIdentity
不是问题,但它实际上可由应用程序调用。它只是挂在那里,等着有人不小心打电话给它。那么为什么不抽象身份的创造。例如,创建一个知道如何为当前上下文获取正确标识的抽象工厂。你可以注入这个工厂,而不是注入身份本身。这使您可以在应用程序的组合根目录附近定义一个实现,并且可以在应用程序的其余部分之外进行定义。除此之外,代码更清楚地传达了正在发生的事情。没有人需要问“我实际上得到了哪个身份?”,因为他们所称的工厂的方法很明显。
下面是一个例子:
public interface IIdentityProvider
{
// Bit verbose, but veeeery clear,
// but pick another name if you like,
MyIdentity GetIdentityForCurrentUser();
}
在你的作文根,你可以有一个这样的实现:
private sealed class AspNetIdentityProvider : IIdentityProvider
{
public MyIdentity GetIdentityForCurrentUser()
{
// here the code of the MyIdentity.GetIdentity() method.
}
}
作为招有时我有我的测试对象,对工厂和产品两个,只是为了方便在单元测试。例如:
private sealed class FakeMyIdentity
: FakeMyIdentity, IIdentityProvider
{
public MyIdentity GetIdentityForCurrentUser()
{
// just returning itself.
return this;
}
}
这样你可以注入一个FakeMyIdentity在期望一个IIdentityProvider构造。我发现这不会牺牲测试的可读性(这很重要)。 当然你会希望有尽可能少的代码尽可能在AspNetIdentityProvider,因为你不能测试它(自动)。同时确保你的类不依赖任何框架特定的部分。如果是这样,你也需要抽象。
我希望这是有道理的。
+1。如果可能,我同意使用'IPrinciple'接口。这是.NET的核心概念,可以在整个应用程序中使用,而不会有任何问题。 – Steven 2011-03-10 09:08:14
我已经考虑过使用模型绑定器并将它传递给方法,但由于每一次调用(除了我的通用内容控制器中的那些)都需要身份,我觉得它违反了DRY,并认为更好的解决方案是拥有它通过控制器级别传入。 – 2011-03-10 15:29:31