Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 单元测试ASP.NET MVC5应用程序_C#_Asp.net_Unit Testing_Asp.net Mvc 5 - Fatal编程技术网

C# 单元测试ASP.NET MVC5应用程序

C# 单元测试ASP.NET MVC5应用程序,c#,asp.net,unit-testing,asp.net-mvc-5,C#,Asp.net,Unit Testing,Asp.net Mvc 5,我通过添加一个新属性(如教程所示)来扩展ApplicationUser类 ) 现在我想创建一个单元测试来验证AccountController是否正确保存了生日 我已经创建了一个名为TestUserStore的内存中用户存储 [TestMethod] public void Register() { // Arrange var userManager = new UserManager<ApplicationUser>(new TestUserStore<Ap

我通过添加一个新属性(如教程所示)来扩展ApplicationUser类 )

现在我想创建一个单元测试来验证AccountController是否正确保存了生日

我已经创建了一个名为TestUserStore的内存中用户存储

[TestMethod]
public void Register()
{
    // Arrange
    var userManager = new UserManager<ApplicationUser>(new TestUserStore<ApplicationUser>());
    var controller = new AccountController(userManager);

    // This will setup a fake HttpContext using Moq
    controller.SetFakeControllerContext();

    // Act
    var result =
        controller.Register(new RegisterViewModel
        {
            BirthDate = TestBirthDate,
            UserName = TestUser,
            Password = TestUserPassword,
            ConfirmPassword = TestUserPassword
        }).Result;

    // Assert
    Assert.IsNotNull(result);

    var addedUser = userManager.FindByName(TestUser);
    Assert.IsNotNull(addedUser);
    Assert.AreEqual(TestBirthDate, addedUser.BirthDate);
}
在最底层,样板代码包含了这个小贴士

private IAuthenticationManager AuthenticationManager
{
    get
    {
        return HttpContext.GetOwinContext().Authentication;
    }
}
这就是问题的根源所在。对GetOwinContext的调用是一个扩展方法,我不能模拟它,也不能用存根替换它(当然,除非我更改样板代码)

当我运行这个测试时,我得到一个异常

Test method MVCLabMigration.Tests.Controllers.AccountControllerTest.Register threw exception: 
System.AggregateException: One or more errors occurred. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at System.Web.HttpContextBaseExtensions.GetOwinEnvironment(HttpContextBase context)
at System.Web.HttpContextBaseExtensions.GetOwinContext(HttpContextBase context)
at MVCLabMigration.Controllers.AccountController.get_AuthenticationManager() in AccountController.cs: line 330
at MVCLabMigration.Controllers.AccountController.<SignInAsync>d__40.MoveNext() in AccountController.cs: line 336
测试方法MVCLabMigration.Tests.Controllers.AccountControllerTest.Register引发异常:
System.AggregateException:发生一个或多个错误。-->System.NullReferenceException:对象引用未设置为对象的实例。
位于System.Web.HttpContextBaseExtensions.GetowinenEnvironment(HttpContextBase上下文)
位于System.Web.HttpContextBaseExtensions.GetOwinContext(HttpContextBase上下文)
在AccountController.cs中的MVCLabMigration.Controllers.AccountController.get_AuthenticationManager()处:第330行
在AccountController.cs中的MVCLabMigration.Controllers.AccountController.d_u40.MoveNext()处:第336行
在以前的版本中,ASP.NET MVC团队非常努力地使代码可测试。表面上看来,现在测试AccountController并不容易。我有一些选择

