"가상이 아닌 (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"에 대한 인터페이스를 도입하고 대신 인터페이스를 조롱하는 것입니다. 코드를 분리하고 장기적으로 이익을 얻는 데 도움이됩니다.

: 마지막으로, 같은 프레임 워크가 TypeMockJustMock 가상이 아닌 방법을 조롱 할 수 따라서 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.