Почему я получаю исключение с сообщением "Неверная настройка для не виртуального (переопределяемого в VB) члена..."?

У меня есть unit тест, где я должен смоделировать не виртуальный метод, который возвращает тип bool

public class XmlCupboardAccess
{
    public bool IsDataEntityInXmlCupboard(string dataId,
                                          out string nameInCupboard,
                                          out string refTypeInCupboard,
                                          string nameTemplate = null)
    {
        return IsDataEntityInXmlCupboard(_theDb, dataId, out nameInCupboard, out refTypeInCupboard, nameTemplate);
    }
}

Итак, у меня есть фиктивный объект класса XmlCupboardAccess и я пытаюсь настроить макет для этого метода в моем тестовом примере, как показано ниже

[TestMethod]
Public void Test()
{
    private string temp1;
    private string temp2;
    private Mock<XmlCupboardAccess> _xmlCupboardAccess = new Mock<XmlCupboardAccess>();
    _xmlCupboardAccess.Setup(x => x.IsDataEntityInXmlCupboard(It.IsAny<string>(), out temp1, out temp2, It.IsAny<string>())).Returns(false); 
    //exception is thrown by this line of code
}

Но эта строка выдает исключение

Invalid setup on a non-virtual (overridable in VB) member: 
x => x.IsDataEntityInXmlCupboard(It.IsAny<String>(), .temp1, .temp2, 
It.IsAny<String>())

Любое предложение, как обойти это исключение?

Ответ 1

Moq не может высмеять не виртуальные методы и закрытые классы. При выполнении теста с использованием макета объекта MOQ фактически создает прокси-тип в памяти, который наследуется от вашего "XmlCupboardAccess" и переопределяет поведение, которое вы настроили в методе "SetUp". И, как вы знаете, на С# вы можете переопределить что-то, только если оно отмечено как виртуальное, что не соответствует Java. Java предполагает, что каждый нестатический метод будет виртуальным по умолчанию.

Еще одна вещь, которую, я считаю, вам следует рассмотреть - это ввести интерфейс для вашего "CupboardAccess" и начать издеваться над интерфейсом. Это поможет вам разделить ваш код и получить преимущества в долгосрочной перспективе.

Наконец, существуют такие рамки, как: TypeMock и JustMock, которые работают непосредственно с IL и следовательно, могут высмеивать не виртуальные методы. Оба, однако, являются коммерческими продуктами.

Ответ 2

В качестве помощи любому, у кого была такая же проблема, как и я, я случайно набрал тип реализации вместо интерфейса, например

var mockFileBrowser = new Mock<FileBrowser>();

вместо

var mockFileBrowser = new Mock<IFileBrowser>();

Ответ 3

См. Почему свойство, которое я хочу высмеять, должно быть виртуальным?

Возможно, вам придется написать интерфейс оболочки или пометить свойство как виртуальное/абстрактное, так как Moq создает прокси-класс, который он использует для перехвата вызовов и возвращает ваши пользовательские значения, которые вы вставляете в вызов .Returns(x).

Ответ 4

Вы также получите эту ошибку, если убедитесь, что вызывается метод расширения интерфейса.

Например, если вы издеваетесь:

var mockValidator = new Mock<IValidator<Foo>>();
mockValidator
  .Verify(validator => validator.ValidateAndThrow(foo, null));

Вы получите то же исключение, потому что .ValidateAndThrow() является расширением интерфейса IValidator<T>.

public static void ValidateAndThrow<T>(this IValidator<T> validator, T instance, string ruleSet = null)...

Ответ 5

Вместо того, чтобы издеваться над конкретным классом, вы должны высмеивать интерфейс этого класса Извлечь интерфейс из класса XmlCupboardAccess

public interface IXmlCupboardAccess
{
    bool IsDataEntityInXmlCupboard(string dataId, out string nameInCupboard, out string refTypeInCupboard, string nameTemplate = null);
}

И вместо

private Mock<XmlCupboardAccess> _xmlCupboardAccess = new Mock<XmlCupboardAccess>();

изменить на

private Mock<IXmlCupboardAccess> _xmlCupboardAccess = new Mock<IXmlCupboardAccess>();

Ответ 6

Код:

private static void RegisterServices(IKernel kernel)
{
    Mock<IProductRepository> mock=new Mock<IProductRepository>();
    mock.Setup(x => x.Products).Returns(new List<Product>
    {
        new Product {Name = "Football", Price = 23},
        new Product {Name = "Surf board", Price = 179},
        new Product {Name = "Running shose", Price = 95}
    });

    kernel.Bind<IProductRepository>().ToConstant(mock.Object);
}        

но см. исключение.