我能

  • 修改boiler plate代码,使其不会调用扩展方法并在该级别处理此问题

  • 为测试目的设置OWin管道

  • 避免编写需要AuthN/AuthZ基础结构的测试代码(这不是一个合理的选项)

  • 我不知道哪条路更好。任何一个都可以解决这个问题。我的问题归结为哪一个是最好的策略


    注意:是的,我知道我不需要测试我没有编写的代码。MVC5提供的UserManager基础设施就是这样一个基础设施,但是如果我想编写测试来验证我对ApplicationUser的修改,或者编写代码来验证依赖于用户角色的行为,那么我必须使用UserManager进行测试。

    我在回答我自己的问题,这样如果你认为这是正确的,我就可以从社区中获得感觉一个好答案

    步骤1:修改生成的AccountController以使用支持字段为AuthenticationManager提供属性设置器

    // Add this private variable
    private IAuthenticationManager _authnManager;
    
    // Modified this from private to public and add the setter
    public IAuthenticationManager AuthenticationManager
    {
        get
        {
            if (_authnManager == null)
                _authnManager = HttpContext.GetOwinContext().Authentication;
            return _authnManager;
        }
        set { _authnManager = value; }
    }
    
    第二步: 修改单元测试以添加Microsoft.OWin.iaAuthenticationManager界面的模拟

    [TestMethod]
    public void Register()
    {
        // Arrange
        var userManager = new UserManager<ApplicationUser>(new TestUserStore<ApplicationUser>());
        var controller = new AccountController(userManager);
        controller.SetFakeControllerContext();
    
        // Modify the test to setup a mock IAuthenticationManager
        var mockAuthenticationManager = new Mock<IAuthenticationManager>();
        mockAuthenticationManager.Setup(am => am.SignOut());
        mockAuthenticationManager.Setup(am => am.SignIn());
    
        // Add it to the controller - this is why you have to make a public setter
        controller.AuthenticationManager = mockAuthenticationManager.Object;
    
        // Act
        var result =
            controller.Register(new RegisterViewModel
            {
                BirthDate = TestBirthDate,
                UserName = TestUser,
                Password = TestUserPassword,
                ConfirmPassword = TestUserPassword
            }).Result;
    
        // Assert
        Assert.IsNotNull(result);
    
        var addedUser = userManager.FindByName(TestUser);
        Assert.IsNotNull(addedUser);
        Assert.AreEqual(TestBirthDate, addedUser.BirthDate);
    }
    
    [TestMethod]
    公开作废登记册()
    {
    //安排
    var userManager=newusermanager(newtestuserstore());
    var控制器=新的AccountController(userManager);
    controller.SetFakeControllerContext();
    //修改测试以设置模拟IAAuthenticationManager
    var mockAuthenticationManager=new Mock();
    mockAuthenticationManager.Setup(am=>am.SignOut());
    mockAuthenticationManager.Setup(am=>am.SignIn());
    //将其添加到控制器-这就是为什么您必须创建公共setter
    controller.AuthenticationManager=mockAuthenticationManager.Object;
    //表演
    var结果=
    控制器.寄存器(新的RegisterViewModel
    {
    生日=测试生日,
    UserName=TestUser,
    Password=TestUserPassword,
    ConfirmPassword=TestUserPassword
    }).结果;
    //断言
    Assert.IsNotNull(结果);
    var addedUser=userManager.FindByName(TestUser);
    Assert.IsNotNull(addedUser);
    AreEqual(TestBirthDate,addedUser.BirthDate);
    }
    
    现在测试通过了


    好主意?坏主意?

    我使用了一个与您类似的解决方案—模拟IAAuthenticationManager—但我的登录代码位于LoginManager类中,该类通过构造函数注入获取IAAuthenticationManager

        public LoginHandler(HttpContextBase httpContext, IAuthenticationManager authManager)
        {
            _httpContext = httpContext;
            _authManager = authManager;
        }
    
    我正在使用注册我的依赖项:

        public static void RegisterTypes(IUnityContainer container)
        {
            container.RegisterType<HttpContextBase>(
                new InjectionFactory(_ => new HttpContextWrapper(HttpContext.Current)));
            container.RegisterType<IOwinContext>(new InjectionFactory(c => c.Resolve<HttpContextBase>().GetOwinContext()));
            container.RegisterType<IAuthenticationManager>(
                new InjectionFactory(c => c.Resolve<IOwinContext>().Authentication));
            container.RegisterType<ILoginHandler, LoginHandler>();
            // Further registrations here...
        }
    
    公共静态无效注册表类型(IUnityContainer容器)
    {
    container.RegisterType(
    新的InjectionFactory(=>newhttpcontextwrapper(HttpContext.Current));
    RegisterType(新的InjectionFactory(c=>c.Resolve().GetOwinContext());
    container.RegisterType(
    新的InjectionFactory(c=>c.Resolve().Authentication));
    container.RegisterType();
    //在这里进一步注册。。。
    }
    
    但是,我想测试我的Unity注册,如果不伪造(a)HttpContext.Current(足够硬)和(b)GetOwinContext(),这是很棘手的,正如您所发现的,这是不可能直接完成的

    我以Phil Haack的形式找到了一个解决方案,并对HttpContext进行了一些操作,以创建一个基本的。到目前为止,我发现设置一个伪Owin变量就足以使GetOwinContext()正常工作,但是YMMV

    public static class HttpSimulatorExtensions
    {
        public static void SimulateRequestAndOwinContext(this HttpSimulator simulator)
        {
            simulator.SimulateRequest();
            Dictionary<string, object> owinEnvironment = new Dictionary<string, object>()
                {
                    {"owin.RequestBody", null}
                };
            HttpContext.Current.Items.Add("owin.Environment", owinEnvironment);
        }        
    }
    
    [TestClass]
    public class UnityConfigTests
    {
        [TestMethod]
        public void RegisterTypes_RegistersAllDependenciesOfHomeController()
        {
            IUnityContainer container = UnityConfig.GetConfiguredContainer();
            HomeController controller;
    
            using (HttpSimulator simulator = new HttpSimulator())
            {
                simulator.SimulateRequestAndOwinContext();
                controller = container.Resolve<HomeController>();
            }
    
            Assert.IsNotNull(controller);
        }
    }
    
    公共静态类HttpSimulatorExtensions
    {
    公共静态void模拟器RequestStandowInContext(此HttpSimulator模拟器)
    {
    simulator.SimulateRequest();
    字典owinenEnvironment=新字典()
    {
    {“owin.RequestBody”,null}
    };
    HttpContext.Current.Items.Add(“owin.Environment”,owinevironment);
    }        
    }
    [测试类]
    公共类UnityConfigTests
    {
    [测试方法]
    public void RegisterTypes\u registerAllDependenciesOfHomeController()
    {
    IUnityContainer容器=UnityConfig.GetConfiguredContainer();
    家庭控制器;
    使用(HttpSimulator=new HttpSimulator())
    {
    simulator.SimulateRequestAndOwinContext();
    controller=container.Resolve();
    }
    Assert.IsNotNull(控制器);
    }
    }
    

    如果你的SetFakeControllerContext()方法能完成这项工作,那么HttpSimulator可能会有些过分,但它看起来是集成测试的有用工具。

    我的需求与此类似,但我意识到我不想要一个纯粹的un
        public LoginHandler(HttpContextBase httpContext, IAuthenticationManager authManager)
        {
            _httpContext = httpContext;
            _authManager = authManager;
        }
    
        public static void RegisterTypes(IUnityContainer container)
        {
            container.RegisterType<HttpContextBase>(
                new InjectionFactory(_ => new HttpContextWrapper(HttpContext.Current)));
            container.RegisterType<IOwinContext>(new InjectionFactory(c => c.Resolve<HttpContextBase>().GetOwinContext()));
            container.RegisterType<IAuthenticationManager>(
                new InjectionFactory(c => c.Resolve<IOwinContext>().Authentication));
            container.RegisterType<ILoginHandler, LoginHandler>();
            // Further registrations here...
        }
    
    public static class HttpSimulatorExtensions
    {
        public static void SimulateRequestAndOwinContext(this HttpSimulator simulator)
        {
            simulator.SimulateRequest();
            Dictionary<string, object> owinEnvironment = new Dictionary<string, object>()
                {
                    {"owin.RequestBody", null}
                };
            HttpContext.Current.Items.Add("owin.Environment", owinEnvironment);
        }        
    }
    
    [TestClass]
    public class UnityConfigTests
    {
        [TestMethod]
        public void RegisterTypes_RegistersAllDependenciesOfHomeController()
        {
            IUnityContainer container = UnityConfig.GetConfiguredContainer();
            HomeController controller;
    
            using (HttpSimulator simulator = new HttpSimulator())
            {
                simulator.SimulateRequestAndOwinContext();
                controller = container.Resolve<HomeController>();
            }
    
            Assert.IsNotNull(controller);
        }
    }
    
    /// <summary> Set up an account controller with just enough context to work through the tests. </summary>
    /// <param name="userManager"> The user manager to be used </param>
    /// <returns>A new account controller</returns>
    private static AccountController SetupAccountController(ApplicationUserManager userManager)
    {
        AccountController controller = new AccountController(userManager);
        Uri url = new Uri("https://localhost/Account/ForgotPassword"); // the real string appears to be irrelevant
        RouteData routeData = new RouteData();
    
        HttpRequest httpRequest = new HttpRequest("", url.AbsoluteUri, "");
        HttpResponse httpResponse = new HttpResponse(null);
        HttpContext httpContext = new HttpContext(httpRequest, httpResponse);
        Dictionary<string, object> owinEnvironment = new Dictionary<string, object>()
        {
            {"owin.RequestBody", null}
        };
        httpContext.Items.Add("owin.Environment", owinEnvironment);
        HttpContextWrapper contextWrapper = new HttpContextWrapper(httpContext);
    
        ControllerContext controllerContext = new ControllerContext(contextWrapper, routeData, controller);
        controller.ControllerContext = controllerContext;
        controller.Url = new UrlHelper(new RequestContext(contextWrapper, routeData));
        // We have not found out how to set up this UrlHelper so that we get a real callbackUrl in AccountController.ForgotPassword.
    
        return controller;
    }