gpt4 book ai didi

asp.net-mvc - 存储库模式和单元测试 ASP.NET Web API

转载 作者:行者123 更新时间:2023-12-04 10:53:41 25 4
gpt4 key购买 nike

我刚刚开始深入研究单元测试,并且刚刚开始掌握存储库模式和 IoC。然而,我不认为我完全理解它,因为它的某些部分似乎有点傻。让我解释。

我的 Controller :

public class UserProfileController : ApiController
{
private IUserProfileRepository repository;

// Optional constructor, passes repository, allows dependency injection
public UserProfileController(IUserProfileRepository userProfileRepository)
{
this.repository = userProfileRepository;
}

// GET api/UserProfile
// Returns a list of all users
public IEnumerable<UserProfile> Get()
{
// Only Admins can see a list of users
if (Roles.IsUserInRole("Admin"))
{
return repository.Get();
}
else
{
throw new HttpResponseException(
new HttpResponseMessage(HttpStatusCode.Forbidden)
{
ReasonPhrase = "Administrator access required"
});
}
}

// Other methods, etc.

(请注意,我有一个依赖项 Roles.IsUserInRole("Admin") 我无法弄清楚如何抽象,这会导致一些问题)。

我的典型 repo 界面:
public interface IUserProfileRepository : IDisposable
{
IEnumerable<UserProfile> Get();
// Other methods, etc.
}

repo :
public class UserProfileRepository : IUserProfileRepository, IDisposable
{
private OfootContext context;

public UserProfileRepository(OfootContext context)
{
this.context = context;
}

public IEnumerable<UserProfile> Get()
{
return context.UserProfiles.AsEnumerable();
}

// ... More code

所以一切看起来都很好,我已经从我的业务逻辑中抽象出我的业务访问层,现在我可以创建一个假存储库来运行单元测试。

假 repo :
public class FakeUserProfileRepository : IUserProfileRepository, IDisposable
{
private List<UserProfile> context;

public FakeUserProfileRepository(List<UserProfile> context)
{
this.context = context;
}

public IEnumerable<UserProfile> Get()
{
return context.AsEnumerable();
}

和测试:
[TestMethod]
public void GetUsers()
{
// Arrange
var items = new List<UserProfile>()
{
new UserProfile
{
UserId = 1,
Username = "Bob",
},
new UserProfile
{
UserId = 2,
Username = "Bob2",
}
};

FakeUserProfileRepository repo = new FakeUserProfileRepository(
items);
UserProfileController controller = new UserProfileController(
repo);

// Act
IEnumerable<UserProfile> result = controller.Get();

// Assert
Assert.IsNotNull(result);
}

现在我们在同一页面上(并且随时指出任何“代码气味”),这是我的想法:
  • 假存储库要求我重新实现我的所有 Entity Framework 逻辑并将其更改为处理 List 对象。这是我需要调试的更多工作和链中的更多链接。
  • 如果单元测试确实通过了,它不会说明我的访问 EF 的代码,所以我的应用程序仍然可能失败。这只是意味着我需要单独测试我的 EF 代码并让它访问数据库。
  • 从 #1 开始,如果单元测试没有测试 EF 代码,那么它只是在我的 Controller 中处理我的身份验证、授权和用户创建代码。它不能,因为 WebSecurity 和 Roles 类访问了我的数据库。
  • 如果我要使用数据库来测试 EF 代码(第 2 点)并且需要一个数据库来测试 Controller 代码(用于身份验证和授权,第 3 点),那么为什么还要费心使用存储库进行抽象。为什么我不只是抽象上下文(使用 IContext 或其他东西?)并连接到使用 DropCreateDatabaseAlways 类填充的测试数据库中?

  • 如果我确实找到了一种将用户帐户垃圾抽象出来的方法,我仍然只是在改组代码并创建更多代码(甚至可能是两倍?因为我需要创建假货),我可以在其中替换 Context。

    我的问题是:我错过了什么?它是一个整体概念还是特定的东西?

    最佳答案

    你在正确的轨道上。启动和运行总是很痛苦,但你会发现它在路上得到了返回。

    我建议使用像 Moq 这样的框架,而不是创建“假”对象。 .它允许您在测试时设置所需的行为,而不是重新实现整个接口(interface)。例如,在您的测试中,您可以简单地编写:

        Mock<IUserProfileRepository> mockUserRepo = new Mock<IUserProfileRepository>();
    var items = new List<UserProfile>()
    {
    new UserProfile
    {
    UserId = 1,
    Username = "Bob",
    },
    new UserProfile
    {
    UserId = 2,
    Username = "Bob2",
    }
    };
    mockUserRepo.Setup(m => m.Get().Returns(items.AsEnumerable());
    UserProfileController controller = new UserProfileController(
    mockUserRepo.Object);

    // Act
    IEnumerable<UserProfile> result = controller.Get();
    //Now you can keep varying the mock response by changing the Setup(), so now
    //check for null response handling, 0 items, exceptions etc...

    所有这些努力的最终结果是您已经将测试完全隔离到您的 Controller ,没有数据库依赖项,您可以轻松更改输入而无需编写类,而是使用模拟设置。

    如果您遵循这种简单的架构模式,您将获得出色的可测试性和清晰的关注点分离。随着系统中的事情变得越来越复杂,您可以利用像 Unity 这样的 DI 容器。

    在身份验证部分,我建议创建可以用来装饰方法的属性,例如 ASP.Net MVC 使用:[Authorization(Roles="Admin")] 作为示例。这创建了另一种有用的横切模式,使 Auth 内容与 Controller 中的业务逻辑分离。

    关于asp.net-mvc - 存储库模式和单元测试 ASP.NET Web API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14531888/

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