Unit testing LINQ to SQL CRUD operations using Moq

2019-06-24 10:58发布

问题:

I've had a look around at other questions, but nothing really matches what I'm looking for... mainly because I'm not 100% certain on what it is I'm looking for!

Basically I'm working on a new project at the moment, I've created my abstraction layer for DB entities and set the DAC to be repositories. I'm wanting to use unit testing for this with Mock objects, however I've hit a intellectual wall with CRUD (specifically C) operations, my unit test class:

[TestClass]
public class RepositoryTest
{
    private Mock<IPersonRepository> _repository;
    private IList<IPerson> _personStore;

    [TestInitialize()]
    public void Initialize()
    {
        _personStore= new List<IPerson>() {
            new Person() { Name = "Paul" },
            new Person() { Name = "John" },
            new Person() { Name = "Bob" },
            new Person() { Name = "Bill" },
        };

        _repository = new Mock<IPersonRepository>();
        _repository.Setup(r => r.Entirely()).Returns(_personStore.AsQueryable());
    }

    [TestCleanup()]
    public void Cleanup()
    {
        _personStore.Clear();
    }

    [TestMethod()]
    public void Can_Query_Repository()
    {
        IEnumerable<IPerson> people = _repository.Object.Entirely();
        Assert.IsTrue(people.Count() == 4);
        Assert.IsTrue(people.ElementAt(0).Name == "Paul");
        Assert.IsTrue(people.ElementAt(1).Name == "John");
        Assert.IsTrue(people.ElementAt(2).Name == "Bob");
        Assert.IsTrue(people.ElementAt(3).Name == "Bill");
    }

    [TestMethod()]
    public void Can_Add_Person()
    {
        IPerson newPerson = new Person() { Name = "Steve" };

        _repository.Setup(r => r.Create(newPerson));

        // all this Create method does in the repository is InsertOnSubmit(IPerson)
        // then SubmitChanges on the data context
        _repository.Object.Create(newPerson);

        IEnumerable<IPerson> people = _repository.Object.Entirely();
        Assert.IsTrue(people.Count() == 5);
    }
}

My Can_Query_Repository method is successful, however the Can_Add_Person method fails assertion. Now, do I need to do:

  1. Setup the .Create method of the Mock repository to add the element to the _personStore?
  2. Do something else similar?
  3. Abandon all hope as what I want to achieve isn't possible and I'm doing it all wrong!

As always, any help / advice appreciated!

回答1:

Ideally you would do some integration tests for those, but if you want to unit test it, there are possible avenues, including one that wasn't mentioned in the comments of the original question.

The first one. When testing your crud, you can use .Verify to check that the Create methods have really been called.

mock.Verify(foo => foo.Execute("ping"));

With Verify, you can check that the argument was a certain argument, of a certain type and the number of times the method was actually called.

The second one. Or if you want to verify the actual objects that have been added in the repository's collection, what you could do is use the .Callback method on your mock.

In your test create a list that will receive the objects you create. Then on the same line where you call your setup add a callback that will insert the created objects in the list.

In your asserts you can check the elements in the list, including their properties to make sure the correct elements have been added.

var personsThatWereCreated = new List<Person>(); 

_repository.Setup(r => r.Create(newPerson)).Callback((Person p) => personsThatWereCreated.Add(p));

// test code
// ...

// Asserts
Assert.AreEuqal(1, personsThatWereCreated.Count());
Assert.AreEqual("Bob", personsThatWereCreated.First().FirstName);

Granted in your actual example, you create the person and then add it in the setup. Using the callback method here would not be useful.

You could also use this technique to increment a variable to count the number of times it was called.