Moq: недопустимая настройка для непереопределяемого элемента: x => x.GetByTitle («asdf»)

111

Не знаю, как это исправить, пытаюсь выполнить модульный тест метода GetByTitle.

Вот мои определения:

public class ArticleDAO :  GenericNHibernateDAO(IArticle, int>, IArticleDAO
{
    public IArticle GetByTitle(string title)
    {
        IQuery query = Session.CreateQuery("...")
        return query.UniqueResult<IArticle>();
    }
}

public interface IArticleDAO
{
    IArticle GetByTitle(string title);
}

модульный тест:

[Test]
public void can_load_by_title()
{
    _mockDaoFactory.Setup(x => x.GetArticleDao())
                                .Returns(_mockArticleDao.Object);
    _mockArticleDao.Setup(x => x.GetByTitle("some title"))
                                .Returns(article1.Object);

    _articleManager.LoadArticle("some title");

    Assert.IsNotNull(_articleManager.Article);
}

Запуск теста дает мне ошибку:

System.ArgumentException: Invalid setup on a non-overridable member:
x => x.GetByTitle("some title")

Обновить

Мой [Setup]выглядит так:

[Setup]
public void SetUp()
{
     _mockDaoFactory = new Mock<IDaoFactory>();
     _mockArticleDao = new Mock<ArticleDao>();

     _articleManager = new ArticleManager(_mockDaoFactory.Object);    
}
mrblah
источник
2
Вы создаете экземпляр _mockDaoFactoryи _mockArticleDaoгде-нибудь? Вы издеваетесь над классом или интерфейсом?
Томас Ашан
Да, я издевался над daofactory и mockarticleDao в [Setup] с помощью интерфейса. DAO был выполнен с использованием класса.
mrblah
@tomas Я обновил свой вопрос установочным кодом.
mrblah
2
Как видно из моего ответа, вам нужно либо смоделировать интерфейс (это то, что я рекомендую), либо отметить GetByTitleметод virtual.
Томас Ашан,
Также похоже, что первую строку в вашем тесте можно переместить в процедуру настройки ...?
Томас Ашан,

Ответы:

154

Чтобы контролировать поведение фиктивного объекта (по крайней мере, в Moq), вам нужно либо имитировать интерфейс, либо убедиться, что поведение, которым вы пытаетесь управлять, помечено как виртуальное. В вашем комментарии я понимаю, что создание экземпляра _mockArticleDaoвыполняется примерно так:

_mockArticleDao = new Mock<ArticleDAO>();

Если вы хотите сохранить его таким, вам нужно отметить GetArticleметод virtual:

public class ArticleDAO :  GenericNHibernateDAO(IArticle, int>, IArticleDAO
{
    public virtual IArticle GetByTitle(string title)
    {
        // ...
    }
}

В противном случае (и это то, что я рекомендую) вместо этого имитируйте интерфейс.

_mockArticleDao = new Mock<IArticleDAO>();
Томас Ашан
источник
но поскольку ArticleDAO наследуется от Generic ...., если я высмеиваю интерфейс, методы в GenericNhibern. не будет доступно?
mrblah
поскольку вызов GetArticleDAO из фабрики возвращает ArticleDAO, а не IArticleDAO, b / c articleDAO также связывается с абстрактным классом, в котором есть материал nhibernate.
mrblah
2
Если вы не можете имитировать интерфейс, возможно, вы тестируете не то ... но, тем не менее, отметка метода виртуальным решит проблему.
Томас Ашан,
+1 Томас, мне нужно ввести параметр в ctor, поэтому в моем случае мне пришлось издеваться над фактическим классом и установить методы виртуальными, потому что вы не можете вводить параметры в ctor интерфейса. Это правильный подход?
Houman
4
@Kave: Если вам нужно что-то вставить в конструктор, вы определенно тестируете не то. Имитируйте все, что вы даете конструктору, настройте его поведение и проверьте, что этот класс ведет себя так, как должен. Если вам нужно, напишите новый интерфейс, который реализует «внедренный» тип, чтобы получить доступ ко всем сигнатурам методов.
Томас Ашан,