Category Archives: Unit Test

Testing that all Fault Exceptions are being handled in a WCF client

One of the things that the .Net compiler won’t warn developers about, is when another developer decides to add a new FaultException type and the client code isn’t updated to handle this new type of exception. The solution I’m demonstrating here is a generic solution to check for this, but implies that the client is going through a ChannelFactory and not a ClientBase implementation.

ChannelFactory implementations are usually better if there’s full ownership, in the institution, of service and clients. The share of the service contracts will allow Continuous Integration builds to fail if there was a breaking change made on the service that broke one or more of the consuming clients. You may argue that ChannelFactory implementations have the issue that if you change the service, with a non-breaking change, you need to re-test and re-deploy all your clients code: This isn’t exactly true, as if it is a non-breaking change, all the clients will continue to work even with a re-deploy of the service.

Default ChannelFactory Wrapper

The generic implementation depends on our default WcfService wrapper for a ChannelFactory. This could be abstracted through an interface that had the Channel getter on it, and make the generic method depend on the interface instead of the actual implementation.

I will provide here a simple implementation of the ChannelFactory wrapper:


public class WcfService<T> : IDisposable where T : class
{
private readonly object _lockObject = new object();
private bool _disposed;
private ChannelFactory<T> _factory;
private T _channel;
internal WcfService()
{
_disposed = false;
}
internal virtual T Channel
{
get
{
if (_disposed)
{
throw new ObjectDisposedException("Resource WcfService<" + typeof(T) + "> has been disposed");
}
lock (_lockObject)
{
if (_factory == null)
{
_factory = new ChannelFactory<T>("*"); // First qualifying endpoint from the config file
_channel = _factory.CreateChannel();
}
}
return _channel;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
internal void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (!disposing)
{
return;
}
lock (_lockObject)
{
if (_channel != null)
{
try
{
((IClientChannel)_channel).Close();
}
catch (Exception)
{
((IClientChannel)_channel).Abort();
}
}
if (_factory != null)
{
try
{
_factory.Close();
}
catch (Exception)
{
_factory.Abort();
}
}
_channel = null;
_factory = null;
_disposed = true;
}
}
}

view raw

gistfile1.cs

hosted with ❤ by GitHub

Example of a client using the Wrapper

Here’s an example of code that we want to test, for a client that’s using the WcfService wrappe. The separation from the method that creates the WcfService wrapped in a using clause and the internal static one is just for testing purposes, just so we can inject a WcfService mock and assert against it. The client successfully wraps a FaultException into something meaningful for the consuming application.


public class DocumentClient : IDocumentService
{
public string InsertDocument(string documentClass, string filePath)
{
using (var service = new WcfService<IDocumentService>())
{
return InsertDocument(documentClass, filePath, service);
}
}
internal static string InsertDocument(string documentClass, string filePath, WcfService<IDocumentService> service)
{
try
{
return service.Channel.InsertDocument(documentClass, filePath);
}
catch (FaultException<CALFault> ex)
{
throw new DocumentCALException(ex);
}
catch (Exception ex)
{
throw new ServiceUnavailableException(ex.Message, ex);
}
}
}

view raw

gistfile1.cs

hosted with ❤ by GitHub

The generic Fault contract checker

This implementation is using Moq as the Mocking framework and the code is dependent on it. It also provides signatures up to 4 exceptions that are expected, this is done with a Top-Down approach, where the signature with the most type parameters has the full implementation and the others just call the one that’s one higher level in the signature chain. To support this mind set, a special empty DummyException is declared to fill the gaps between Type Parameters in the different signatures.

Breaking down the code, what it is doing is creating a dynamic Expression Tree that we can wire in the Setup method of the client mock that will intercept calls with any type of parameter (It.IsAny). Then for each FaultContractAttribute that is decorating the service, instantiate it and wire everything so that the service method is setup to throw it. Finally call it, and check if it was caught and wrapped or if we are getting the original FaultException back.


public static class ContractCheckerExtension
{
public static string CheckFaultContractMapping<TContract, TEx1>(this MethodInfo method, Action<Mock<WcfService<TContract>>> action)
where TContract : class
where TEx1 : Exception
{
return method.CheckFaultContractMapping<TContract, TEx1, DummyException>(action);
}
public static string CheckFaultContractMapping<TContract, TEx1, TEx2>(this MethodInfo method, Action<Mock<WcfService<TContract>>> action)
where TContract : class
where TEx1 : Exception
where TEx2 : Exception
{
return method.CheckFaultContractMapping<TContract, TEx1, TEx2, DummyException>(action);
}
public static string CheckFaultContractMapping<TContract, TEx1, TEx2, TEx3>(this MethodInfo method, Action<Mock<WcfService<TContract>>> action)
where TContract : class
where TEx1 : Exception
where TEx2 : Exception
where TEx3 : Exception
{
return method.CheckFaultContractMapping<TContract, TEx1, TEx2, TEx3, DummyException>(action);
}
public static string CheckFaultContractMapping<TContract, TEx1, TEx2, TEx3, TEx4>(this MethodInfo method, Action<Mock<WcfService<TContract>>> action)
where TContract : class
where TEx1 : Exception
where TEx2 : Exception
where TEx3 : Exception
where TEx4 : Exception
{
// we're creating a lambda on the fly that will call on the target method
// with all parameters set to It.IsAny<[the type of the param]>.
var lambda = Expression.Lambda<Action<TContract>>(
Expression.Call(
Expression.Parameter(typeof (TContract)),
method,
CreateAnyParameters(method)),
Expression.Parameter(typeof (TContract)));
// for all the fault contract attributes that are decorating the method
foreach (var faultAttr in method.GetCustomAttributes(typeof(FaultContractAttribute), false).Cast<FaultContractAttribute>())
{
// create the specific exception that get's thrown by the fault contract
var faultDetail = Activator.CreateInstance(faultAttr.DetailType);
var faultExceptionType = typeof(FaultException<>).MakeGenericType(new[] { faultAttr.DetailType });
var exception = (FaultException)Activator.CreateInstance(faultExceptionType, faultDetail);
// mock the WCF pipeline objects, channel and client
var mockChannel = new Mock<WcfService<TContract>>();
var mockClient = new Mock<TContract>();
// set the mocks
mockChannel.Setup(x => x.Channel)
.Returns(mockClient.Object);
mockClient.Setup(lambda)
.Throws(exception);
try
{
// invoke the client, wrapped in an Action delegate
action(mockChannel);
}
catch (Exception ex)
{
// if we get a targeted exception it's because the fault isn't being handled
// and we return with the type of the fault contract detail type that was caught
if (ex is TEx1 || ex is TEx2 || ex is TEx3 || ex is TEx4)
return faultAttr.DetailType.FullName;
// else soak all other exceptions because we are expecting them
}
}
return null;
}
private static IEnumerable<Expression> CreateAnyParameters(MethodInfo method)
{
return method.GetParameters()
.Select(p => typeof (It).GetMethod("IsAny").MakeGenericMethod(p.ParameterType))
.Select(a => Expression.Call(null, a));
}
}
[Serializable]
public class DummyException : Exception
{
}

view raw

gistfile1.cs

hosted with ❤ by GitHub

Here’s a sample of a unit test using the ContractChecker for the example client showed previously in the post:


[TestMethod]
public void Ensure_InsertDocument_FaultContracts_AreAllMapped()
{
var targetOperation = typeof (IDocumentService).GetMethod(
"InsertDocument",
new[]
{
typeof (string),
typeof (string)
});
var result = targetOperation.CheckFaultContractMapping<IDocumentService, ServiceUnavailableException>(
m => DocumentClient.InsertDocument(string.Empty, string.Empty, m.Object));
Assert.IsNull(result, "The type {0} used to detail a FaultContract isn't being properly handled on the Service client", result);
}

view raw

gistfile1.cs

hosted with ❤ by GitHub

Unit Testing IBM Websphere MQ with Fakes

.Net Projects that target the IBM Websphere MQ objects are often hard to unit test. Even with some amount of effort in isolating all the MQ objects through Dependency Injection and some tweaks around stubbing some of the common MQ classes, it’s easy to get into trouble with NullReferenceExceptions being thrown.

When targeting IBM MQ, there are two separate options: The native libraries (amqmdnet.dll) or the IBM.XMS library. I have found the JMS .Net implementation very problematic and hiding important queue options from the consuming classes, so I use mostly the native libraries and those are the focus of this post.

I won’t cover the basic principles of starting to use fakes, many people have covered that already and MSDN has a very nice series of articles on that. I will just highlight some common utility code and tricks I have learned along the way.

IBM MQ Design considerations

When I’m targeting IBM MQ, there’s a common set of design choices I make, and some of the code samples will reflect these options:

  • I always browse messages first. Only after I have actually done what I need to do with the messages, I do a read on them.
  • I use Rx right after the queues, this is why I always browse first. Once a message is browsed I push it through an IObservable, so that later I can do things like filter, sort, throttle, etc.
  • I use System.Threading Timers to do pooling on the MQ queues. They do a very nice usage of threads and they also allow me to change the pooling frequency at run-time.
  • Although I use several mocking frameworks, I tend to use only one per test class. On the test examples, everything will be going through Fakes, but I can easily argue that Fakes isn’t as mature as Moq or Rhino Mocks when it comes to fluent writing and API productivity.

IBM MQ id fields and properties

One common task around testing MQ objects is playing around with their Ids, either the object Id or other Ids like the correlation Id. These are always byte arrays and most times they are fixed size, so I wrote a nice utility method for creating fixed sized arrays:


private static byte[] CreateByteArray(int size, byte b)
{
var array = new byte[size];
for (var i = 0; i < array.Length; i++)
{
array[i] = b;
}
return array;
}

view raw

gistfile1.cs

hosted with ❤ by GitHub

To assert the different Id fields just use CollectionAssert and use the ToList on both byte arrays.

CollectionAssert.AreEqual(messageId.ToList(), message.MessageId.ToList());

IBM MQ Shims tips and tricks

One of the problems you will see when you start using IBM.WMQ Shims is null references when instantiating some objects. This is easily fixed by overriding the constructors on the Shims:

ShimMQQueueManager.ConstructorStringHashtable = (_, __, ___) => { };
ShimMQMessage.ConstructorMQMessage = (_, __) => { };

Some of the objects in IBM.WMQ have a long inheritance chain. Shims don’t follow this, so for example, if you’re doing a Get on a queue with MQMessage and MQGetMessageOptions, this exists in MQDestination that MQQueue inherits from, so to be able to stub this method you need to write something like this:

ShimMQDestination.AllInstances.GetMQMessageMQGetMessageOptions = (_, message, options) =>
{
    Assert.AreEqual(MQC.MQGMO_NONE, options.Options);
    Assert.AreEqual(MQC.MQMO_MATCH_MSG_ID, options.MatchOptions);
    CollectionAssert.AreEqual(messageId.ToList(), message.MessageId.ToList());
};

Open Queue example with the corresponding tests

Here’s a full example of a method that Opens a Queue for reading and/or writing


/// <summary>
/// Opens this queue. Supports listening and writing options, if setup
/// for listening it will do browse and not really reads from the queue
/// as messages arrive to the queue.
/// </summary>
/// <param name="reading">True if we want to read from this queue, false otherwise.</param>
/// <param name="writing">True if we want to write to this queue, false otherwise.</param>
/// <returns>The Observable where all the messages read from this queue will appear.</returns>
public IObservable<IServiceMessage> OpenConnection(bool reading = true, bool writing = false)
{
// create the properties HashTable
var mqProperties = new Hashtable
{
{ MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED },
{ MQC.HOST_NAME_PROPERTY, ConfigurationProvider.MQMessageListenerHostName },
{ MQC.PORT_PROPERTY, ConfigurationProvider.MQMessageListenerPortNumeric },
{ MQC.CHANNEL_PROPERTY, ConfigurationProvider.MQMessageListenerChannelName }
};
// create the queue manager
_queueManager = new MQQueueManager(ConfigurationProvider.MQMessageListenerQueueManagerName, mqProperties);
// deal with the queue open options
var openOptions = MQC.MQOO_INPUT_AS_Q_DEF + MQC.MQOO_FAIL_IF_QUIESCING;
if (reading)
{
openOptions += MQC.MQOO_BROWSE;
}
if (writing)
{
openOptions += MQC.MQOO_OUTPUT;
}
// create and start the queue, check for potential bad queue names
try
{
Queue = _queueManager.AccessQueue(QueueName, openOptions);
}
catch (MQException ex)
{
if (ex.ReasonCode == 2085)
{
throw new ConfigurationErrorsException(string.Format(CultureInfo.InvariantCulture, "Wrong Queue name: {0}", QueueName));
}
throw;
}
if (reading)
{
StartListening();
}
return Stream.AsObservable();
}

view raw

gistfile1.cs

hosted with ❤ by GitHub

And the unit tests that test it


/// <summary>
/// Tests that OpenConnection creates the proper MQQueueManager and accesses the
/// queue with the right set of options.
/// </summary>
[TestMethod]
public void Test_OpenConnection_GoesThrough()
{
const string host = "some random host";
const string channel = "some random channel";
const string manager = "some random manager";
const string queueName = "some random queue";
const int port = 1234;
var startListeningCall = false;
using (ShimsContext.Create())
{
var configShim = new ShimConfigurationProvider
{
MQMessageListenerChannelNameGet = () => channel,
MQMessageListenerHostNameGet = () => host,
MQMessageListenerPortNumericGet = () => port,
MQMessageListenerQueueManagerNameGet = () => manager
};
ShimMQQueueManager.ConstructorStringHashtable = (_, s, options) =>
{
Assert.AreEqual(manager, s);
Assert.AreEqual(host, options[MQC.HOST_NAME_PROPERTY]);
Assert.AreEqual(channel, options[MQC.CHANNEL_PROPERTY]);
Assert.AreEqual(port, options[MQC.PORT_PROPERTY]);
Assert.AreEqual(MQC.TRANSPORT_MQSERIES_MANAGED, options[MQC.TRANSPORT_PROPERTY]);
};
ShimMQQueueManager.AllInstances.AccessQueueStringInt32 = (_, s, options) =>
{
Assert.AreEqual(MQC.MQOO_INPUT_AS_Q_DEF + MQC.MQOO_FAIL_IF_QUIESCING, options);
Assert.AreEqual(queueName, s);
return null;
};
var mqShim = new ShimMQMessageQueue
{
InstanceBehavior = ShimBehaviors.Fallthrough,
ConfigurationProviderGet = () => configShim.Instance,
QueueNameGet = () => queueName,
StreamGet = () => new Subject<IServiceMessage>(),
StartListening = () => { startListeningCall = true; }
};
mqShim.Instance.OpenConnection(false);
Assert.IsFalse(startListeningCall);
}
}
/// <summary>
/// Tests the OpenConnection options in the queue access when the queue is setup to read.
/// It also ensures that StartListening is called if the queue is opened for reading.
/// </summary>
[TestMethod]
public void Test_OpenConnection_ForReading()
{
const string queueName = "some random queue";
var startListeningCall = false;
using (ShimsContext.Create())
{
ShimMQQueueManager.ConstructorStringHashtable = (_, __, ___) => { };
ShimMQQueueManager.AllInstances.AccessQueueStringInt32 = (_, s, options) =>
{
Assert.AreEqual(MQC.MQOO_INPUT_AS_Q_DEF + MQC.MQOO_FAIL_IF_QUIESCING + MQC.MQOO_BROWSE, options);
Assert.AreEqual(queueName, s);
return null;
};
var mqShim = new ShimMQMessageQueue
{
InstanceBehavior = ShimBehaviors.Fallthrough,
ConfigurationProviderGet = () => new ShimConfigurationProvider().Instance,
QueueNameGet = () => queueName,
StreamGet = () => new Subject<IServiceMessage>(),
StartListening = () => { startListeningCall = true; }
};
mqShim.Instance.OpenConnection();
Assert.IsTrue(startListeningCall);
}
}
/// <summary>
/// Tests the OpenConnection options in the queue access when the queue is setup to write.
/// </summary>
[TestMethod]
public void Test_OpenConnection_ForWriting()
{
const string queueName = "some random queue";
var startListeningCall = false;
using (ShimsContext.Create())
{
ShimMQQueueManager.ConstructorStringHashtable = (_, __, ___) => { };
ShimMQQueueManager.AllInstances.AccessQueueStringInt32 = (_, s, options) =>
{
Assert.AreEqual(MQC.MQOO_INPUT_AS_Q_DEF + MQC.MQOO_FAIL_IF_QUIESCING + MQC.MQOO_OUTPUT, options);
Assert.AreEqual(queueName, s);
return null;
};
var mqShim = new ShimMQMessageQueue
{
InstanceBehavior = ShimBehaviors.Fallthrough,
ConfigurationProviderGet = () => new ShimConfigurationProvider().Instance,
QueueNameGet = () => queueName,
StreamGet = () => new Subject<IServiceMessage>(),
StartListening = () => { startListeningCall = true; }
};
mqShim.Instance.OpenConnection(false, true);
Assert.IsFalse(startListeningCall);
}
}
/// <summary>
/// Tests the OpenConnection options in the queue access when the queue is setup to
/// read and write at the same time.
/// It also ensures that StartListening is called if the queue is opened for reading.
/// </summary>
[TestMethod]
public void Test_OpenConnection_ForReadingAndWriting()
{
const string queueName = "some random queue";
var startListeningCall = false;
using (ShimsContext.Create())
{
ShimMQQueueManager.ConstructorStringHashtable = (_, __, ___) => { };
ShimMQQueueManager.AllInstances.AccessQueueStringInt32 = (_, s, options) =>
{
Assert.AreEqual(MQC.MQOO_INPUT_AS_Q_DEF + MQC.MQOO_FAIL_IF_QUIESCING + MQC.MQOO_BROWSE + MQC.MQOO_OUTPUT, options);
Assert.AreEqual(queueName, s);
return null;
};
var mqShim = new ShimMQMessageQueue
{
InstanceBehavior = ShimBehaviors.Fallthrough,
ConfigurationProviderGet = () => new ShimConfigurationProvider().Instance,
QueueNameGet = () => queueName,
StreamGet = () => new Subject<IServiceMessage>(),
StartListening = () => { startListeningCall = true; }
};
mqShim.Instance.OpenConnection(true, true);
Assert.IsTrue(startListeningCall);
}
}
/// <summary>
/// Ensure that opening a connection with a Bad Queue Name will throw a proper
/// <see cref="ConfigurationErrorsException"/>.
/// </summary>
[TestMethod]
[ExpectedException(typeof(ConfigurationErrorsException))]
public void Ensure_OpenConnection_ThrowsBadQueueName()
{
const int nameReasonCode = 2085;
using (ShimsContext.Create())
{
ShimMQQueueManager.ConstructorStringHashtable = (_, __, ___) => { };
ShimMQQueueManager.AllInstances.AccessQueueStringInt32 = (_, __, ___) =>
{
throw new MQException(1, nameReasonCode);
};
var mqShim = new ShimMQMessageQueue
{
InstanceBehavior = ShimBehaviors.Fallthrough,
ConfigurationProviderGet = () => new ShimConfigurationProvider().Instance,
QueueNameGet = () => "something",
StreamGet = () => new Subject<IServiceMessage>(),
};
mqShim.Instance.OpenConnection();
}
}
/// <summary>
/// Ensure that any exception besides Bad Queue Name will be re-thrown
/// and bubble out of the OpenConnection method.
/// </summary>
[TestMethod]
[ExpectedException(typeof(MQException))]
public void Ensure_OpenConnection_ThrowsOthersExceptBadName()
{
using (ShimsContext.Create())
{
ShimMQQueueManager.ConstructorStringHashtable = (_, __, ___) => { };
ShimMQQueueManager.AllInstances.AccessQueueStringInt32 = (_, __, ___) =>
{
throw new MQException(1, 1);
};
var mqShim = new ShimMQMessageQueue
{
InstanceBehavior = ShimBehaviors.Fallthrough,
ConfigurationProviderGet = () => new ShimConfigurationProvider().Instance,
QueueNameGet = () => "something",
StreamGet = () => new Subject<IServiceMessage>(),
};
mqShim.Instance.OpenConnection();
}
}

view raw

gistfile1.cs

hosted with ❤ by GitHub