簡體   English   中英

如何使用 moq 和 xunit 測試業務邏輯方法?

[英]How to test business logic methods using moq and xunit?

我正在使用通用存儲庫模式和我的業務邏輯中的這段代碼。

public class FolderManager : GenericManager<Folder>, IFolderService
{
    private readonly IGenericDal<Folder> _genericDal;
    private readonly IFolderDal _folderDal;
    public FolderManager(IFolderDal folderDal,IGenericDal<Folder> genericDal) : base(genericDal)
    {
        _genericDal = genericDal;
        _folderDal = folderDal;
    }

    public async Task<List<Folder>> GetFoldersByUserId(int id)
    {
        return await _genericDal.GetAllByFilter(I => I.AppUserId == id && I.IsDeleted == false && I.ParentFolderId==null);
    } ...another methods

IFolderService 接口:

public interface IFolderService : IGenericService<Folder>
{
    Task<List<Folder>> GetFoldersByUserId(int id);
}     ...another methods

我想測試GetFoldersByUserId(int id)方法,我試過這個:

public class FolderServiceTest
{  
    private readonly FolderManager _sut;
    private readonly Mock<IGenericDal<Folder>> _folderRepoMock = new Mock<IGenericDal<Folder>>();
    private readonly Mock<IFolderDal> _folderDalMock = new Mock<IFolderDal>();
    public FolderServiceTest()
    {
        _sut = new FolderManager(_folderDalMock.Object, _folderRepoMock.Object);
    }

    [Fact]
    public async Task GetFoldersByUserId_ShouldReturnListOfFolders_WhenUserExist()
    {
        //Arrange
        Mock<IFolderService> folderServiceMock = new Mock<IFolderService>();
        folderServiceMock.Setup(x => x.GetFoldersByUserId(It.IsAny<int>())).ReturnsAsync(GetSampleFolder);
        
        var expected = GetSampleFolder();

        //Act
         
        //returns null beacuse _sut does not work with the setup I wrote above
        //how can i test this method ? 
        var actual = await _sut.GetFoldersByUserId(1); /* */

        //Assert 
        Assert.Equal(expected.Count, actual.Count);

        for (int i = 0; i < expected.Count; i++)
        {
            Assert.Equal(expected[i].FolderName, actual[i].FolderName);
            Assert.Equal(expected[i].Size, actual[i].Size);
        } 
    }

當我開始測試時,實際值為 null 並且測試失敗。 GetSampleFolder方法有一個文件夾列表並返回此列表。 我的問題是如何測試GetFoldersByUserId(int id)方法?

下面的測試顯示了如何正確設置模擬。

棘手的部分是表達式的匹配,Moq 不支持。 這就是我在那里使用It.IsAny匹配器的原因。

[Fact]
public async Task GetFoldersByUserId_ShouldReturnListOfFolders_WhenUserExist()
{
    //Arrange
    var expected = GetSampleFolder();

    var _folderRepoMock = new Mock<IGenericDal<Folder>>();
    _folderRepoMock
        .Setup(x => x.GetAllByFilter(It.IsAny<Expression<Func<Folder, bool>>>()))
        .ReturnsAsync(expected);

    var _folderDalMock = new Mock<IFolderDal>();

    var _sut = new FolderManager(_folderDalMock.Object, _folderRepoMock.Object);


    //Act
    var actual = await _sut.GetFoldersByUserId(1);

    //Assert 
    Assert.Equal(expected.Count, actual.Count);

    for (int i = 0; i < expected.Count; i++)
    {
        Assert.Equal(expected[i].FolderName, actual[i].FolderName);
        Assert.Equal(expected[i].Size, actual[i].Size);
    }
}

如果您真的想測試由GetFoldersByUserId移交給GetAllByFilter的表達式的正確性,您需要做一些額外的工作。 我個人曾經根據您的測試用例而不是It.IsAny...來匹配 expression.ToString() 結果:

Is.Is<Expression<Func<Folder, bool>>>(exp => exp.ToString() == "I => I.AppUserId == 1 && I.IsDeleted == false && I.ParentFolderId==null")

但要使其正常工作,您應該在使用實際值顯式替換封裝的變量引用和常量引用之前對表達式進行部分評估。 如果沒有這一步exp.ToString()將如下所示:

I => (((I.AppUserId == value(StackOverflow.UnitTest1+FolderManager+<>c__DisplayClass3_0).id) AndAlso (I.IsDeleted == False)) AndAlso (I.ParentFolderId == null))

StackOverflow.UnitTest1+FolderManager之類的東西將是導致實際封裝的id變量在您的代碼中的位置的部分。

除了使用exp.ToString()方法,您始終可以修改您的測試以實際使用表達式而不是對其進行匹配:

// just populate to cover all special cases
private List<Folder> testFolderList = new List<Folder>()
{
    new Folder() { AppUserId=1, IsDeleted=false, ParentFolderId=null, FolderName = "a", Size = 1 },
    new Folder() { AppUserId=1, IsDeleted=true, ParentFolderId=null, FolderName = "b", Size = 2 },
    new Folder() { AppUserId=1, IsDeleted=false, ParentFolderId=2, FolderName = "c", Size = 3 },
    new Folder() { AppUserId=2, IsDeleted=false, ParentFolderId=null, FolderName = "a", Size = 4 },
};


[Fact]
public async Task GetFoldersByUserId_ShouldReturnListOfFolders_WhenUserExist()
{
    //Arrange
    var userId = 1;
    var expected = testFolderList
        // replace with expression based on the _contract_ you expect from GetFoldersByUserId
        .Where(I => I.AppUserId == userId && I.IsDeleted == false && I.ParentFolderId == null) 
        .ToList();

    var _folderRepoMock = new Mock<IGenericDal<Folder>>();
    _folderRepoMock
        .Setup(x => x.GetAllByFilter(It.IsAny<Expression<Func<Folder, bool>>>()))
        .ReturnsAsync((Expression<Func<Folder, bool>> exp) =>
        {
            return testFolderList
                // here we explicitly use the expression we got as parameter
                .Where(exp.Compile())
                .ToList();
        });

    var _folderDalMock = new Mock<IFolderDal>();

    var _sut = new FolderManager(_folderDalMock.Object, _folderRepoMock.Object);


    //Act
    var actual = await _sut.GetFoldersByUserId(userId);

    //Assert 
    Assert.Equal(expected.Count, actual.Count);

    for (int i = 0; i < expected.Count; i++)
    {
        Assert.Equal(expected[i].FolderName, actual[i].FolderName);
        Assert.Equal(expected[i].Size, actual[i].Size);
    }
}

這樣,在給定測試數據的情況下,可以根據您的期望來測試構造表達式的適當性。

暫無
暫無

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

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