In my last post about testing WCF, I described how to easily test a server side component which is exposed by WCF. And obviously, the next step is to go to the client side. For this to work you don’t need configuration files, or any other definition. Isolator just needs type definition.
So here’s a consumer of my service:
1 |
<span class="kwrd">public</span> <span class="kwrd">class</span> DataProviderClient<br />{<br /> ISourceListProvider channel;<br /><br /> <span class="kwrd">public</span> DataProviderClient()<br /> {<br /> RetrieveChannel();<br /> }<br /><br /> <span class="kwrd">private</span> <span class="kwrd">void</span> RetrieveChannel()<br /> {<br /> ChannelFactory<ISourceListProvider> factory = GetFactory();<br /> channel = factory.CreateChannel();<br /> }<br /><br /> <span class="kwrd">private</span> ChannelFactory<ISourceListProvider> GetFactory()<br /> {<br /> EndpointAddress address = <span class="kwrd">new</span> EndpointAddress(<span class="str">"http://AnyServer/service.svc"</span>);<br /> WSHttpBinding binding = <span class="kwrd">new</span> WSHttpBinding();<br /> <span class="kwrd">return</span> <span class="kwrd">new</span> ChannelFactory<ISourceListProvider>(binding, address);<br /> } |
1 |
<span class="kwrd">public</span> <span class="kwrd">string</span> GetSourceListFromServer(<span class="kwrd">string</span> source)<br /> {<br /> <span class="kwrd">string</span> result;<br /> <span class="kwrd">try</span><br /> {<br /> result = channel.GetSourceList(source);<br /> }<br /> <span class="kwrd">catch</span> (CommunicationException)<br /> {<br /> <span class="kwrd">return</span> <span class="str">"Error"</span>;<br /> }<br /> <span class="kwrd">if</span> (result == <span class="kwrd">string</span>.Empty )<br /> <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentException(<span class="str">"Bad Result"</span>);<br /> <span class="kwrd">return</span> result;<br /> }<br />} |
The client, at construction, connects to the service, and at the GetSourceListServer, calls it, and performs a simple logic to see if the result has any meaning.
Note the code is refactored a bit. I could go on and fake all the nasty little WCF objects, but by extracting them into a single method, I just fake this method, making my life easier, and my tests more readable, and less fragile.
Let’s start with a simple test – fake the channel, and verify that the correct argument was sent to the server:
1 |
[TestMethod]<br />[Isolated]<br /><span class="kwrd">public</span> <span class="kwrd">void</span> FakeChannel_VerifyThatCallToChannelOccurs()<br />{<br /> ISourceListProvider sourceListProvider = Isolate.Fake.Instance<ISourceListProvider>();<br /> Isolate.WhenCalled(()=>sourceListProvider.GetSourceList (<span class="str">"AnyValue"</span>)).WillReturn(<span class="str">"<XML></XML>"</span>);<br /><br /> SetupFakeChannelFactory(sourceListProvider);<br /><br /> DataProviderClient client = <span class="kwrd">new</span> DataProviderClient();<br /> client.GetSourceListServer(<span class="str">"MySource"</span>);<br /><br /> Isolate.Verify.WasCalledWithExactArguments(()=> sourceListProvider.GetSourceList(<span class="str">"MySource"</span>));<br />} |
And the SetupFakeChannelFactory private method looks like that:
1 |
<span class="kwrd">private</span> <span class="kwrd">void</span> SetupFakeChannelFactory(ISourceListProvider provider)<br />{<br /> ChannelFactory<ISourceListProvider> fakeFactory = Isolate.Fake.Instance<ChannelFactory<ISourceListProvider>>();<br /> Isolate.SwapNextInstance<ChannelFactory<ISourceListProvider>>().With(fakeFactory);<br /> Isolate.WhenCalled(()=>fakeFactory.CreateChannel()).WillReturn(provider);<br />} |
The SwapNextInstance replaces the Swap method (based on customer feedback, who found this clearer), but does exactly the same: It sets up the fakeFactory to be swapped with the next instance of this type that will be created (this will happen inside the client’s constructor). The next line sets a return value as the provider. Note that our provider is not a concrete type – it’s a fake interface, representing our service.
I can of course test what happens when an incorrect result is returned from the server (I expect an exception in this case):
1 |
[TestMethod]<br />[ExpectedException(<span class="kwrd">typeof</span>(ArgumentException))]<br />[Isolated]<br /><span class="kwrd">public</span> <span class="kwrd">void</span> FakeChannel_SimulateEmptyReturn()<br />{<br /> ISourceListProvider sourceListProvider = Isolate.Fake.Instance<ISourceListProvider>();<br /> Isolate.WhenCalled(()=>sourceListProvider.GetSourceList(<span class="str">"AnyValue"</span>)).WillReturn(<span class="kwrd">string</span>.Empty);<br /><br /> SetupFakeChannelFactory(sourceListProvider);<br /><br /> DataProviderClient client = <span class="kwrd">new</span> DataProviderClient();<br /> client.GetSourceListServer(<span class="str">"MySource"</span>);<br />} |
But if I want to test that something occurred on the wire, I can inject a CommunicationException (which causes an “Error” to be returned):
1 |
[TestMethod]<br />[Isolated]<br /><span class="kwrd">public</span> <span class="kwrd">void</span> GetSourceListFromServer_SimulateCommunicationException_ReturnError()<br />{<br /> ISourceListProvider sourceListProvider = Isolate.Fake.Instance<ISourceListProvider>();<br /> Isolate.WhenCalled(()=><br /> sourceListProvider.GetSourceList(<span class="str">"AnyValue"</span>))<br /> .WillThrow(<span class="kwrd">new</span> CommunicationException());<br /><br /> InjectFakeProviderIntoFactory(sourceListProvider);<br /><br /> DataProviderClient client = <span class="kwrd">new</span> DataProviderClient();<br /> <span class="kwrd">string</span> result = client.GetSourceListFromServer(<span class="str">""</span>);<br /><br /> Assert.AreEqual(<span class="str">"Error"</span>, result);<br />} |
So there you go, WCF simply isolated. Now you can test your logic around it, no excuses.