"가상이 아닌 (VB에서 재정의 할 수없는) 멤버에서 유효하지 않은 설정 ..."메시지와 함께 예외가 발생하는 이유는 무엇입니까?


176

부울 유형을 반환하는 비가 상 메소드를 조롱 해야하는 단위 테스트가 있습니다.

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>())

이 예외를 해결하는 방법에 대한 제안이 있으십니까?


테스트에서 무엇에 의존 XmlCupboardAccess합니까?
— 프레스턴 길로

9
그것의 간단한 .. 당신은 그것을 표시해야합니다 virtual. Moq는 재정의 할 수없는 구체적인 유형을 조롱 할 수 없습니다.
— Simon Whitehead

답변:


265

Moq는 비가 상 메서드와 봉인 클래스를 조롱 할 수 없습니다. 모의 객체를 사용하여 테스트를 실행하는 동안 MOQ는 실제로 "XmlCupboardAccess"에서 상속하고 "SetUp"메소드에서 설정 한 동작을 무시하는 메모리 내 프록시 유형을 만듭니다. C #에서 알 수 있듯이 Java에서는 그렇지 않은 가상으로 표시된 경우에만 무언가를 무시할 수 있습니다. Java는 모든 비 정적 메소드가 기본적으로 가상 인 것으로 가정합니다.

고려해야 할 또 다른 사항은 "CupboardAccess"에 대한 인터페이스를 도입하고 대신 인터페이스를 조롱하는 것입니다. 코드를 분리하고 장기적으로 이익을 얻는 데 도움이됩니다.

: 마지막으로, 같은 프레임 워크가 TypeMock 및 JustMock 가상이 아닌 방법을 조롱 할 수 따라서 IL 직접 작업합니다. 그러나 둘 다 상용 제품입니다.


59
인터페이스 만 조롱해야한다는 사실에 +1하십시오. 이 질문은 실수로 기본 인터페이스가 아닌 클래스를 조롱했기 때문에 내가 겪고있는 것을 해결했습니다.
— Paul Raff

1
이렇게하면 문제가 해결 될뿐만 아니라 테스트가 필요한 모든 클래스에 인터페이스를 사용하는 것이 좋습니다. Moq는 본질적으로 다른 모의 프레임 워크와 같이이 원칙을 해결할 수있는 좋은 종속성 반전을 강요합니다.
— Xipooo

IPeopleRepository와 같은 인터페이스의 가짜 구현 (예 : FakePeopleRepository)이 있고 가짜 구현을 조롱하는 경우이 원칙을 위반 한 것으로 간주됩니까? 테스트 설정에서 가짜 객체를 생성자에서 인터페이스를 사용하는 서비스 클래스로 전달해야하기 때문에 IoC는 여전히 보존되어 있다고 생각합니다.
— paz

1
@paz MOQ 사용의 요점은 가짜 구현을 피하는 것입니다. 이제 경계 조건 등을 확인하는 데 필요한 가짜 구현의 변형 유형을 고려하십시오. 이론적으로는 가짜 구현을 조롱 할 수 있습니다. 그러나 실제로는 코드 냄새처럼 들립니다.
— Amol

이 오류는 실제로 인터페이스의 확장 메소드에서 발생할 수 있으며 혼동 될 수 있습니다.
— Dan Pantry

34

나와 같은 문제가있는 사람을 돕기 위해 실수로 인터페이스 대신 구현 유형을 잘못 입력했습니다.

var mockFileBrowser = new Mock<FileBrowser>();

대신에

var mockFileBrowser = new Mock<IFileBrowser>();


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>();

3

인터페이스의 확장 메소드가 호출되는지 확인하는 경우에도이 오류가 발생합니다.

예를 들어 조롱하는 경우 :

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)...


-12

암호:

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);
}        

그러나 예외를 참조하십시오.


4
솔루션에 대한 설명을 제공해 주시겠습니까? 또한 "예외 참조 ..."가 걸려 있습니다. 이것을 확장 할 수 있습니까?
— amadan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.