c# - 如何在 C# 中使用 XUnit 正确模拟 mongoDb?

标签 c# mongodb unit-testing

问题:

我刚开始使用 xunit 在 C# 中编写单元测试。所以我试图模拟 MongoDB 连接。在我的项目中,我使用了存储库模式,我使用了这样的工作单元类。所以我通过它访问每个存储库。所以工作单元类代码就在这里。

namespace QuestionBank.API.Repositories
{
    public class UnitOfWork : IUnitOfWork
    {
        public readonly IQuestionsBankDBContext _context;

        private readonly ILogger<UnitOfWork> _logger;

        private Dictionary<Type, object> repositories;

        private IQuestionsRepository _questionsRepository;

        private ICampaignQuestionsRepository _campaignQuestionsRepository;

        private ICandidateAnswerRepository _candidateAnswerRepository;

        private IIntergrationEventLogRepository _integrationEventLogRepository;

        private IControlRepository _controlRepository;


        public UnitOfWork(IQuestionsBankDBContext context, ILogger<UnitOfWork> logger)
        {
            _context = context;
            _logger = logger;
        }

        public IQuestionsRepository QuestionsRepository
        {
            get
            {
                this._questionsRepository = new QuestionsRepository(_context as IQuestionsBankDBContext, this, _logger);
                return this._questionsRepository;
            }
        }

        public ICandidateAnswerRepository CandidateAnswerRepository
        {
            get
            {
                this._candidateAnswerRepository = new CandidateAnswerRepository(_context as IQuestionsBankDBContext, this, _logger);
                return this._candidateAnswerRepository;
            }
        }

        public ICampaignQuestionsRepository CampaignQuestionsRepository
        {
            get
            {
                this._campaignQuestionsRepository = new CampaignQuestionsRepository(_context as IQuestionsBankDBContext, this, _logger);
                return this._campaignQuestionsRepository;
            }
        }

        public IIntergrationEventLogRepository IntegrationEventLogRepository
        {
            get
            {
                this._integrationEventLogRepository = new IntergrationEventLogRepository(_context as IQuestionsBankDBContext, this, _logger);
                return this._integrationEventLogRepository;
            }
        }

        public IControlRepository ControlRepository
        {
            get
            {
                this._controlRepository = new ControlRepository(_context as IQuestionsBankDBContext, this, _logger);
                return this._controlRepository;
            }
        }

        public IGenericRepository<TDocument> GetRepository<TDocument>() where TDocument : IDocument
        {
            if (this.repositories == null)
            {
                this.repositories = new Dictionary<Type, object>();
            }

            var type = typeof(TDocument);
            if (!this.repositories.ContainsKey(type))
            {
                this.repositories[type] = new GenericRepository<TDocument>(_context);
            }

            return (IGenericRepository<TDocument>)this.repositories[type];
        }
    }
}

因此,在模拟服务和存储库的单元测试中,我需要将数据库上下文传递给unitofwork。我按照这个方法尝试过。

var mockDbContext = new Mock<QuestionsBankDBContext>();
var dbContext = mockDbContext.Object;
var mock = new Mock<ILogger<UnitOfWork>>();
_logger = mock.Object;
unitOfWork = new UnitOfWork(dbContext, _logger);

questionsService = new QuestionsService(unitOfWork);
campaignQuestionsService = new CampaignQuestionsService(unitOfWork);
tokenService = new TokenService();

stringLocalizer = new Mock<IStringLocalizer<SharedResource>>();

questionBankIntergrationEventService = new Mock<IQuestionBankIntergrationEventService>();

questionsController = new QuestionsController(questionsService, campaignQuestionsService, stringLocalizer.Object, tokenService, questionBankIntergrationEventService.Object);

contextMock = new Mock<HttpContext>();

这是我的数据库上下文类。

using MongoDB.Driver;
using QuestionBank.API.Models;

namespace QuestionBank.API.Data
{
    public class QuestionsBankDBContext : IQuestionsBankDBContext
    {
        public IMongoClient Client { get; set; }
        public IMongoDatabase Database { get; set; }


        public QuestionsBankDBContext(IQuestionBankDatabaseSettings settings)
        {
            Client = new MongoClient(settings.ConnectionString);
            Database = Client.GetDatabase(settings.DatabaseName);
        }
    }
}

然后我编写了一个这样的单元测试。

