views:

106

answers:

3

Hello there, 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...

A: 

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.

John Nicholas
hmm.. thats pretty much already what I have done atm (if you look at my post, I have updated with some more code)...
ebb
From your update and other comments I can't help. Interested in the solution though as i gave up when i was trying something similar.
John Nicholas
+2  A: 

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

adrift
I'm getting an error at line 22: "Test method MyApp.Test.Services.CustomerServiceTest.GetCustomerTest threw exception: System.ArgumentException: Invalid setup on a non-overridable member:x => x.CustomerId"
ebb
check to make sure CustomerId is virtual, then it should be ok
adrift
so I have to edit the generated code from EF?
ebb
I did not know that Customer was generated by EF; in that case I don't know that you can do this, and maybe your first Assert.IsNotNull is the best that can be done, and that should work using the code above, though I am not sure whether the test is useful to you. Might be the topic for a different question (how to Mock EF entities).
adrift
Just a thought - I haven't used EF so I don't know, but my comment above was based on the assumption that EF will regenerate this class, overwriting any changes you make. You could still change the generated class, but would have to make the same changes each time the class was regenerated. I don't know if there is a better approach to this. It would be ideal if you could create your own entity classes and use attributes to direct EF to use your class instead of generating one, but I have no idea whether this is possible.
adrift
A: 

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.

Lunivore