I've just started to implement unit tests (using xUnit and Moq) on an already established project of mine. The project extensively uses dependency injection via the unity container.
I have two services A and B. Service A is the one being tested in this case. Service A calls B and gives it a delegate to an inter开发者_Python百科nal function. This 'callback' is used to notify A when a message has been received that it must handle.
Hence A calls (where b is an instance of service B):
b.RegisterHandler(Guid id, Action<byte[]> messageHandler);
In order to test service A, I need to be able to call messageHandler
, as this is the only way it currently accepts messages.
Can this be done using Moq? ie. Can I mock service B, such that when RegisterHandler
is called, the value of messageHandler
is passed out to my test?
Or do I need to redesign this? Are there any design patterns I should be using in this case? Does anyone know of any good resources on this kind of design?
You can get an instance of the callback (or any other input parameter) by using the Callback (the name similarity is incidental) method on the Mock:
[TestMethod]
public void Test19()
{
Action<byte[]> callback = null;
var bSpy = new Mock<IServiceB>();
bSpy.Setup(b => b.RegisterHandler(It.IsAny<Guid>(), It.IsAny<Action<byte[]>>()))
.Callback((Guid g, Action<byte[]> a) => callback = a);
var sut = new ServiceA(bSpy.Object);
sut.RegisterCallback();
Assert.AreEqual(sut.Do, callback);
}
This works when ServiceA is defined as this:
public class ServiceA
{
private readonly IServiceB b;
public ServiceA(IServiceB b)
{
if (b == null)
{
throw new ArgumentNullException("b");
}
this.b = b;
}
public void RegisterCallback()
{
this.b.RegisterHandler(Guid.NewGuid(), this.Do);
}
public void Do(byte[] bytes)
{
}
}
Yes you can setup the Moq object to respond to expected and unexpected operations. Here's a Visual Studio Unit Test example...
[TestMethod]
public void MyTest()
{
var moqObject = new Mock<ServiceB>();
// Setup the mock object to throw an exception if a certain value is passed to it...
moqObject.Setup(b => b.RegisterHandle(unexpectedValue).Throws(new ArgumentException());
// Or, setup the mock object to expect a certain method call...
moqObject.Setup(b => b.RegisterHandle(expectedValue));
var serviceA = new ServiceA(moqObject.Object);
serviceA.DoSomethingToTest();
// This will throw an exception if an expected operation didn't happen...
moqObject.VerifyAll();
}
精彩评论