[Theory]
[InlineData("61879e54e86be1fa5e41831f")]
[InlineData("61879e54e86be1fa5e41831e")]
public async Task GetQuestionById(string questionId)
{
    var actionResult = await questionsController.GetQuestionById(questionId);
    var result = actionResult as ObjectResult;
    Assert.NotNull(result.Value);
    if (result.StatusCode == (int)System.Net.HttpStatusCode.OK)
    {
        Assert.IsType<Questions>(result.Value);
    }
    else if (result.StatusCode == (int)System.Net.HttpStatusCode.NotFound)
    {
        Assert.Contains("ErrorCode", result.Value.ToString());
    }
    else if (result.StatusCode == (int)System.Net.HttpStatusCode.InternalServerError)
    {
        var code = (int)ErroCodes.InternalServerError;
        Assert.Contains(code.ToString(), result.Value.ToString());
    }
}

然后当运行它时它给出 enter image description here

我的问题 Controller GetQuestionById是这样的。

[HttpGet]
//[Authorize(Roles = "SuperAdmin,Admin")]
[Route("getquestionbyidfrombank")]
[ProducesResponseType(typeof(Questions), 200)]
[ProducesResponseType(typeof(string), 404)]
[ProducesResponseType(typeof(string), 500)]
public async Task<IActionResult> GetQuestionById([FromQuery] string questionId)
{
    try
    {
        string errorText;
        if (!string.IsNullOrEmpty(questionId))
        {
            var question = await 
            questionsService.GetQuestionById(questionId);
            return Ok(question);
        }
        else
        {
            errorText = string.Format(stringLocalizer[Constants.ErrorCodeString],
        (int)ErroCodes.SpecifiedItemNotFound,
        stringLocalizer[Helper.ToEnumString(ErroCodes.SpecifiedItemNotFound)]);

            return StatusCode(404, errorText);
        }
    }
    catch (Exception ex)
    {
        string exceptionData =
        $"Exception occured while getiing question by id. " +
        $"\nException Data: Message- {ex.Message}; " +
        $"InnerException- {ex.InnerException}; StackTrace- {ex.StackTrace}";

        string errorText = string.Format(stringLocalizer[Constants.ErrorCodeString],
            (int)ErroCodes.InternalServerError,
            stringLocalizer[Helper.ToEnumString(ErroCodes.InternalServerError)]);

        return StatusCode(500, errorText);
    }
}

这就是我进行服务实例化的方式。

    public QuestionsService(IUnitOfWork unitOfWork)
    {
       _unitOfWork = unitOfWork;
    }

通过id函数获取问题

public async Task<Questions> GetQuestionById(string id)
{
    var question = await _unitOfWork.QuestionsRepository.FindByIdAsync(id);
    return question;
}

有人可以帮我正确编写这个单元测试并解决这个问题吗?我做了很多尝试来找到一种方法来做到这一点,但我无法做到。谢谢

最佳答案

不要——仅仅因为某些东西可以被 mock ,并不意味着它应该被 mock 。

相反,您可以使用 Docker 镜像来运行 Mongo,为每个测试类 init 删除并创建一个数据库,为每个测试 init 删除集合。

在没有数据库的情况下测试 DAL(数据访问层)是浪费时间,并且不会帮助您真正找到错误。

当您对其他组件进行单元测试时,模拟整个 DAL 以返回您期望的对象。

并且不要跳过为整个服务编写测试,其中包括空/通过测试配置数据数据库预先填充。

此外,您的 DbContext 中的字段应该是私有(private)的,而不是公共(public)的,并且在传递 DbContext 的常量实例时到 MongoDB 是可以的,因为 MongoDB 上下文是无状态的(没有事务和连接被池化),通常传递 DbContext via 构造函数是错误的(因为关系数据库有事务并且连接不应该保持打开状态),而是传递 Func<DbContext> (例如 public MyClass(Func<DbContext> contextConstructor) )它返回构造函数并具有 DbContext实现IDisposable 。这样客户端类就可以做 using (context = contextCreator()) { ... } .

关于c# - 如何在 C# 中使用 XUnit 正确模拟 mongoDb?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69960520/

相关文章:

javascript - NodeJS 和 MongoDB - ObjectId 数组如何返回对象

node.js - MongoDB/ Mongoose :Getting incorrect distance with geoNear method using geoJSON

python - django-crispy-form : Unit test fails because of TypeError of helper object

c# - 在 File.Exists 上找不到文件,路径中有空格

c# - 无法在 JAVA 中验证签名,但在 .NET 中验证成功

c# - 有没有办法使用 C# 在 Excel 中模仿格式刷?

Mongodb解释聚合框架

Java:单元测试图像操作

c# - 存在依赖时如何设计可模拟性?

c# - 使用 InMemoryConnection 测试 ElasticSearch