ebb
ebb

Reputation: 9377

C# + Mock service layer?

I have just started playing with unit testing / mocks using Moq, and ran into a problem..

I have a Service layer named "CustomerService" which have following code:

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;
    }
}

My repository class is generic, and are following:

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;
                }
            }
        }
    }

Now is my question.. How can I make unit test to check whether my GetCustomerById returns null or not?

Already tried:

[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);
}

without luck...

Upvotes: 6

Views: 17095

Answers (3)

Lunivore
Lunivore

Reputation: 17602

The reason you can't do this is because one lambda x => x.CustomerId == id is not equal to another lambda x => x.CustomerId == id so Moq can't match them.

However, if you had, say, a class with your common operations on:

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

and reused this lambda in both code and test, then your Moq should pass without any problem. You could alternatively use a method on a known instance of a class, as long as it's exactly the same delegate and not merely a similar one or another copy.

PS: Have you considered using Predicate<T> instead of Expression<Func<T, bool>>? It's easier to read and to understand the purpose.

Upvotes: 0

Jeff Ogata
Jeff Ogata

Reputation: 57783

You need to make the Repository<T>.Get method virtual so Moq can override it and return the value you set up:

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

and in your test, change

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

to

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

which says return a new Customer for any Expression<Func<Customer, bool>> passed in. Ideally you would test a specific expression, but per the accepted answer to this SO question, Moq cannot do this.

If you wanted to test that your service layer was not doing anything unexpected to the Customer returned by the repository, instead of testing to see that any Customer was returned, you could set up a mock Customer (being sure to make the CustomerId property virtual) and assert that the Customer returned by the service layer had the expected properties.

[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);
}

HTH

Upvotes: 7

John Nicholas
John Nicholas

Reputation: 4836

you have to create the mock against the interface.

you then need to set the member in the class to the mock instead of the implementor

you then need to call .Setup on the mock you created for the method .. rememver to use method chaining to make it verifiable.

run the method in your test and then call mock.Verify()

Will add code tomorrow if you require. I have loads of code samples at work i can use.

Upvotes: 0

Related Questions