Использование Moq для проверки параметра типа List <>? - PullRequest
27 голосов
/ 31 октября 2011

Используя Moq, я хотел бы иметь возможность убедиться, что определенные условия выполняются для параметра, передаваемого вызову смоделированного метода. В этом сценарии я хотел бы проверить, что список, передаваемый в метод mocked, имеет определенный размер:

var mockSomeRepository = new Mock<SomeRepository>();
mockSomeRepository.Setup(m => m.Write(It.IsAny<List<SomeDTO>>())).Verifiable();

var mainClass = new MainClass(mockSomeRepository.Object);
List<SomeDTO> someList = GetListWith25Items();

mainClass.DoRepositoryWrite(someList); // calls SomeRepository.Write(someList);

mockSomeRepository.Verify(m => 
    m.Write(It.Is<List<SomeDTO>>(l => l.Count() == 25)), Times.Once());

Проверка assert генерирует исключение, которое говорит, что метод никогда не вызывается таким образом. Однако удаление ограничения и использование Is.Any<List<SomeDTO>>() вместо этого приводит к проходу. Я не уверен, правильно ли я использую It.Is <> () - это то, что я интуитивно хочу, чтобы мой тест выглядел, но я не уверен, правильно ли я использую фреймворк. Как правильно оформить этот тест?

Ответы [ 2 ]

27 голосов
/ 31 октября 2011

Вы можете избавиться от звонка на Настройка и Подтверждаемый на вашем макете.Просто используйте Verify .

Я создал небольшой тест-проект, и это сработало для меня:

using System;
using System.Collections.Generic;
using System.Linq;
using Moq;

namespace csharp
{
    public class SomeDTO { }

    public class SomeRepository { public virtual void Write(List<SomeDTO> list) { } }

    public class MainClass
    {
        private SomeRepository someRepository;

        public MainClass(SomeRepository someRepository) { this.someRepository = someRepository; }

        public void DoRepositoryWrite(List<SomeDTO> list) { this.someRepository.Write(list); }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var mockSomeRepository = new Mock<SomeRepository>();
            var someList = Enumerable.Repeat(new SomeDTO(), 25).ToList();

            var mainClass = new MainClass(mockSomeRepository.Object);
            mainClass.DoRepositoryWrite(someList);

            mockSomeRepository.Verify(m => m.Write(It.IsAny<List<SomeDTO>>()), Times.Once(), "Write was not called");
            mockSomeRepository.Verify(m => m.Write(It.Is<List<SomeDTO>>(l => l.Count == 25)), Times.Once(), "Write was not called with a 25-element-list");
        }
    }
}
6 голосов
/ 31 октября 2011

Когда я отправил этот вопрос, я упустил несколько важных деталей. Я хочу подробно рассказать о том, что на самом деле происходило, если это поможет кому-то в будущем. Мой тестируемый метод фактически очищал список, который передавался в макет:

public class SomeDTO { }

public class SomeRepository
{
    public virtual void Write(IEnumerable<SomeDTO> list) { }
}

public class MainClass
{
    private readonly SomeRepository _someRepository;
    private readonly List<SomeDTO> _testList = new List<SomeDTO>(); 

    public MainClass(SomeRepository someRepository)
    {
        _someRepository = someRepository;
    }

    public void DoRepositoryWrite()
    {
        _testList.AddRange(Enumerable.Repeat(new SomeDTO(), 25));
        _someRepository.Write(_testList);
        _testList.Clear();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var mockSomeRepository = new Mock<SomeRepository>();

        var mainClass = new MainClass(mockSomeRepository.Object);

        mainClass.DoRepositoryWrite();

        mockSomeRepository.Verify(m => m.Write(It.IsAny<IEnumerable<SomeDTO>>()), Times.Once(), "Write was not called");
        mockSomeRepository.Verify(m => m.Write(It.Is<IEnumerable<SomeDTO>>(l => l.Count() == 25)), Times.Once(), "Write was not called with a 25-element-list");
    }
}

Оглядываясь назад, я думаю, что это немного очевидно, но для меня здесь важно то, что насмешка держится за ссылку на список, который был передан. Поэтому вы должны быть осторожны с любыми побочными эффектами, которые изменяют этот список.

Чтобы правильно написать тест, мне нужно было сразу проверить свойства переданного списка при вызове проверяемого метода. Для этого я использовал обратный вызов:

int listCountAtTimeOfCall = 0;
mockSomeRepository.Setup(
    m => m.Write(It.IsAny<IEnumerable<SomeDTO>>())).Callback
        <IEnumerable<SomeDTO>>(list => listCountAtTimeOfCall = list.Count());

... do the work ...

Assert.AreEqual(listCountAtTimeOfCall, 25);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...