gpt4 book ai didi

entity-framework - 如何构建工作单元/服务层/存储库,以便它们与 DI (Unity) 和 Moq 一起进行单元测试

转载 作者:行者123 更新时间:2023-12-04 21:42:59 25 4
gpt4 key购买 nike

我有一个 MVC 应用程序(EF6、SQL Server CE 4),我最近对其进行了重构以添加 UnitOfWork类和一个服务层(这样我可以为每个请求使用一个 DbContext,并成功进行事务)。

以前,我使用 Unity 将存储库注入(inject) Controller 。我的单元测试(针对 Controller )很容易设置——我只是模拟了每个存储库,并将它们传递给 Controller ​​构造函数。

重构后,我现在使用 Unity 注入(inject)服务层(到 Controller )和 UnitOfWork (进入服务层)。服务层现在实例化每个存储库,通过传递 UnitOfWork.DbContext到存储库的构造函数。

在我的单元测试中,我试图模拟 UnitOfWork , 和 ServiceLayer(并将模拟的 UnitOfWork 对象传递给 ServiceLayer 的构造函数)。但是,测试失败,说“TestFixtureSetup 在 ControllerTest 中失败”。

我认为这是由于我试图通过 UnitOfWork模拟到 ServiceLayer 模拟中,因此将不胜感激有关如何正确执行此操作的任何指导。

下面的相关代码片段。

工作单位

public interface IUnitOfWork:IDisposable
{
void Save();
IDSMContext Context { get; }
}

public class UnitOfWork : IUnitOfWork, IDisposable
{
private IDSMContext _context;

public UnitOfWork()
{
_context = new IDSMContext();
}

public IDSMContext Context
{
get {return _context;}
}

public void Save()
{
_context.SaveChanges();
}

private bool disposed = false;

protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
_context.Dispose();
}
}
this.disposed = true;
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}

服务层
public interface IService
{
// Repositories
IUserRepository Users { get; }
IUserTeamRepository UserTeams { get; }
IPlayerRepository Players { get; }
IGameRepository Games { get; }
IUserTeam_PlayerRepository UserTeamPlayers { get; }

void Save();
}

public class Service: IService, IDisposable
{
private IUnitOfWork _unitOfWork;
private IUserRepository _userRepository;
private IUserTeamRepository _userTeamRepository;
private IPlayerRepository _playerRepository;
private IGameRepository _gameRepository;
private IUserTeam_PlayerRepository _userTeamPlayerRepository;

public Service(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
initialiseRepos();
}

private void initialiseRepos(){
_userRepository = _userRepository ?? new UserRepository(_unitOfWork.Context);
_userTeamRepository = _userTeamRepository ?? new UserTeamRepository(_unitOfWork.Context);
_playerRepository = _playerRepository ?? new PlayerRepository(_unitOfWork.Context);
_gameRepository = _gameRepository ?? new GameRepository(_unitOfWork.Context);
_userTeamPlayerRepository = _userTeamPlayerRepository ?? new UserTeam_PlayerRepository(_unitOfWork.Context);
}

public IUserRepository Users { get { return _userRepository; } }
public IUserTeamRepository UserTeams { get { return _userTeamRepository; } }
public IPlayerRepository Players { get { return _playerRepository; } }
public IGameRepository Games { get { return _gameRepository; } }
public IUserTeam_PlayerRepository UserTeamPlayers { get { return _userTeamPlayerRepository; } }

public void Save()
{
_unitOfWork.Save();
}

Unity 容器实例设置
    Instance.RegisterType<IService, Service>(new PerThreadLifetimeManager())
.RegisterType<IUnitOfWork, UnitOfWork>();

Controller 构造器
public GameController(IService service)
{
_service = service;
}

测试构造函数
_mockUnitOfWork = new Mock<IUnitOfWork>();
_mockServiceLayer = new Mock<IService>(_mockUnitOfWork.Object); //this line fails

测试 Controller 方法
GameController Controller = new GameController(_mockServiceLayer.Object);

最佳答案

如果要测试GameController的方法您只需要模拟/ stub 该类的依赖项。只需这样做:

_mockServiceLayer = new Mock<IService>();
_controller = new GameController(_mockServiceLayer.Object);

当你测试 Controller 时,你不应该担心服务的依赖关系。 UnitOfWork 永远不会暴露在你的服务之外,所以在测试 Controller 时不要担心它。在您的测试中,您现在可以设置在您的 上调用的方法的预期。服务 ,例如验证 Save 是否被调用一次(如果您正在测试服务,那么您会担心 IService.Save 在 IUnitOfWork 的模拟上调用 Save!):
_mockServiceLayer.Verify(s=> s.Save(), Times.Once()); 

您会发现的问题是您的服务类没有从存储库中抽象出 Controller ,因为您的 Controller 将通过 IService 中的属性获取存储库。并直接查询存储库。因此,如果您想测试您的 Controller 方法,您仍然需要模拟存储库,执行以下操作:
//Initialization before each test:
_mockUserRepo = new Mock<IUserRepository>();
//...other repositories
_mockServiceLayer = new Mock<IService>();
_mockServiceLayer.Setup(s => s.Users).Returns(_mockUserRepo.Object);
//... setup properties in IService for other repositories
_controller = new GameController(_mockServiceLayer.Object);

//In some test:
var user = new User();
_mockUserRepo.Setup(s => s.Get(123)).Returns(user);

call some controller method and make sure returned model is "user"

这样,您可能会发现自己配置了一些存储库和 UnityOfWork 返回的期望和数据,只是为了测试 Controller 中的方法!更不用说您的 Controller 类实际上取决于您的存储库,而不仅仅是服务。

另一种方法是,如果您的服务类包含更高级别的方法,例如 获取用户 , 创建用户 添加用户到团队 (可能有多个服务与密切相关的方法)。然后,该服务将屏蔽 Controller 检索/发送数据到存储库和使用 UnitOfWork。

这样在你的测试中你只需要模拟 IService .
例如,典型“GET”操作的测试可能如下所示:
//Arrange
var user = new User();
_mockServiceLayer.Setup(s => s.GetUser(123)).Returns(user);

//Act
var viewResult = _controller.GetUser(123) as ViewResult;

//Assert
Assert.AreEqual(user, viewResult.Model);

希望这将有助于澄清一些事情!

关于entity-framework - 如何构建工作单元/服务层/存储库,以便它们与 DI (Unity) 和 Moq 一起进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21724057/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com