Насмешливое конкретное выражение в репозитории - PullRequest
0 голосов
/ 21 января 2012

Я хочу смоделировать определенное выражение в одном из моих репозиториев, и у меня возникли некоторые проблемы.

У меня сейчас есть:

Mock<Container> returnContainer = new Mock<Container>();
Mock<IRepository<Container>> CntnrRepository =
    new Mock<IRepository<Container>>();

CntnrRepository.Setup<Container>(repo => repo
    .Find(x => x.Name == "foo")
    .Returns(returnContainer.Object);

Всякий раз, когда запускается следующий код, он возвращает нольвместо моего Mock<Container> выше.

Container found = 
    containerRepository.Find(x => x.Name == cntnrName);

Что я здесь не так делаю?

Редактировать

Ниже приведен код, использующий внедренный репозиторий:

public int Foo(Guid id, string name)
{
    Container found = 
        containerRepository.Find(x => x.Name == name);

    if (found != null)
        return CONTAINER_NOT_FREE;

    Container cntnrToAssociate =
        containerRepository.Find(x => x.Id == cntnrId);

    if (cntnrToAssociate == null)
        return CONTAINER_NOT_FOUND;

    return OK;
}

В приведенном выше коде для одного из моих тестов мне нужно вернуть значение только в первом запросе (Find) в containerRepository

Ответы [ 4 ]

4 голосов
/ 27 января 2012

Редактировать: я обновил решение, это работает с аргументами Expression Edit2: я добавил более общее решение (см. Последний тест), в котором используется ExpressionComparer из IQToolkit.Это должно удовлетворять любому универсальному выражению в настройке

Если вы установите его, как показано ниже, вы можете вернуть не нуль только для некоторых входных аргументов

    [Test]
    public void SetupFunc_TestWithExpectedArgument_ReturnsNotNull()
    {
        // Arrange
        var repository = new Mock<IRepository<Container>>();
        repository.Setup(r => r.Find(It.Is<Expression<Func<Container, bool>>>(x => NameIsFoo(x)))).Returns(new Container());

        // Act
        Container container = repository.Object.Find(x => x.Name == "foo");

        // Assert
        Assert.That(container, Is.Not.Null);
    }

    [Test]
    public void SetupFunc_TestWithOtherargument_ReturnsNull()
    {
        // arrange
        var repository = new Mock<IRepository<Container>>();
        repository.Setup(r => r.Find(It.Is<Expression<Func<Container, bool>>>(x => NameIsFoo(x)))).Returns(new Container());

        // Act
        Container container = repository.Object.Find(x => x.Name == "bar");

        // Assert
        Assert.That(container, Is.Null);
    }

    private static bool NameIsFoo(Expression<Func<Container, bool>> expression)
    {
        if (expression == null)
            return false;

        var mExpr = expression.Body as BinaryExpression;

        if (mExpr == null)
            return false;

        var constantExpression = mExpr.Right as ConstantExpression;

        if (constantExpression == null)
            return false;

        return Equals(constantExpression.Value, "foo");
    }

    [Test]
    public void SetupFunc_TestWithExpectedArgumentUsingExpressionComparer_ReturnsNotNull()
    {
        // Arrange
        var repository = new Mock<IRepository<Container>>();

        Expression<Func<Container, bool>> expectedArgument = x => x.Name == "foo";

        repository.Setup(r => r.Find(It.Is<Expression<Func<Container, bool>>>(x => ExpressionComparer.AreEqual(x, expectedArgument)))).Returns(new Container());

        // Act
        Container container = repository.Object.Find(x => x.Name == "foo");

        // Assert
        Assert.That(container, Is.Not.Null);
    }
0 голосов
/ 28 января 2012

Обходной путь

Я не нашел решения, которое искал на данный момент, но нашел достойный обходной путь.В итоге я использовал метод Moq Callback, чтобы делать то, что я хочу.Пожалуйста, смотрите следующее:

int numCalls = 0;
List<Container> expectedContainers = new List<Container>();

//Add list of expected containers here.  Since I want the first call to return null
//and the 2nd call to return a valid container object I will fill the array with null
//in the 1st entry and a valid container in the 2nd
expectedContainers.Add(null);
expectedContainers.Add(new Container());

CntnrRepository.Setup<Container>(
                repo => repo.Find(It.IsAny<Expression<Func<Container, bool>>>()))
                .Returns(() => returnContainers[numCalls])
                .Callback(() => numCalls++);

Приведенный выше код позволяет мне возвращать разные результаты в разное время из одного и того же хранилища.

Поэтому вместо поиска конкретных выражений linq я возвращаю правильные объекты на основе количества вызовов, которые функция делает в хранилище.

0 голосов
/ 28 января 2012

Похоже, что ответ "это невозможно сделать".

См. Ответ Джейсона Пуньона на Moq.Mock - как настроить метод, который принимает выражение .

Согласно его ответу, вы можете использовать

CntnrRepository.Setup(repo => repo.FindAll(
    It.IsAny<Expression<Func<Container, bool>>>()))
    .Returns(returnContainer.Object);

но вы не можете наложить какие-либо ограничения на выражение. Я полагаю, вы могли бы разобрать выражение, используя It.Is, но это, скорее всего, больше проблем, чем стоит издеваться.

Это одна из нескольких причин, по которым я нахожусь в лагере " не выставлять IQueryable для репозиториев ". В то время как вы выставляете только IList, использование Expression идет по тому же скользкому склону ИМХО.

0 голосов
/ 21 января 2012

Следующее, кажется, возвращает ненулевой объект:

    static void Main(string[] args)
    {
        Mock<Container> returnContainer = new Mock<Container>();
        var CntnrRepository = new Mock<IRepository<Container>>();

        CntnrRepository.Setup<Container>(repo => repo.Find(x => x.Name == "foo")).Returns(returnContainer.Object);

        var found = CntnrRepository.Object.Find(x => x.Name == "foo");

        // Or if you want to pass the mock repository to a method
        var container = GetContainer(CntnrRepository.Object);
    }

    public static Container GetContainer(IRepository<Container> container)
    {
        return container.Find(x => x.Name == "foo");
    }

Вместо вызова containerRepository.Find необходимо вызвать containerRepository.Object.Find. Я даже не смог скомпилировать код, если отбросил часть .Object.

Редактировать: я добавил пример, как передать IRepository в метод

...