Это возможно без необходимости писать больше обёрток. Важно понимать принцип косвенности, который вводится при добавлении интерфейса. Каждый добавленный интерфейс - это возможность изолировать абстракцию и заставить ее выглядеть и чувствовать себя по-разному.
Я добавил важные фрагменты ниже, из-за ожидаемой краткости, я не присоединяю все решение.
Быстрыйобъяснение классов и иерархии использования - 1. IInnerChannel - это интерфейс, предоставляемый сторонней библиотекой. 2. IClientChannelWrapper - это класс-оболочка, созданный для того, чтобы скрыть внутренний интерфейс от вызывающих клиентов. 3. ClassUsingChannelWrapper - это класс, который вызывает эту логику, и в нашем модульном тесте его метод будет нашим sut (объектом тестирования).
Код выглядит следующим образом -
Объявление интерфейса IInnerChannel-
public interface IInnerChannel
{
string TheInnerChannelMethod();
}
Реализация InnerChannel (возможно, в сторонней библиотеке в вашем случае) -
public class InnerChannelImplementation : IInnerChannel
{
public InnerChannelImplementation()
{
}
public string TheInnerChannelMethod()
{
var result = "This is coming from innser channel.";
Console.WriteLine(result);
return result;
}
}
Оболочка, созданная вами для внутреннего канала -
public interface IClientChannelWrapper
{
void DoSomething();
IInnerChannel GetTheInnerChannelMethod();
}
Реализация интерфейса оболочки -
public class ClientChannelWrapperImplementation : IClientChannelWrapper
{
public ClientChannelWrapperImplementation()
{
}
public void DoSomething()
{
Console.WriteLine("The DoSomething Method!");
}
public IInnerChannel GetTheInnerChannelMethod()
{
InnerChannelImplementation imp = new InnerChannelImplementation();
return imp;
}
}
Класс, который вызывает реализацию вашей оболочки. Этот класс будет вашим SUT при реализации модульных тестов -
public class ClassUsingChannelWrapper
{
IClientChannelWrapper _wrapper;
public ClassUsingChannelWrapper(IClientChannelWrapper wrapper)
{
_wrapper = wrapper;
}
public void TheClientChannelConsumerMethod()
{
IInnerChannel theChannel = _wrapper.GetTheInnerChannelMethod();
var result = theChannel.TheInnerChannelMethod();
Console.WriteLine(result);
}
}
Наконец, модульный тест с имитацией поведения обоих интерфейсов. Обратите внимание, как оболочка поддельного клиентского канала возвращает объект поддельного внутреннего канала, который возвращает предварительно запрограммированное значение.
public class UnitTest1
{
[Fact]
public void Test1()
{
//Arrange
Mock<IInnerChannel> innerChannelMock = new Mock<IInnerChannel>();
innerChannelMock.Setup(i => i.TheInnerChannelMethod()).Returns("This
is a test from mocked object.");
Mock<InterfaceUt.IClientChannelWrapper> mockClientWrapper = new
Mock<IClientChannelWrapper>();
mockClientWrapper.Setup(m =>
m.GetTheInnerChannelMethod()).Returns(innerChannelMock.Object);
//Act
ClassUsingChannelWrapper sut = new
ClassUsingChannelWrapper(mockClientWrapper.Object);
sut.TheClientChannelConsumerMethod();
//Assert
innerChannelMock.Verify();
mockClientWrapper.Verify();
}
}
При выполнении этого модульного теста печатается
"Это тест из смоделированного объекта."
По сути, ваш модульный тест ориентирован только на клиентский код, который пытается использовать поведение вашего интерфейса. Это не проверяет реализацию вашей оболочки. Если вы хотите добиться этого, вам нужно будет создать новый экземпляр класса-оболочки вместо фиктивного объекта и передать ему фиктивный объект внутреннего канала.