私の WCF サービスクラスの単体テストコードは、いつも大体次のようになっています。
ServiceHost host = new ServiceHost(typeof(FooService)); string address = "net.tcp://localhost/FooService"; host.AddServiceEndpoint( typeof(IFooService), new NetTcpBinding(), address); host.Open(); IFooService proxy = ChannelFactory<IFooService>.CreateChannel( new NetTcpBinding(), new EndpointAddress(address)); proxy.GetFoo(); ((IClientChannel)proxy).Close(); host.Close();
単純なメソッドのテストでも、これだけのコード量です。これが複雑な処理を行うメソッドのテストになったら…。考えただけでもゾッとします。腱鞘炎になってしまうよ!
WCF サービスの場合、どんなテストでも、次の処理は必ず必要です。
- ServiceHost にサービスクラスを登録
- エンドポイントを登録
- サービス起動
- プロキシ生成
- サービスの呼び出し
- プロキシを閉じる
- サービスを閉じる
これらのコードを毎回記述するのはすごく面倒。そこで、「サービスの呼び出し」以外をやってくれるクラスを作ってみました。
public class ServiceScope<TService, TChannel> : IDisposable where TService : class, new() where TChannel : class { private ServiceHost _host; private Binding _binding; private string _address; private TChannel _channel; public ServiceScope(Binding binding) { _host = new ServiceHost(typeof(TService)); _binding=binding; _address = string.Format("{0}://localhost/{1}/{2}", binding.Scheme, typeof(TService).Name, typeof(TChannel).Name); _host.AddServiceEndpoint(typeof(TChannel), binding, _address); _host.Open(); } public TChannel CreateProxy() { _channel = ChannelFactory<TChannel>.CreateChannel( _binding, new EndpointAddress(_address)); return _channel; } public void Dispose() { if (_channel != null) { ((IClientChannel)_channel).Close(); } if (_host != null) { _host.Close(); _host = null; } } }
使い方は次の通り。
using (ServiceScope<FooService, IFooService> scope = new ServiceScope<FooService, IFooService>(new NetTcpBinding())) { // プロキシ作成 var proxy = scope.CreateProxy(); // サービスのメソッド呼び出し proxy.GetFoo(); }
最初のサンプルと比較して、コード量が半分以下になりました。手にも優しいですよ。