Client compatibility check management - c#

I'm working on a client-server application (.NET 4, WCF) that must support backwards compatibility. In other words, old clients should be compatible with new servers and vice versa. As a result, our client code is littered with statements such as:
if (_serverVersion > new Version(2, 1, 3))
{
//show/hide something or call method Foo()...
}
else
{
//show/hide something or call method Foo2()...
}
Obviously, this becomes somewhat of a maintenance nightmare. Luckily, we're allowed to break backwards compatibility with every minor release. When we get to a point where compatibility can be broken, I'd like to clean up the code that's in the form of the example above.
My questions:
(1) Is there a way to easily identify code blocks such as these when they are no longer "valid"? My initial thoughts were to somehow conditionally apply an Obsolete attribute based on the assembly's version. When we get to a new minor version, the Obsolete attribute would "kick-in", and all of a sudden we would have several compiler warnings pointing to these code blocks... Has anyone done anything like this? Or is there a better way to manage this?
(2) I cringe every time I see hard-coded versions such as new Version(2, 1, 3). What makes things worse is that during development, we don't know the final Version that's being released, so the version checks are based on the current build number + 1 when the developer adds the check. Although this works, it's not very clean. Any ideas on how this could be improved?
Thanks!

I would suggest at least creating a method where you can do the logic like this:
public static class ServerUtilities
{
public static bool IsValidToRun(Version desiredVersion)
{
if (_serverVersion >= desiredVersion)
return true;
else if (/* your other logic to determine if they're in some acceptable range */)
return true;
return false;
}
}
Then, use like this:
if (ServerUtilities.IsValidToRun(new Version(2, 1, 3)))
{
// Do new logic
}
else
{
// Do old logic
}
If you need to centralize the versions, have a static repository of features to version mapping, so that you can call:
if (ServerUtilities.IsValidToRun(ServerFeatures.FancyFeatureRequiredVersion))
{
...
}
public static class ServerFeatures
{
public static Version FancyFeatureRequiredVersion
{
get { return new Version(2, 1, 3); }
}
}

An alternative would be to implement versioning of your service contracts: at that point you could leverage WCF's own features to ignore minor changes which do not break the client, as listed on this Versioning Strategies page.
In Figure 1 you can see that when adding new parameters to an operation signature, removing parameters from an operation signature and adding new operations the client is unaffected.
In case there are still breaking changes or your client has to support both versions (please correct me if I'm wrong since I don't know your deploying strategy), you could offer different versions of the service on different endpoints and have a WCF client factory in your client code, which could then be configured to return the client for the appropriate endpoint.
At this point you have isolated the different implementations in different clients, which is probably cleaner and less a maintenance nightmare than it is now.
Very basic sample implementation to clear things up: suppose that we have two different contracts for our service, an old one and a new one.
[ServiceContract(Name = "Service", Namespace = "http://stackoverflow.com/2012/03")]
public interface IServiceOld
{
[OperationContract]
void DoWork();
}
[ServiceContract(Name = "Service", Namespace = "http://stackoverflow.com/2012/04")]
public interface IServiceNew
{
[OperationContract]
void DoWork();
[OperationContract]
void DoAdditionalWork();
}
Note how both services have the same name but different namespaces.
Let's handle the issue of having a client that has to be able to support both the extended and new service and the old one. Let's assume that we want to call the DoAdditionalWork method when we previously just called DoWork, and that we want to handle the situation client-side, because hypothetically DoAdditionalWork could require some extra arguments from the client. Then the configuration of the service could be something like this:
<service name="ConsoleApplication1.Service">
<endpoint address="http://localhost:8732/test/new" binding="wsHttpBinding" contract="ConsoleApplication1.IServiceNew" />
<endpoint address="http://localhost:8732/test/old" binding="wsHttpBinding" contract="ConsoleApplication1.IServiceOld" />
...
</service>
Fine, we have the service side, now to the interesting part: we would like to communicate with the services using the same interface. In this case I will use the old one, but you could need to put an adapter in between. Ideally, in our client code, we would do something like this:
IServiceOld client = *Magic*
client.DoWork();
The magic in this case is a simple factory like this:
internal class ClientFactory
{
public IServiceOld GetClient()
{
string service = ConfigurationManager.AppSettings["Service"];
if(service == "Old")
return new ClientOld();
else if(service == "New")
return new ClientNew();
throw new NotImplementedException();
}
}
I delegated the decision of which client to use to the app.config, but you could insert your version check there. The implementation of ClientOld is just a regular WCF client for IServiceOld:
public class ClientOld : IServiceOld
{
private IServiceOld m_Client;
public ClientOld()
{
var factory = new ChannelFactory<IServiceOld>(new WSHttpBinding(), "http://localhost:8732/test/old");
m_Client = factory.CreateChannel();
}
public void DoWork()
{
m_Client.DoWork();
}
...
}
ClientNew instead implements the behavior we were wishing for, namely calling the DoAdditionalWork operation:
public class ClientNew : IServiceOld
{
private IServiceNew m_Client;
public ClientNew()
{
var factory = new ChannelFactory<IServiceNew>(new WSHttpBinding(), "http://localhost:8732/test/new");
m_Client = factory.CreateChannel();
}
public void DoWork()
{
m_Client.DoWork();
m_Client.DoAdditionalWork();
}
...
}
That's it, now our client can be used like in the following example:
var client = new ClientFactory().GetClient();
client.DoWork();
What have we achieved? The code using the client is abstracted from what additional work the actual WCF client has to do and the decision about which client to use is delegated to a factory. I hope some variation/expansion of this sample suits your needs.

