我正在使用Castle Windsor的ASP.NET MVC 4项目。其中一个控制器依赖于MembershipService:
public class FooController : Controller
{
private readonly MembershipService MembershipService;
public FooController( MembershipService membershipService )
{
MembershipService = membershipService;
}
[Authorize( Roles = "Administrator" )]
public ActionResult DoSomething()
{
var user = MembershipService.GetUser( User.Identity.Name );
// etc...
}
}
当我开始为这个控制器编写单元测试时(使用Moq),我遇到了一个问题:
private Mock<MembershipService> membershipServiceMock;
[TestInitialize]
public void MyTestInitialize()
{
membershipServiceMock = new Mock<MembershipService>();
}
[TestMethod]
public void DoSomethingReturnsWhatever()
{
var controller = new FooController( membershipServiceMock.Object );
// etc...
}
测试失败,因为Castle抛出异常:
Castle.DynamicProxy.InvalidProxyConstructorArgumentsException: Can not instantiate proxy of class: MembershipService.
Could not find a parameterless constructor.
我决定创建一个MembershipService可以实现的接口,因为这就是我们的应用程序通常是如何设计的:
public interface IMembershipService
{
User GetCurrentUser( string userName );
}
我更新了控制器以使用界面,但现在Castle引发了另一个异常:
[HandlerException: Handler for IMembershipService was not found.]
Castle.MicroKernel.Resolvers.DefaultDependencyResolver.TryGetHandlerFromKernel(DependencyModel dependency, CreationContext context) +210
Castle.MicroKernel.Resolvers.DefaultDependencyResolver.ResolveFromKernelByType(CreationContext context, ComponentModel model, DependencyModel dependency) +38
[DependencyResolverException: Missing dependency.
Component FooController has a dependency on IMembershipService, which could not be resolved.
Make sure the dependency is correctly registered in the container as a service, or provided as inline argument.]
显然我没有在Castle上注册IMembershipService,所以我改变了我的安装程序:
container.Register( Component.For<MembershipService>() );
对此:
container.Register( Component.For( typeof(IMembershipService) )
.ImplementedBy( typeof(MembershipService) ) );
现在Castle引发了另一个例外:
Configuration Error
Parser Error Message: Activation error occurred while trying to get instance of type MembershipService, key ""
这是web.config文件的相关部分。违规行是以add name="MyRoleProvider"
:
<authentication mode="Windows" />
<roleManager enabled="true" defaultProvider="MyRoleProvider">
<providers>
<clear />
<add name="MyRoleProvider" type="MyRoleProvider" applicationName="MyApplication" autoCreateRole="true" autoCreateUser="true" />
<add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
</providers>
</roleManager>
这是因为MyRoleProvider也引用了MembershipService:
public class MyRoleProvider : RoleProvider
{
private MembershipService MembershipService;
public override void Initialize( string name, NameValueCollection config )
{
MembershipService = ServiceLocator.Current.GetInstance<MembershipService>();
// blah blah blah...
}
}
我尝试更改MyRoleProvider中MembershipService字段的类型,如下所示:
public class MyRoleProvider : RoleProvider
{
private IMembershipService MembershipService;
但它仍会抛出相同的异常(“尝试获取MembershipService类型的实例时发生激活错误...”)。到目前为止我管理的唯一方法就是通过像这样注册IMembershipService 和 MembershipService:
container.Register( Component.For<MembershipService>() );
container.Register( Component.For( typeof(IMembershipService) )
.ImplementedBy( typeof(MembershipService) ) );
我很确定我不应该两次注册。那么我怎么能解决这个问题呢?在这一点上,如果我能继续编写单元测试,我会很高兴将应用程序与会员服务紧密结合。但是,如果它不涉及主要的体系结构变化,那么解耦解决方案也会很好。
堆栈追踪:
at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance(Type serviceType, String key) in c:\Projects\CommonServiceLocator\main\Microsoft.Practices.ServiceLocation\ServiceLocatorImplBase.cs:line 53
at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance[TService]() in c:\Projects\CommonServiceLocator\main\Microsoft.Practices.ServiceLocation\ServiceLocatorImplBase.cs:line 90
at MyRoleProvider.Initialize(String name, NameValueCollection config) in d:\Code\MyApplication\MyRoleProvider.cs:line 51
at System.Web.Configuration.ProvidersHelper.InstantiateProvider(ProviderSettings providerSettings, Type providerType)
答案 0 :(得分:1)
您已定义了IMembershipService
public interface IMembershipService
{
User GetCurrentUser( string userName );
}
现在更改你的FooController
public class FooController : Controller
{
private readonly IMembershipService MembershipService;
public FooController( IMembershipService membershipService )
{
MembershipService = membershipService;
}
[Authorize( Roles = "Administrator" )]
public ActionResult DoSomething()
{
var user = MembershipService.GetUser( User.Identity.Name );
// etc...
}
}
转到并将代码MembershipService中的任何位置更改为IMembershipService,如FooController中所示。 现在,在Castle
中注册 IMembershipServicecontainer.Register( Component.For( typeof(IMembershipService) )
.ImplementedBy( typeof(MembershipService) ) );
现在更改单元测试
private Mock<IMembershipService> membershipServiceMock;
[TestInitialize]
public void MyTestInitialize()
{
membershipServiceMock = new Mock<IMembershipService>();
}
[TestMethod]
public void DoSomethingReturnsWhatever()
{
var controller = new FooController( membershipServiceMock.Object );
// etc...
}