Я только что начал реализовывать модульные тесты (использующий xUnit и Moq) на уже установленном моем проекте. Проект экстенсивно использует внедрение зависимости через контейнер единицы.
У меня есть два сервиса A и B. Сервис A является тем, протестированным в этом случае. Обслужите вызовы B, и дает ему делегата во внутренней функции. Этот 'обратный вызов' используется для уведомления, когда сообщение было получено, что это должно обработать.
Следовательно вызовы (где b является экземпляром сервиса B):
b.RegisterHandler(Guid id, Action<byte[]> messageHandler);
Для тестирования сервиса A я должен смочь звонить messageHandler
, поскольку это - единственный способ, которым это в настоящее время принимает сообщения.
Это может быть сделано с помощью Moq? т.е. я могу дразнить сервис B, такой что когда RegisterHandler
назван, значение messageHandler
роздан к моему тесту?
Или я должен перепроектировать это? Есть ли какие-либо шаблоны разработки, которые я должен использовать в этом случае? Кто-либо знает о каких-либо хороших ресурсах на этом виде дизайна?
Можно получить экземпляр обратного вызова (или любой другой входной параметр) при помощи Обратного вызова (, подобие имени несущественно ), метод на Насмешке:
[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);
}
Это работает, когда ServiceA определяется как это:
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)
{
}
}
Да можно установить объект Moq ответить на ожидаемые и неожиданные операции. Вот пример Модульного теста Visual Studio...
[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();
}