簡體   English   中英

如何正確地對與Entity Framework / DbContext耦合的存儲庫模式方法進行單元測試?

[英]How do I properly unit test a repository-pattern method, that has coupling to Entity Framework / DbContext?

我是單元測試的新手,希望編寫一個N層的應用程序,因此我的ORM從BLL中抽象出來,中間是一個存儲庫模式。 我將如何對與EF(DbContext)耦合的存儲庫模式方法進行單元測試?

我的SUT方法在這里:

public IList<Volunteer> GetAllVolunteers() {
    return dbctx.Volunteer.ToList();
}

到目前為止,我的單元測試:

[Fact]
public void GetAllVolunteersMethodWorks() {
    // Arrange
    var fakeVolunteer = new Volunteer { Id = 0, Name = "Dummy" };
    var expected = new List<Volunteer>(new Volunteer[] { fakeVolunteer });

    var mockedCtxVolunteer = new Mock<DbSet<Volunteer>>();
    mockedCtxVolunteer.Setup(m => m.ToList()).Returns(expected);

    var mockedctx = new Mock<RSMContext>();
    mockedctx.Setup(m => m.Volunteer).Returns(mockedCtxVolunteer.Object);

    // Act
    var volunteerRepo = new VolunteerRepository(mockedctx.Object);
    var result = volunteerRepo.GetAllVolunteers();

    // Assert
    Assert.Equal(expected, result);
}

我收到一個錯誤,即System.NotSupportedException:'表達式引用了一個不屬於模擬對象的方法:m => m.ToList()因此,如何正確模擬DbSet以便使我的假數據駐留在那里以正確測試我的VolunteerRepository .GetAllVolunteers()方法有效嗎?

編輯(解決方案):

var mockedDbCtxVolunteer = new Mock<DbSet<Volunteer>>();
mockedDbCtxVolunteer.As<IQueryable<Volunteer>>().Setup(m => m.GetEnumerator()).Returns(expected.GetEnumerator());

我們正在使用一個輔助類來模擬DbSet。 它使用內部數據存儲集中的模擬項目。

public class MockDbSet<T> : IDbSet<T> where T : class {

    readonly HashSet<T> _data;
    readonly IQueryable _query;

    public MockDbSet() : this(new HashSet<T>()) {            
    }

    public MockDbSet(params T[] entries) : this(new HashSet<T>(entries)) {
    }

    private MockDbSet(IEnumerable<T> data) {
        _data = new HashSet<T>(data);
        _query = _data.AsQueryable();
    }

    public T Add(T item) {
        _data.Add(item);
         return item;
    }

    public T Remove(T item) {
        _data.Remove(item);
        return item;
    }

    Type IQueryable.ElementType {
        get { return _query.ElementType; }
    }

    IEnumerator IEnumerable.GetEnumerator() {
        return _data.GetEnumerator();
    }

    // implement other members of IDbSet<T> ...
}

然后在單元測試中,使用Moq設置數據上下文。 Volunteer集的訪問將被重定向到模擬實現。

var mockedVolunteer1 = new Volunteer { Name = "Uno" };
var mockedDbSet = new MockDbSet<Volunteer> {
     mockedVolunteer1, mockedVolunteer2, mockedVolunteer3
};

var mockedContext = new Mock<MyDataContext>();
mockedContext.Setup(ctx => ctx.Volunteer).Returns(mockedDbSet);

使用MockDbSet類的優點是,您不必猜測測試將訪問哪些方法,因此不需要模擬,也不必每次都設置模擬的方法。

ToList() Moq(和其他庫) 無法模擬 的擴展方法 不必擔心模擬該方法,而可以模擬它的用途: GetEnumerator()

var mockedCtxVolunteer = new Mock<DbSet<Volunteer>>();
mockedCtxVolunteer.Setup(m => m.GetEnumerator()).Returns(expected.GetEnumerator());

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM