有没有试过在单元测试中调用自己写的WCF服务?
该不会还是添加一个服务引用再测试吧...
这里就介绍一个非常简单的方式直接调用自己的WCF服务。
1.启动服务
首先,需要启动自己的WCF服务,如果服务没起来,在怎么调用也是白搭。
当然这些代码都是大同小异的,所以干脆封装成一个扩展方法:
public static void RunWcfService(this Type type, Action action) { var host = new ServiceHost(type); try { host.Open(); action(); } finally { if (host.State == CommunicationState.Faulted) host.Abort(); else host.Close(); } }
这里直接在类型上添加了一个扩展方法,当这个类型是一个服务的实现,并且配置文件中有相关的配置(需要把config里面的必要信息复制到测试工程的app.config中)时,这个扩展方法可以正常执行。
在action内的执行的代码时,这个服务一直处于打开状态。
2.创建客户端
只有服务端还不行,还需要一个客户端,需要生成客户端的代码吗?
如果还需要生成Client的代理类不是又在自己给自己找麻烦了嘛,别忘了WCF客户端还可以用ChannelFactory来生成:
public static void InvokeWcfClient<TChannel>( this Uri uri, Binding binding, Action<TChannel> action) where TChannel : class { TChannel client = ChannelFactory<TChannel>.CreateChannel( binding, new EndpointAddress(uri)); var co = (ICommunicationObject)client; try { co.Open(); action(client); } finally { if (co.State == CommunicationState.Faulted) co.Abort(); else co.Close(); } }
其中TChannel就是服务契约(也就是通常的那个接口),Binding是服务的Binding方式,如果服务端使用的是标准的Binding,那就直接new那个标准Binding就可以了(例如:NetTcpBinding),Uri是服务配置中侦听的的地址。
3.例子
现在有了这两个扩展方法,看看怎么写单元测试吧:
[ServiceContract] public interface IHelloWorld { [OperationContract] string SayHello(); } public class HelloWorld : IHelloWorld { public string SayHello() { return "Hello world"; } } [TestMethod] public void TestSayHello() { Uri uri = new Uri("net.tcp://localhost:8765/vwxyzh/helloworld/"); typeof(HelloWorld).RunWcfService(() => { uri.InvokeWcfClient<IHelloWorld>(new NetTcpBinding(), client => { Assert.AreEqual("Hello world", client.SayHello()); }); }); }
是不是很简单,当然,别忘了HelloWorld的配置,否则在创建服务的时候会因为找不到配置而导致异常。