Asp.net mvc 存储库模式和单元测试ASP.NET Web API
我刚刚开始深入单元测试,并且刚刚开始掌握存储库模式和IoC。然而,我不认为我完全理解它,因为它的某些部分似乎有点愚蠢。让我解释一下 我的控制器:Asp.net mvc 存储库模式和单元测试ASP.NET Web API,asp.net-mvc,unit-testing,asp.net-web-api,repository-pattern,Asp.net Mvc,Unit Testing,Asp.net Web Api,Repository Pattern,我刚刚开始深入单元测试,并且刚刚开始掌握存储库模式和IoC。然而,我不认为我完全理解它,因为它的某些部分似乎有点愚蠢。让我解释一下 我的控制器: public class UserProfileController : ApiController { private IUserProfileRepository repository; // Optional constructor, passes repository, allows dependency injection
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.
公共类UserProfileController:ApicController
{
私有IUSERPROFILE存储库;
//可选构造函数,传递存储库,允许依赖项注入
公共UserProfileController(IUserProfileRepository userProfileRepository)
{
this.repository=userProfileRepository;
}
//获取api/UserProfile
//返回所有用户的列表
公共IEnumerable Get()
{
//只有管理员才能看到用户列表
if(Roles.IsUserInRole(“Admin”))
{
返回repository.Get();
}
其他的
{
抛出新的HttpResponseException(
新的HttpResponseMessage(HttpStatusCode.Forbidden)
{
ReasonPhrase=“需要管理员访问”
});
}
}
//其他方法等。
(注意,我有一个依赖项Roles.IsUserInRole(“Admin”),我不知道如何进行抽象,这会导致一些问题)
我的典型回购界面:
public interface IUserProfileRepository : IDisposable
{
IEnumerable<UserProfile> Get();
// Other methods, etc.
}
公共接口IUserProfileRepository:IDisposable
{
IEnumerable Get();
//其他方法等。
}
回购:
公共类UserProfileRepository:IUserProfileRepository,IDisposable
{
语境的私密性;
公共UserProfileRepository(OfootContext上下文)
{
this.context=上下文;
}
公共IEnumerable Get()
{
返回context.UserProfiles.AsEnumerable();
}
//…更多代码
所以一切看起来都很好,我已经从我的业务逻辑中抽象出了我的业务访问层,现在我可以创建一个假的存储库来运行单元测试
虚假回购:
public class FakeUserProfileRepository : IUserProfileRepository, IDisposable
{
private List<UserProfile> context;
public FakeUserProfileRepository(List<UserProfile> context)
{
this.context = context;
}
public IEnumerable<UserProfile> Get()
{
return context.AsEnumerable();
}
公共类FakeUserProfileRepository:IUserProfileRepository,IDisposable
{
私有列表上下文;
公共FakeUserProfileRepository(列表上下文)
{
this.context=上下文;
}
公共IEnumerable Get()
{
返回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);
}
[TestMethod]
public void GetUsers()
{
//安排
var items=新列表()
{
新用户配置文件
{
UserId=1,
Username=“Bob”,
},
新用户配置文件
{
UserId=2,
Username=“Bob2”,
}
};
FakeUserProfileRepository repo=新建FakeUserProfileRepository(
项目);
UserProfileController=新的UserProfileController(
回购协议);
//表演
IEnumerable result=controller.Get();
//断言
Assert.IsNotNull(结果);
}
现在我们已经在同一页上了(并且可以随意指出任何“代码气味”),下面是我的想法:
我的问题是:我遗漏了什么?这是一个整体概念还是一个具体的概念?你走在正确的轨道上。启动和运行东西总是很痛苦的,但你会发现这会带来回报 与其创建“伪”对象,不如推荐一个类似的框架。它允许您在测试时设置所需的行为,而不是重新实现整个接口。例如,在测试中,您可以简单地编写:
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...
Mock mockUserRepo=new Mock();
var items=新列表()
{
新用户配置文件
{
UserId=1,
Username=“Bob”,
},
新用户配置文件
{
UserId=2,
Username=“Bob2”,
}
};
mockUserRepo.Setup(m=>m.Get().Returns(items.AsEnumerable());
UserProfileController=新的UserProfileController(
mockUserRepo.Object);
//表演
IEnumerable result=controller.Get();
//现在,您可以通过更改Setup()来不断更改模拟响应,所以现在
//检查空响应处理、0项、异常等。。。
所有这些工作的最终结果是,您已经将测试完全隔离到您的控制器,没有DB依赖关系,您可以轻松地更改输入,而无需编写类,而是使用模拟设置
如果您遵循这个简单的体系结构模式,您将获得极好的可测试性和清晰的关注点分离。随着系统中的事情变得更加复杂,您可以利用Unity这样的DI容器
在身份验证部分,我建议您创建一些属性来装饰您的方法,例如ASP.Net MVC使用:[Authorization(Roles=“Admin”)]作为示例。这将创建另一个有用的横切模式
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...