Почему я получаю исключение с сообщением "Неверная настройка для не виртуального (переопределяемого в VB) члена..."?
У меня есть модульный тест, где я должен смоделировать не виртуальный метод, который возвращает тип 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>())
Любое предложение, как обойти это исключение?
7 ответов
Moq не может высмеивать не виртуальные методы и запечатанные классы. Во время выполнения теста с использованием фиктивного объекта MOQ фактически создает прокси-тип в памяти, который наследуется от вашего "XmlCupboardAccess" и переопределяет поведение, настроенное вами в методе "SetUp". И, как вы знаете в C#, вы можете переопределить что-то, только если оно помечено как виртуальное, что не так с Java. Java предполагает, что каждый нестатический метод является виртуальным по умолчанию.
Еще одна вещь, которую я считаю, что вы должны рассмотреть, это ввести интерфейс для вашего "CupboardAccess" и вместо этого начать дразнить интерфейс. Это поможет вам отделить ваш код и получит преимущества в долгосрочной перспективе.
Наконец, существуют такие инфраструктуры, как: TypeMock и JustMock, которые работают напрямую с IL и, следовательно, могут имитировать не виртуальные методы. Оба, однако, являются коммерческими продуктами.
В качестве помощи любому, у кого была такая же проблема, как у меня, я случайно набрал тип реализации вместо интерфейса, например
var mockFileBrowser = new Mock<FileBrowser>();
вместо
var mockFileBrowser = new Mock<IFileBrowser>();
Вместо того, чтобы издеваться над конкретным классом, вы должны высмеивать интерфейс этого класса. Извлечь интерфейс из класса 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>();
Посмотрите, почему собственность, которую я хочу высмеять, должна быть виртуальной?
Возможно, вам придется написать интерфейс-обертку или пометить свойство как виртуальное / абстрактное, поскольку Moq создает прокси-класс, который он использует для перехвата вызовов и возврата ваших пользовательских значений, которые вы поместили в .Returns(x)
вызов.
Вы также получите эту ошибку, если убедитесь, что вызывается метод расширения интерфейса.
Например, если вы издеваетесь:
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)...
В моем случае я использовал версию Moq ниже 4.16 и использовал
.Result
синтаксис для имитации асинхронного метода, который поддерживается только начиная с Moq 4.16
На макетной версии ниже 4.16 следующие результаты в
Invalid setup on a non-virtual member ...
даже при использовании интерфейса.
mock.Setup(foo => foo.DoSomethingAsync().Result).Returns(true);
В версии Moq ниже 4.16 используйте следующие
mock.Setup(foo => foo.DoSomethingAsync()).ReturnsAsync(true);
Для получения дополнительной информации см.
Код:
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);
}
но вижу исключение.