加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

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

发布时间:2020-12-16 04:11:10 所属栏目:asp.Net 来源:网络整理
导读:我刚刚开始深入研究单元测试,并且刚刚开始掌握存储库模式和IoC.但是,我不认为我完全理解它,因为它的一部分似乎有点傻.让我解释. 我的控制器: public class UserProfileController : ApiController{ private IUserProfileRepository repository; // Optional
我刚刚开始深入研究单元测试,并且刚刚开始掌握存储库模式和IoC.但是,我不认为我完全理解它,因为它的一部分似乎有点傻.让我解释.

我的控制器:

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.
}

回购:

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

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

假回购:

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代码,那么它只是在我的控制器中处理我的身份验证,授权和用户创建代码.它不能,因为WebSecurity和Roles类击中了我的数据库.
>如果我要使用数据库来测试EF代码(第2点)并且需要一个数据库来测试控制器代码(用于身份验证和授权,第3点),那么为什么甚至无法使用存储库进行抽象.为什么我不直接抽象上下文(使用IContext或其他东西?)并连接使用DropCreateDatabaseAlways类填充的测试数据库?

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

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

解决方法

你走在正确的轨道上.让事情正常运转总是令人痛苦,但你会发现它会在未来发展.

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

Mock<IUserProfileRepository> mockUserRepo = new Mock<IUserProfileRepository>();
    var items = new List<UserProfile>()
    {
        new UserProfile
        {
            UserId = 1,}
    };
   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...

所有这些努力的最终结果是你完全将测试隔离到你的控制器,没有数据库依赖关系,你可以轻松改变输入而无需编写类,而是使用模拟设置.

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

在身份验证部分,我建议创建属性,您可以使用ASP.Net MVC使用:[Authorization(Roles =“Admin”)]作为示例来装饰您的方法.这创建了另一个有用的横切模式,使Auth资源与控制器中的业务逻辑分离.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读