简体   繁体   中英

Unit Testing Service Layer and Entity Framework 6

I am just starting unit testing, so forgive me if this is an obvious answer.

First, i do not have a repository layer, as i dont think an abstraction over Entity Framework is needed, so i have a project that contains my DbContext, then my service layer calls this.

I am trying to build unit tests for my service layer and had lots of problems so far, but nearly there. So far i have this

[TestClass]
public class PageServiceTests
{
    private Mock<DbSet<Page>> _mockSet;
    private Mock<IDataContext> _mockContext;
    private PageService _service;

    [TestInitialize]
    public void TestInitialize()
    {
        var data = new List<Page>
        {
            new Page { Id = 1, Name = "Page1" },
            new Page { Id = 2, Name = "Page2", IsHomePage = true },
            new Page { Id = 3, Name = "Page3" }
        }.AsQueryable();

        _mockSet = data.MockSet<Page>();

        _mockContext = new Mock<IDataContext>();
        _mockContext.Setup(e => e.IsDetached(It.IsAny<Page>())).Returns(true);
        _mockContext.Setup(e => e.Pages).Returns(_mockSet.Object);
        _mockContext.Setup(e => e.Set<Page>()).Returns(_mockSet.Object);

        _service = new PageService(_mockContext.Object);
    }

    [TestMethod]
    public void GetAllAsync()
    {
        var pages = _service.GetAllAsync().Result;

        Assert.AreEqual(3, pages.Count);
        Assert.AreEqual("Page1", pages.ElementAt(0).Name);
        Assert.AreEqual("Page2", pages.ElementAt(1).Name);
        Assert.AreEqual("Page3", pages.ElementAt(2).Name);
    }

    [TestMethod]
    public void GetHomePageAsync()
    {
        var page = _service.GetHomeAsync().Result;

        Assert.AreEqual("Page2", page.Name);
    }

    [TestMethod]
    public void GetAsync()
    {
        var page = _service.GetAsync(2).Result;

        Assert.AreEqual("Page2", page.Name);
    }

    [TestMethod]
    public void AddAsync()
    {
        var page = new Page
        {
            Name = "New Page",
            IsHomePage = true,
            Index = 1
        };

        var result = _service.AddAsync(page).Result;

        _mockSet.Verify(m => m.Add(It.IsAny<Page>()), Times.Once);
        _mockContext.Verify(m => m.SaveChangesAsync(), Times.Once);
    }
}

Now this all seems to work, but i thought that a test to add a page should look something like this

[TestMethod]
public void AddAsync()
{
    var page = new Page
    {
        Name = "New Page",
        IsHomePage = true,
        Index = 1
    };

    var result = _service.AddAsync(page).Result;

    var pages = _service.GetAllAsync().Result;

    Assert.AreEqual(4, pages.Count);
}

But when i do this, i get a fail because pages only contains 3 records and not 4. I guess that i am not "mocking" the Add method in my service to add the page to DbSet. How do you do this, and is this a better way of doing it, or is what i have the "way" it should be done?

Would it be bad practice to test this against the database directly? It seems like i am jumping through hoops just to get the mocking framework to work with entity framework..

With your unit tests, you are testing Linq to Objects, not Linq to Entities. This, sometimes is a big difference and still can result in runtime errors if you're not querying a real database.

For a long time, I did the same... testing my repositories against mocked DbSets seemed as a good practice to me. But not anymore. Today it is very easy to create a test database at runtime with code first. Of course, you need to prepare some sample data. But this is what you have to do anyway (In your case, setting up the mocks).

I am curious what other people will answer.

For your method calls ending in Async , it doesn't look like it's really running async since I don't see that or the await modifiers being used. If you call the async call without awaiting it, you would probaby see 3 items in the database as the database add record operation could complete after that check. I think if you used synchronous methods, or decorated with async/await, your unit test would work fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM