你的控制器的单元测试不应该使用你的CustomMembershipProvider
或标准SQL提供程序。如果进行单元测试,应该使用一个存根/虚拟对象,以完全控制其返回结果。如果在测试中使用真实的提供程序,则不再是单元测试,而是集成测试。
为了实现单元测试能力,你需要定义一个虚拟的提供程序,可以使用模拟框架或手动实现带有“预设”结果的虚拟对象。虚拟对象可能如下所示:
public class FakeMembershipProvider : MembershipProvider
{
public MembershipCreateStatus CreateStatus = MembershipCreateStatus.Success;
public void CreateUser((string username, string password, string email,
string passwordQuestion, string passwordAnswer, bool isApproved,
object providerUserKey, out MembershipCreateStatus status)
{
status = CreateStatus;
}
...
}
让控制器将提供程序作为构造函数参数
public class AccountController
{
private readonly MembershipProvider _membershipProvider;
public AccountController(MembershipProvider membershipProvider)
{
_membershipProvider = membershipProvider;
}
public ActionResult Register(RegistrationModel model)
{
MembershipCreateStatus result;
_membershipProvider.CreateUser(model.Email, model.Password, ..., out result);
return View();
}
}
在单元测试中,您希望根据要测试的内容设置虚拟对象,并且您可以针对每个注册结果断言您期望的结果。
[Test]
void Should_display_success_view_when_user_successfully_created()
{
var membershipProvider = new FakeMembershipProvider();
membershipProvider.CreateStatus = MembershipCreateStatus.Success;
var controller = new AccountController(membershipProvider);
var model = new RegistrationModel();
var result = controller.Register(model) as ViewResult;
Assert.That(result.Name, Is.EqualTo("ExpectedViewName"));
}
由于MemberShipProvider相当庞大,您可能不会使用其中的所有内容,因此使用@SteveWilkes的方法包装Membership类来创建更小、更有针对性的接口可能是明智的选择。此外,模拟框架可以为您节省大量工作。为了将控制器与新依赖项连接起来,您必须创建一个新的ControllerFactory。
AspDotNetMembershipProvider
应该实现接口吗?像这样public class AspDotNetMembershipProvider : IMembershipProvider { ...
- marapetMembership
类是静态的,但底层提供程序由ASP运行时设置...我在想我也可以设置它。 - Radu DAccountController
直接访问静态Membership
类不是最优的设计决策;如果您已经在使用仓储的注入,我不确定为什么您想要避免对其他依赖项进行注入?正如 @PHeiberg 所提到的,通过 Microsoft 的 Membership 系统运行测试使它们成为了集成测试而不是单元测试,并且是不必要的;Microsoft 已经为您测试了那部分内容。没有注入就依赖于 Membership 的控制器会产生隐藏的依赖关系,您应该始终避免这种情况。 - Steve Wilkes