patterncsharpModerate
Make WCF Service testable
Viewed 0 times
testablemakeservicewcf
Problem
I have WCF Service. It works fine and I want to have test coverage for it. Unit tests and acceptance.
The problem is the
If it will be refactored to the code without static classes - I can use mocks for example
I have also used
The problem is the
static class in the code. How is it possible to avoid it?If it will be refactored to the code without static classes - I can use mocks for example
Moq.I have also used
Ninject as DI Framework so far.[ServiceContract]
public interface IWorkingService
{
[OperationContract]
Collection UpdateEntity(int entityID);
}
public class WorkingService : IWorkingService
{
private static readonly WorkingService Logger = ProviderLog4Net.GiveLogger(typeof(SomeService));
///
public Collection UpdateEntity(int entityID)
{
Logger.Info("------- UpdateEntity call ------------");
try
{
return CoreFacade.UpdateEntity(entityID);
}
catch (Exception exception)
{
Logger.Fatal(exception);
throw;
}
}
}
public static class CoreFacade
{
...
}Solution
You are doing it wrong.
Why are you using WCF if you are only using it as an old timer ASMX service without using the extensibility points?
First things first - clean up
The service interface is the same as yours, but:
-
Do not use primitives as service parameters, ever! Using primitives as parameters means that you cannot versionize your service, such as adding extra non-required properties.
The service implementation is much more slimmer then yours because it's missing the logging stuff (be patient!). The important thing is that it isn't containing a parameter-less constructor because the service has a dependency: the
Install-Package Ninject.Extensions.Wcf
The Ninject has an extension for WCF so install it via NuGet (remove
Nothing fancy; we only telling the factory which
I'm only telling the kernel how to resolve the
Save and close this.
Now we have a service with dependency injection and the only one real facade is our service.
WCF extensibility -
The WCF infrastructure has great possibilities for extending its capabilities, such as handling an error!
To do that we will create a new service behavior and we will use it for adding our error handlers to the service:
What can we see here? We are adding all
I have created the
```
public interface IAmLogger
{
void In
Why are you using WCF if you are only using it as an old timer ASMX service without using the extensibility points?
First things first - clean up
[ServiceContract]
public interface IWorkingService
{
[OperationContract]
Collection UpdateEntity(int entityId);
}The service interface is the same as yours, but:
- Why are you using
Collectionas the return type? Use someting more generic.
-
Do not use primitives as service parameters, ever! Using primitives as parameters means that you cannot versionize your service, such as adding extra non-required properties.
public class WorkingService : IWorkingService
{
private readonly NotStaticFacade _notStaticFacade;
public WorkingService(NotStaticFacade notStaticFacade)
{
_notStaticFacade = notStaticFacade;
}
public Collection UpdateEntity(int entityId)
{
return _notStaticFacade.UpdateEntity(entityId);
}
}The service implementation is much more slimmer then yours because it's missing the logging stuff (be patient!). The important thing is that it isn't containing a parameter-less constructor because the service has a dependency: the
NotStaticService (in your code this is the static CoreFacade). What does this mean? We will need a custom ServiceHostFactory because the default one cannot handle these kinds of situation, but don't worry; Ninject is with us!Install-Package Ninject.Extensions.Wcf
The Ninject has an extension for WCF so install it via NuGet (remove
App_Start directory after installation) and after that, create our own ServiceHostFactory:public class WorkingServiceHostFactoryWithNinject : NinjectServiceHostFactory
{
private readonly IKernel _kernel;
public WorkingServiceHostFactoryWithNinject()
{
_kernel = new StandardKernel(new WorkingServiceNinjectModule());
// SetKernel is a static method in Ninject WCF!
SetKernel(_kernel);
}
}Nothing fancy; we only telling the factory which
IKernel instance we want to use. As you see, you will have a NinjectModule prepared to map the bindings.public class WorkingServiceNinjectModule : NinjectModule
{
public override void Load()
{
// what ever other binding is need
Kernel.Bind().ToSelf();
}
}I'm only telling the kernel how to resolve the
NotStaticFacade but this isn't necessary this way. We haven't finished yet because we haven't told the system to use our factory to build up our service. To do this, we need to open the *.svc markup (right-click on the file then View markup) and write into the directive the Factory attribute:Save and close this.
Now we have a service with dependency injection and the only one real facade is our service.
WCF extensibility -
IServiceBehavior, IErrorHandlerThe WCF infrastructure has great possibilities for extending its capabilities, such as handling an error!
To do that we will create a new service behavior and we will use it for adding our error handlers to the service:
public class ErrorHandlerBehaviorWithNinjectKernel : IServiceBehavior
{
private readonly IKernel _kernel;
public ErrorHandlerBehaviorWithNinjectKernel(IKernel kernel)
{
_kernel = kernel;
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection endpoints,
BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
var errorHandlers = _kernel.GetAll().ToArray();
if (!errorHandlers.Any())
{
throw new Exception("No errorhandler was found");
}
foreach (var channelDispatcher in serviceHostBase.ChannelDispatchers.Select(channelDispatcherBase => channelDispatcherBase as ChannelDispatcher))
{
foreach (var errorHandler in errorHandlers)
{
channelDispatcher.ErrorHandlers.Add(errorHandler);
}
}
}
}What can we see here? We are adding all
IErrorHandler instance to all possible dispatchers to use it. IErrorHandler is coming from the WCF infrastructure. You need to create your own implementation of it like this:public class ErrorLogger : IErrorHandler
{
private readonly IAmLogger _logger;
public ErrorLogger(IAmLogger logger)
{
_logger = logger;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
//nothing to do here
}
public bool HandleError(Exception error)
{
_logger.Fatal(error);
return true;
}
}I have created the
IAmLogger interface to fake your Log4Net stuff, nothing more:```
public interface IAmLogger
{
void In
Code Snippets
[ServiceContract]
public interface IWorkingService
{
[OperationContract]
Collection<Ergebnis> UpdateEntity(int entityId);
}public class WorkingService : IWorkingService
{
private readonly NotStaticFacade _notStaticFacade;
public WorkingService(NotStaticFacade notStaticFacade)
{
_notStaticFacade = notStaticFacade;
}
public Collection<Ergebnis> UpdateEntity(int entityId)
{
return _notStaticFacade.UpdateEntity(entityId);
}
}public class WorkingServiceHostFactoryWithNinject : NinjectServiceHostFactory
{
private readonly IKernel _kernel;
public WorkingServiceHostFactoryWithNinject()
{
_kernel = new StandardKernel(new WorkingServiceNinjectModule());
// SetKernel is a static method in Ninject WCF!
SetKernel(_kernel);
}
}public class WorkingServiceNinjectModule : NinjectModule
{
public override void Load()
{
// what ever other binding is need
Kernel.Bind<NotStaticFacade>().ToSelf();
}
}<%@ ServiceHost Language="C#" Debug="true" Service="CodeReview.WorkingService" CodeBehind="WorkingService.svc.cs"
Factory="CodeReview.WorkingServiceHostFactoryWithNinject" %>Context
StackExchange Code Review Q#33379, answer score: 14
Revisions (0)
No revisions yet.