Related

How to update HttpClient base address at runtime in ASP.net core

I created several microservices using the ASP.net core API
One of these microservices returns the exact address of the other microservices
How to update the address of any of these microservices without restarting if the address is changed
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("MainMicroservice", x =>
{
x.BaseAddress = new Uri("http://mainmicroservice.com");
});
services.AddHttpClient("Microservice1", x =>
{
x.BaseAddress = new Uri("http://microservice1.com");
});
services.AddHttpClient("Microservice2", x =>
{
x.BaseAddress = new Uri("http://microservice2.com");
});
services.AddHttpClient("Microservice3", x =>
{
x.BaseAddress = new Uri("http://microservice3.com");
});
}
}
public class Test
{
private readonly IHttpClientFactory _client;
public Test(IHttpClientFactory client)
{
_client = client;
}
public async Task<string> Test()
{
var repeat = false;
do
{
try
{
return await _client
.CreateClient("Microservice1")
.GetStringAsync("Test")
.ConfigureAwait(false);
}
catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.NotFound)
{
var newAddress = await _client
.CreateClient("MainMicroservice")
.GetStringAsync("Microservice1")
.ConfigureAwait(false);
//todo change address of microservice1
repeat = true;
}
} while (repeat);
}
}
If you are building a microservice-based solution sooner or later (rather sooner) you will encounter a situation when one service needs to talk to another. In order to do this caller must know the exact location of a target microservice in a network, they operate in.
You must somehow provide an IP address and port where the target microservice listens for requests. You can do it using configuration files or environment variables, but this approach has some drawbacks and limitations.
First is that you have to maintain and properly deploy
configuration files for all your environments: local development,
test, pre-production, and production. Forgetting to update any of
these configurations when adding a new service or moving an existing
one to a different node will result in errors discovered at runtime.
Second, the more important issue is that it works only in a static
environment, meaning you cannot dynamically add/remove nodes,
therefore you won’t be able to dynamically scale your system. The
ability to scale and deploy given microservice autonomously is one
of the key advantages of microservice-based architecture, and we do
not want to lose this ability.
Therefore we need to introduce service discovery. Service discovery is a mechanism that allows services to find each other's network location. There are many possible implementations of this pattern.
We have two types of service discovery: client-side and server-side which you can find a good NuGet package to handle in ASP.NET projects.

"Simulate" command line argument in unit-tests

