C # + Ложный сервисный слой? - PullRequest
       0

C # + Ложный сервисный слой?

4 голосов
/ 17 октября 2010

Я только что начал играть с юнит-тестированием / mocks, используя Moq, и столкнулся с проблемой ..

У меня есть сервисный слой с именем "CustomerService", который имеет следующий код:

public interface ICustomerService
{
    Customer GetCustomerById(int id);
}

public class CustomerService : ICustomerService
{
    private IRepository<Customer> customerRepository;

    public CustomerService(IRepository<Customer> rep)
    {
        customerRepository = rep;
    }
    public Customer GetCustomerById(int id)
    {
        var customer = customerRepository.Get(x => x.CustomerId == id);

        if (customer == null)
            return null;

        return customer;
    }
}

Мой класс репозитория является универсальным и имеет следующий вид:

public interface IRepository<T> : IDisposable where T : class
    {
        T Get(Expression<Func<T, bool>> predicate);
    }

    public class Repository<T> : IRepository<T> where T : class
    {
        private ObjectContext context;
        private IObjectSet<T> objectSet;

        public Repository()
            : this(new demonEntities())
        {
        }

        public Repository(ObjectContext ctx)
        {
            context = ctx;
            objectSet = context.CreateObjectSet<T>();
        }

        public T Get(Expression<Func<T, bool>> predicate)
        {
            T entity = objectSet.Where<T>(predicate).FirstOrDefault();

            if (entity == null)
                return null;

            return objectSet.Where<T>(predicate).FirstOrDefault();
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (context != null)
                {
                    context.Dispose();
                    context = null;
                }
            }
        }
    }

Теперь мой вопрос .. Как мне выполнить модульный тест, чтобы проверить, возвращает ли мой GetCustomerById значение NULL или нет?

Ужепопробовал:

[TestMethod]
public void GetCustomerTest()
{
    const int customerId = 5;

    var mock = new Mock<IRepository<Customer>>();
    mock.Setup(x => x.Get(z => z.CustomerId == customerId))
        .Returns(new Customer());

    var repository = mock.Object;
    var service = new CustomerService(repository);
    var result = service.GetCustomerById(customerId);

    Assert.IsNotNull(result);
}

без удачи ...

Ответы [ 3 ]

5 голосов
/ 17 октября 2010

Вам нужно сделать метод Repository<T>.Get виртуальным, чтобы Moq мог его переопределить и вернуть значение, которое вы установили:

public virtual T Get(Expression<Func<T, bool>> predicate)

и в своем тесте измените

mock.Setup(x => x.Get(z => z.CustomerId == customerId))
        .Returns(new Customer());

на

mock.Setup(x => x.Get(It.IsAny<Expression<Func<Customer, bool>>>()))
        .Returns(new Customer());

, где указано возвращать новый Customer для любого переданного Expression<Func<Customer, bool>>. В идеале вы должны проверить определенное выражение, но согласно принятому ответу на этот SO вопрос ,Moq не может этого сделать.

Если вы хотите проверить, что ваш уровень обслуживания не делал ничего неожиданного для Customer, возвращаемого хранилищем, вместо того, чтобы проверить, что any Customer было возвращено, вы можете настроить макет Customer (обязательно сделав свойство CustomerId виртуальным) и утверждать, что Customer, возвращаемый сервисным уровнем, имеет ожидаемые свойства.

[TestMethod]
public void GetCustomerTest()
{
    const int customerId = 5;

    var mockCustomer = new Mock<Customer>();

    mockCustomer.SetupGet(x => x.CustomerId)
        .Returns(customerId);

    var mock = new Mock<IRepository<Customer>>();

    mock.Setup(x => x.Get(It.IsAny<Expression<Func<Customer, bool>>>()))
        .Returns(mockCustomer.Object);

    var repository = mock.Object;
    var service = new CustomerService(repository);
    var result = service.GetCustomerById(customerId);

    Assert.AreEqual(customerId, result.CustomerId);
}

НТН

0 голосов
/ 19 октября 2010

Причина, по которой вы не можете сделать это, заключается в том, что одна лямбда x => x.CustomerId == id не равна другой лямбде x => x.CustomerId == id, поэтому Moq не может сопоставить их.Класс с вашими общими операциями на:

public class CustomerQueries {
    public static Predicate<Customer> ById(int id) = x => x.CustomerId == id;
}

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

PS: Вы рассматривали возможность использования Predicate<T> вместо Expression<Func<T, bool>>?Проще читать и понимать цель.

0 голосов
/ 17 октября 2010

вы должны создать макет против интерфейса.

Затем вам нужно установить для члена класса вместо макета

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

запустите метод в вашем тесте и затем вызовите mock.Verify ()

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

...