问题:
我刚开始使用 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());
}
}
我的问题 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/