I have some functionality, which depends on command line arguments, and different arguments should lead to different results.
I can't directly "simulate" this arguments, since there are some sort of chain dependencies - I need to unit-test some xaml control, which depends on view-model, which depends on certain additional class, which fetches command line arguments using Environment.GetCommandLineArgs, and I can't directly impact on this last class to set arguments manually instead of using GetCommandLineArgs.
So, I'd like to know, is there any way to make Environment.GetCommandLineArgs return value I want it to return, for certain unit-test.
You need to abstract Environment.GetCommandLineArgs or what ever is eventually calling it behind something you can mock
public interface ICommandLineInterface {
string[] GetCommandLineArgs();
}
Which can eventually be implemented in a concrete class like
public class CommandInterface : ICommandLineInterface {
public string[] GetCommandLineArgs() {
return Environment.GetCommandLineArgs();
}
}
And can be Tested using Moq and FluentAssertions
[TestMethod]
public void Test_Should_Simulate_Command_Line_Argument() {
// Arrange
string[] expectedArgs = new[] { "Hello", "World", "Fake", "Args" };
var mockedCLI = new Mock<ICommandLineInterface>();
mockedCLI.Setup(m => m.GetCommandLineArgs()).Returns(expectedArgs);
var target = mockedCLI.Object;
// Act
var args = target.GetCommandLineArgs();
// Assert
args.Should().NotBeNull();
args.Should().ContainInOrder(expectedArgs);
}
Since you are dealing with environment variables, why don't we wrap the outside dependencies into one EnvironmentHelper class, then inject the dependencies?
Here is my suggestion:
public class EnvironmentHelper
{
Func<string[]> getEnvironmentCommandLineArgs;
// other dependency injections can be placed here
public EnvironmentHelper(Func<string[]> getEnvironmentCommandLineArgs)
{
this.getEnvironmentCommandLineArgs = getEnvironmentCommandLineArgs;
}
public string[] GetEnvironmentCommandLineArgs()
{
return getEnvironmentCommandLineArgs();
}
}
Here is the Mock method:
public static string[] GetFakeEnvironmentCommandLineArgs()
{
return new string[] { "arg1", "arg2" };
}
In your source code:
EnvironmentHelper envHelper = new EnvironmentHelper(Environment.GetCommandLineArgs);
string[] myArgs = envHelper.GetEnvironmentCommandLineArgs();
In your unit test code:
EnvironmentHelper envHelper = new EnvironmentHelper(GetFakeEnvironmentCommandLineArgs);
string[] myArgs = envHelper.GetEnvironmentCommandLineArgs();
You can do it much more easier with Typemock Isolator.
It allows to mock not only interfaces, so. Take a look:
[TestMethod, Isolated]
public void TestFakeArgs()
{
//Arrange
Isolate.WhenCalled(() => Environment.GetCommandLineArgs()).WillReturn(new[] { "Your", "Fake", "Args" });
//Act
string[] args = Environment.GetCommandLineArgs();
//Assert
Assert.AreEqual("Your", args[0]);
Assert.AreEqual("Fake", args[0]);
Assert.AreEqual("Args", args[0]);
}
Mocking Environment.GetCommandLineArgs() took only one line:
Isolate.WhenCalled(() => Environment.GetCommandLineArgs()).WillReturn(new[] { "Your", "Fake", "Args" });
And you don't need to create new Interfaces and to change production code.
Hope it helps!
If you want something unit-testable it should have its dependencies on a abstraction that is at least as strict as its implementation.
Usually you'd get the dependencies through your constructor of your class or a property method. Constructor is preferred, generally, because now a consumer of your class knows at compile-time what dependencies are needed.
public void int Main(string[] args)
{
// Validate the args are valid (not shown).
var config = new AppConfig();
config.Value1 = args[0];
config.Value2 = int.Parse(args[1]);
// etc....
}
public class MyService()
{
private AppConfig _config;
public MyService(AppConfig config)
{
this._config = config;
}
}
I normally don't put a config object behind an interface because it only has data - which is serializable. As long as it has no methods, then I shouldn't need to replace it with a subclass with override-d behavior. Also I can just new it up directly in my tests.
Also, I've never ran into a situation when I wanted to depend on an abstraction of the command line arguments themselves to a service - why does it need to know it's behind a command-line? The closest I've gotten is use PowerArgs for easy parsing, but I'll consume that right in Main. What I normally do is something like maybe read in the port number for a web server on the command-line arguments (I let the user of the app choose so that I can run multiple copies of my web server on the same machine - maybe different versions or so I can run automated tests while I'm debugging and not conflict ports), parse them directly in my Main class. Then in my web server I depend on the parsed command-line arguments, in this case an int. That way the fact that the configuration is coming from a command-line is irrelevant - I can move it to an App.config file later (which is also basically bound to the lifecycle of the process) if I prefer - then I can extract common configuration to configSource files.
Instead of depending on an abstraction for command-line in general (which each service consuming would have to re-parse if you kept it pure), I usually abstract the command-line and App.config dependencies to a strongly-typed object - maybe an app-level config class and a test-level config class and introduce multiple configuration objects as needed - (the app wouldn't necessarily care about this, while the E2E test infrastructure would need this in a separate part of the App.config: where do I grab the client static files from, where do I grab the build scripts in a test or developer environment to auto-generate/auto-update an index.html file, etc.).

Castle Windsor WCF Facility is not processing one way operations

I currently have a simple use case.
1) A client app that connects to a WCF Service using Castle's AsWcfClient option.
2) WCF Service "A" that is hosted using Castle and is injecting a single dependency. This dependency is a client proxy to another WCF Service (Call it Service "B").
3) Service "B" does some work.
To visualize: Client -> Service "A" with Castle injected proxy to -> Service "B"
Simple right? Works without issue IF, and that's a big if, the Service "B" host is up and running.
The behavior I have seen and can reproduce on demand is that if Service "B" is down, the call chain completes without any hint that there is any issue. To say it another way, there is no resolution exception thrown by Castle nor any WCF exception. I have isolated this to how IsOneWay=true operations are handled.
This is a major issue because you think that everything has executed correctly but in reality none of your code has been executed!
Is this expected behavior? Is there away I can turn on some option in Castle so that it will throw and exception when a WCF Client proxy is the resolved dependency? Some other option?
One more note, the only clue that you have that is issue is occurring is when/if you do a Container.Release() on the client proxy as it throws an exception. This can't be depended upon thou for various reasons not worth getting into here.
Thank!
Additionally below is the code that recreates this issue. To run it
1) Create a new Unit Test project in Visual Studio
2) Add the Castle Windsor WCF Integration Facility via NuGet
3) Paste the code from below into a .cs file, everything is in one to make it easy.
4) Run the two unit tests, SomeOperation_With3Containers_NoException() works as the dependency service (Service "B" from above) is running. SomeOperation_With2Containers_NoException() fails are the .Release
5) Set break points and you can see that no code is hit in the implementations.
****UPDATE****: The primary way this needs to be handled is with an IErrorHandler implantation (As mentioned by Roman in the comments below). Details and an example can be found here: http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler(v=vs.110).aspx
Use this implementation to log any exception on the One Way operation and use that data to take the appropriate action.
using Castle.Facilities.WcfIntegration;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace UnitTestProject1
{
[ServiceContract]
public interface IServiceContractA
{
[OperationContract(IsOneWay = true)]
void SomeOperation();
}
[ServiceContract]
public interface IServiceDependancy
{
[OperationContract]
void SomeOperation();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class ServiceContractAImplementation : IServiceContractA
{
private IServiceDependancy ServiceProxy;
public ServiceContractAImplementation() { }
public ServiceContractAImplementation(IServiceDependancy dep)
{
ServiceProxy = dep;
}
public void SomeOperation()
{
ServiceProxy.SomeOperation();
}
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class ServiceDependancyImplementation : IServiceDependancy
{
public void SomeOperation()
{
//do nothing, just want to see if we can create an instance and hit the operation.
//if we need to do something, do something you can see like: System.IO.File.Create(#"d:\temp\" + Guid.NewGuid().ToString());
}
}
public class ServiceCastleInstaller : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero);
var returnFaults = new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true, HttpHelpPageEnabled = true };
container.Register(Component.For<IServiceBehavior>().Instance(returnFaults));
//local in-proc service hosting
var namedPipeBinding = new NetNamedPipeBinding();
//it works using Named Pipes
var serviceModelPipes = new DefaultServiceModel().AddEndpoints(
WcfEndpoint.BoundTo(namedPipeBinding).At("net.pipe://localhost/IServiceContractA")
).Discoverable();
container.Register(Component.For<IServiceContractA>()
.ImplementedBy<ServiceContractAImplementation>()
.LifeStyle.PerWcfOperation()
.AsWcfService(serviceModelPipes)
);
//our service (IServiceContractA) has a dependancy on another service so needs a client to access it.
container.Register(Castle.MicroKernel.Registration.Component.For<IServiceDependancy>()
.AsWcfClient(WcfEndpoint.BoundTo(namedPipeBinding)
.At(#"net.pipe://localhost/IServiceDependancy")).LifeStyle.Transient);
}
}
public class ServiceDependancyCastleInstaller : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero);
var returnFaults = new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true, HttpHelpPageEnabled = true };
container.Register(Component.For<IServiceBehavior>().Instance(returnFaults));
//local in-proc service hosting
var namedPipeBinding = new NetNamedPipeBinding();
var serviceModel = new DefaultServiceModel().AddEndpoints(
WcfEndpoint.BoundTo(namedPipeBinding).At("net.pipe://localhost/IServiceDependancy")
).Discoverable();
container.Register(Component.For<IServiceDependancy>()
.ImplementedBy<ServiceDependancyImplementation>()
.LifeStyle.PerWcfOperation()
.AsWcfService(serviceModel)
);
}
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void SomeOperation_With3Containers_NoException()
{
//setup the container that is going to host the service dependancy
using (var dependancyContainer = new WindsorContainer().Install(new ServiceDependancyCastleInstaller()))
{
//container that host the service that the client will call.
using (var serviceContainer = new WindsorContainer().Install(new ServiceCastleInstaller()))
{
//client container, nice and simple so doing it in the test here.
using (var clientContainer = new WindsorContainer())
{
clientContainer.AddFacility<WcfFacility>();
var endpoint = WcfEndpoint.BoundTo(new NetNamedPipeBinding())
.At("net.pipe://localhost/IServiceContractA");
clientContainer.Register(Castle.MicroKernel.Registration.Component.For<IServiceContractA>()
.AsWcfClient(endpoint).LifeStyle.Transient);
var proxy = clientContainer.Resolve<IServiceContractA>();
proxy.SomeOperation();
clientContainer.Release(proxy);
}
}
}
}
[TestMethod]
public void SomeOperation_With2Containers_NoException()
{
//this one fails.
// this test omits the dependancy that the IServiceContractA has
//Note that all seems to work, the only hint you have that it doesnt
//is the .Release call throws and exception.
//container that host the service that the client will call.
using (var serviceContainer = new WindsorContainer().Install(new ServiceCastleInstaller()))
{
//client container, nice and simple so doing it in the test here.
using (var clientContainer = new WindsorContainer())
{
clientContainer.AddFacility<WcfFacility>();
var endpoint = WcfEndpoint.BoundTo(new NetNamedPipeBinding())
.At("net.pipe://localhost/IServiceContractA");
clientContainer.Register(Castle.MicroKernel.Registration.Component.For<IServiceContractA>()
.AsWcfClient(endpoint).LifeStyle.Transient);
var proxy = clientContainer.Resolve<IServiceContractA>();
//this call seems like it works but any break points
//in code don't get hit.
proxy.SomeOperation();
//this throws and exception
clientContainer.Release(proxy);
}
}
}
}
}
One way operations exists for the purpose of "Fire and forget" scenarios. You don't care about result, whether it was successful or not. You don't have to wait for the server to respond (only the initial TCP handshake if it's HTTP binding). By using one way operations, the client only gets the confidence that the server received the message successfully on the wire, and the server makes no guarantees that it will succeed in processing the message. This is true in HTTP protocol. In other protocols, like Microsoft MSMQ, or IBM MQ, the server doesn't even need to be online at the same time as the client.
In your scenario, The client does not receive an exception, because service A is up and running. If service A was down, you would have seen an error (Again, assuming HTTP, or in your case .net pipe). The condition of service B does not matter, because service B is an implementation detail of service A, and your client doesn't care about service A return values. If your were to debug service A (by attaching to it) while service B is down, you would have seen first chance, and maybe even unhandled exceptions (depending on the implementation of service A).
Castle should not have thrown an exception anyway, because it has successfully resolved a proxy for service B in service A. The fact that service B is down is no concern of Castle, or any other DI container for that matter.

Testing WCF Service that Uses Impersonation

I am converting some existing integration tests of a legacy WCF service to be automated via NUnit. The current tests call a deployed version of the WCF service; what I would like to do is have the tests hit the service class (MyService.svc.cs) directly/internally.
The problem I am having is that the service uses impersonation:
//this is a method in MyService.svc.cs
public SomeObject GetSomeObject()
{
using (GetWindowsIdentity().Impersonate())
{
//do some stuff
}
return null;
}
private WindowsIdentity GetWindowsIdentity()
{
var callerWinIdentity = ServiceSecurityContext.Current.WindowsIdentity;
var cf = new ChannelFactory<IMyService>();
cf.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
return callerWinIdentity;
}
The problem is that ServiceSecurityContext.Current is always null when I call it from a unit test.
The impersonation is important in downstream operations, so I can't just bypass this code and just call what is within the using block. It might be possible to wrap my test code in WindowsIdentity.GetCurrent().Impersonate() and then call what is within the using block (bypassing MyService.svc.cs code), but this would be less than ideal, as it would not be a complete end-to-end test.
I do not need to fake different users to impersonate--I just need the runner's user context to be available in ServiceSecurityContext.Current.
Is this possible?
I'd still be interested in a better and less invasive way of doing this, but this seems to work for now.
I created a second constructor for MyService to allow the use of WindowsIdentity.GetCurrent().
private readonly bool _useLocalIdentity;
public MyService(bool useLocalIdentity) :this()
{
_useLocalIdentity = useLocalIdentity;
}
private WindowsIdentity GetWindowsIdentity()
{
if (_useLocalIdentity)
{
return WindowsIdentity.GetCurrent();
}
var callerWinIdentity = ServiceSecurityContext.Current.WindowsIdentity;
if (callerWinIdentity == null)
{
throw new InvalidOperationException("Caller not authenticated");
}
var cf = new ChannelFactory<IMyService>();
cf.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
return callerWinIdentity;
}

Unit-test WCF contracts match for sync / async?

WCF makes it easy to call services synchronously or asynchronously, regardless of how the service is implemented. To accommodate clients using ChannelFactory, services can even define separate sync/async contract interfaces. For example:
public interface IFooService
{
int Bar();
}
[ServiceContract(Name = "IFooService")]
public interface IAsyncFooService
{
Task<int> BarAsync();
}
This allows the client to reference either contract version, and WCF translates the actual API calls automatically.
One drawback to providing both contract versions is that they must be kept in-sync. If you forget to update one, the client may receive a contract mismatch exception at runtime.
Is there an easy way to unit test the interfaces to ensure they match from a WCF metadata perspective?
You can retrieve the ContractDescription and use WsdlExporter to generate the WSDL. The output MetadataSet is XML serializable, so you can compare the representations for each contract version to ensure they match:
[TestMethod]
public void ContractsMatch()
{
// Arrange
string expectedWsdl = this.GetContractString<IFooService>();
// Act
string actualWsdl = this.GetContractString<IAsyncFooService>();
// Assert
Assert.AreEqual(expectedWsdl, actualWsdl);
}
private string GetContractString<TContract>()
{
ContractDescription description = ContractDescription.GetContract(typeof(TContract));
WsdlExporter wsdlExporter = new WsdlExporter();
wsdlExporter.ExportContract(description);
if (wsdlExporter.Errors.Any())
{
throw new InvalidOperationException(string.Format("Failed to export WSDL: {0}", string.Join(", ", wsdlExporter.Errors.Select(e => e.Message))));
}
MetadataSet wsdlMetadata = wsdlExporter.GetGeneratedMetadata();
string contractStr;
StringBuilder stringBuilder = new StringBuilder();
using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder))
{
wsdlMetadata.WriteTo(xmlWriter);
contractStr = stringBuilder.ToString();
}
return contractStr;
}
Your own answer is great. As BartoszKP points out it is more of an integration test, but this might be the best fit. You could argue that comparing two units (interfaces) to each other is not a unit test by definition.
The advantage of your approach is that you can be sure to verify what WCF makes from your classes. If you only want to test your own code, you could do something like that:
[TestMethod]
public void ContractsMatch()
{
var asyncMethodsTransformed = typeof(IAsyncFooService)
.GetMethods()
.Select(mi => new
{
ReturnType = mi.ReturnType,
Name = mi.Name,
Parameters = mi.GetParameters()
});
var syncMethodsTransformed = typeof(IFooService)
.GetMethods()
.Select(mi => new
{
ReturnType = WrapInTask(mi.ReturnType),
Name = Asyncify(mi.Name),
Parameters = mi.GetParameters()
});
Assert.That(asyncMethodsTransformed, Is.EquivalentTo(syncMethodsTransformed));
}
The idea is that for each method in your IFooService you expect a method which has a similar signature with clearly defined transformations:
The name must contain a "Async" after the "I"
The return type must be a Task of the type found in the sync version.
The WrapInTask and Asyncify are left as exercise :-) If you like this suggestion I can expand on them.
By using a test like that you might constrain the code more than WCF does (I don't know the Async support very well). But even if it does you might want that to ensure some code consistency.

Categories

Resources