How to unit test [RequiresAuthentication] DomainService
我在上一篇文章中说了怎么在Silverlight中做单元测试,如果你的客户端Silverlight代码里面调用了WCF RIA Service里面的标记有[RequiresAuthentication]的DomainService,那么你在单元测试这些代码的时候就会有麻烦,因为调用这些DomainService会报错,需要Authentication。(有关Enalbe Authentication in WCF RIA Service可以参考这个MSDN页面)
测试任何调用[RequiresAuthentication] DomainService的类都会遇到这个问题,例如ViewModel
有人说你问什么要测标记有[RequiresAuthentication]的DomainService? 测DomainService逻辑用基本方法就可以了,测[RequiresAuthentication]也没有必要。是的,没错,但是你测试Silverlight的ViewModel可以吗?ViewModel里面调用了[RequiresAuthentication]的DomainService的方法你就不测了吗?如果回答是肯定的,那么你就绕不开这个问题。
举个例子,Silverlight应用用MVVM模式来开发,而我们把关于DomainContext调用WCF RIA Service的部分都从ViewModel分离出去,到Service里面。这样的话我们的Service就会用DomainContext来调用WCF RIA Service,如果RIA Service启用了Authentication,也就是WCF RIA Service里面的标记有[RequiresAuthentication]的DomainService,那么我们测试Service就会失败,因为需要Authenciation。同样,你如果没有Service层而直接把那部分逻辑放到ViewModel中,那么你的ViewModel也是不可测试的。除非你绕开这个问题,用Mock对象,但WCF RIA Service是异步的,模拟的真实度有问题。总之,我们需要搞定Authtication的问题,而不是企图绕开问题。
Exception: Load operation failed for query 'GetProducts'. Access to operation 'GetProducts' was denied.
Load operation failed for query 'GetProducts'. Access to operation 'GetProducts' was denied.
at System.ServiceModel.DomainServices.Client.OperationBase.Complete(Exception error)
at System.ServiceModel.DomainServices.Client.LoadOperation.Complete(Exception error)
at System.ServiceModel.DomainServices.Client.DomainContext.CompleteLoad(IAsyncResult asyncResult)
at System.ServiceModel.DomainServices.Client.DomainContext.<>c__DisplayClass1b.<Load>b__17(Object )
以上是当你的代码调用WCF RIA Service的[RequireAuthentication]的DomainService,测试就会报这个错了
标记有[RequiresAuthentication]的DomainService的例子
1: [EnableClientAccess()]
2: [RequiresAuthentication()]
3: public class AdventureWorksDomainService : LinqToEntitiesDomainService<AdventureWorksLT_DataEntities>
4: {
5: [RequiresRole("Managers")]
6: public IQueryable<Customer> GetCustomers()
7: {
8: return this.ObjectContext.Customers;
9: }
10:
11: public IQueryable<Product> GetProducts()
12: {
13: return this.ObjectContext.Products;
14: }
15:
16: public IQueryable<SalesOrderHeader> GetSalesOrderHeaders()
17: {
18: return this.ObjectContext.SalesOrderHeaders;
19: }
20: }
解决RequireAuthentication的办法
其实思路很明确,就是测试程序一起来就调用用户登录,然后才跑测试用例!
解决办法第一步:配置App.xaml删除LifetimeObjects
删除测试工程的App.xaml的如下内容
1: <Application.ApplicationLifetimeObjects>
2: <app:WebContext>
3: <app:WebContext.Authentication>
4: <appsvc:FormsAuthentication />
5: </app:WebContext.Authentication>
6: </app:WebContext>
7: </Application.ApplicationLifetimeObjects>
解决办法第二步:添加Partial WebContext类
在测试工程的App.Xaml.cs添加一个类
2 {
3 partial void OnCreated();
4 public WebContext()
5 {
6 this.OnCreated();
7 }
8 public new static WebContext Current
9 {
10 get
11 {
12 return ((WebContext)(WebContextBase.Current));
13 }
14 }
15 public new User User
16 {
17 get
18 {
19 return ((User)(base.User));
20 }
21 }
22 }
解决办法第三步:手工创建ApplicationLifetimeObjects
2 {
3 this.Startup += this.Application_Startup;
4 this.Exit += this.Application_Exit;
5 this.UnhandledException += this.Application_UnhandledException;
6
7 InitializeComponent();
8
9 //添加如下代码:************************************
10 WebContext webcontext = new WebContext();//这个是我们刚创建的WebContext
11 webcontext.Authentication = new System.ServiceModel.DomainServices.Client.ApplicationServices.FormsAuthentication();
12 this.ApplicationLifetimeObjects.Add(webcontext);
13 }
解决办法第四步:测试程序启动就自动Authentication
在测试项目的App.xaml.cs中添加如下代码:
2
3 private void Application_Startup(object sender, StartupEventArgs e)
4 {
5 authCtx = new AuthenticationDomainContext();
6
7 WebContext.Current.Authentication = new FormsAuthentication
8 {
9 DomainContext = new AuthenticationDomainContext()
10 };
11
12 AuthenticationService auth = WebContext.Current.Authentication;//刚刚创建的WebContext
13 if (!auth.IsLoggingIn)
14 {
15 auth.Login(new LoginParameters("yourusername", "yourpassword", false, null), LoginCompleted, null);
16 }
17
18 //测试页面等登录完成以后再呈现
19 //RootVisual = UnitTestSystem.CreateTestPage();
20 }
21
22 private void LoginCompleted(LoginOperation loginOperation)
23 {
24 if (loginOperation.LoginSuccess)
25 {
26 //登录成功呈现测试页面
27 RootVisual = UnitTestSystem.CreateTestPage();
28 }
29 }
总结
整个过程非常简单,这样以后我们就可以很方便地测试任何调用需要认证的domain service的类和方法了。(注:如果你不懂Silverlight单元测试、WCF RIA Service、DomainService、RIA Authentication、WebContext、MVVM模式等阅读本文可能会云里雾里,您可以先看看我的本系列其它文章。)