Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-mvc/17.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
Asp.net mvc 如何使用Rhino.Mocks来模拟ControllerContext_Asp.net Mvc_Unit Testing_Tdd_Controller_Rhino Mocks - Fatal编程技术网

Asp.net mvc 如何使用Rhino.Mocks来模拟ControllerContext

Asp.net mvc 如何使用Rhino.Mocks来模拟ControllerContext,asp.net-mvc,unit-testing,tdd,controller,rhino-mocks,Asp.net Mvc,Unit Testing,Tdd,Controller,Rhino Mocks,我正在尝试使用Rhino.Mocks来模拟ControllerContext对象,以便在控制器单元测试中访问运行时对象,如用户、请求、响应和会话。我编写下面的方法是为了模拟一个控制器 private TestController CreateTestControllerAs(string userName) { var mock = MockRepository.GenerateStub<ControllerContext>(); mock.Stub(con =>

我正在尝试使用
Rhino.Mocks
来模拟
ControllerContext
对象,以便在控制器单元测试中访问运行时对象,如用户、请求、响应和会话。我编写下面的方法是为了模拟一个控制器

private TestController CreateTestControllerAs(string userName)
{
    var mock = MockRepository.GenerateStub<ControllerContext>();
    mock.Stub(con =>
        con.HttpContext.User.Identity.Name).Return(userName);
    mock.Stub(con =>
        con.HttpContext.Request.IsAuthenticated).Return(true);

    var controller = CreateTestController(); // left out of example for brevity
    controller.ControllerContext = mock;

    return controller;
 }
private TestController CreateTestControllerAs(字符串用户名)
{
var mock=MockRepository.GenerateStub();
mock.Stub(con=>
con.HttpContext.User.Identity.Name).Return(用户名);
mock.Stub(con=>
con.HttpContext.Request.IsAuthenticated)返回(true);
var controller=CreateTestController();//为了简洁起见,省略了示例
controller.ControllerContext=mock;
返回控制器;
}
但是,我的模拟ControllerContext的
HttpContext
为空,并且我尝试访问
HttpContext.User
等会导致
System.NullReferenceException


我的模拟有什么不对吗?

我强烈建议您看看哪个使用了
Rhino.mock
,并提供了一种优雅的方法来测试您的控制器。以下是您的测试可能的样子:

[TestClass]
public class UsersControllerTests : TestControllerBuilder
{
    [TestMethod]
    public void UsersController_Index()
    {
        // arrange
        // TODO : this initialization part should be externalized
        // so that it can be reused by other tests
        var sut = new HomeController();
        this.InitializeController(sut);
        // At this point sut.Request, sut.Response, sut.Session, ... are
        // stubed objects on which you could define expectations.

        // act
        var actual = sut.Index();

        // assert
        actual.AssertViewRendered();
    }
}

这里有一个for a,它是我写的a的一部分。

我认为问题在于,您需要存根整个属性链,或者至少向您的ControllerContext传递一个HttpContext模拟,即沿着以下路线的内容:

private TestController CreateTestControllerAs(string userName)
{
    var mock = MockRepository.GenerateStub<ControllerContext>();
    var context = MockRepository.GenerateStub<IHttpContext>();    
    mock.Stub(con =>
        con.HttpContext).Return(context );
    // etc... with User, Identity ...

    return controller;
 }
private TestController CreateTestControllerAs(字符串用户名)
{
var mock=MockRepository.GenerateStub();
var context=MockRepository.GenerateStub();
mock.Stub(con=>
con.HttpContext)返回(context);
//等…与用户,身份。。。
返回控制器;
}
在您的代码中,假设您从未将HttpContext设置为任何特定的值,默认情况下,存根假定它为null


我没有使用Darin描述的解决方案,但它看起来会让你的生活更轻松

其他答案已经展示了如何模拟属性链来解决问题

但这里真正的问题是,如果您违反了标准,单元测试和模拟就不会真正起作用。如果您希望您的代码是可测试的,并且最大限度地可重用,那么您需要直接注入代码的真正依赖项,并将这些依赖项隐藏在抽象后面

例如,不要这样做:

public class MyClass
{
   public ControllerContext Context { get; set; }

   public void DoSomething()
   {
       // BAD: we're only interested in the name, but instead we've injected 
       // a ControllerContext that can give us a HttpContext that can give us
       // a User that can give us an Identity that can give us the Name.
       string name = Context.HttpContext.User.Identity.Name;
       // etcetera
   }
}
这样做:

public class MyClass
{
    public INameProvider NameProvider { get; set; }

    public void DoSomething()
    {
        // GOOD: we've injected a name provider
        string name = NameProvider.Name;
        // etcetera
    }
}
通过引入
INameProvider
概念,您的组件代码、测试和模拟变得更加简单。您的代码也变得更加可重用:它只依赖于“名称提供程序”的抽象概念,而不是一堆ASP.NET类。只要能够实现
INameProvider
适配器,就可以在任何环境中重用组件

取舍是您需要声明
INameProvider
接口并编写一个实现它的包装类。当您始终遵循这种方法时,您将得到许多小型接口和适配器类。这就是测试驱动开发的方式


(如果您想知道为什么我要引入
INameProvider
而不是直接设置名称-这样IoC容器就可以使用接口将依赖项与实现匹配起来。)

这很有效。仍然必须将对象放入
HttpContext.User
属性中,但这很容易通过这样做来实现:
HttpContext.User=new-GenericPrincipal(new-generientity(loginName),null)这是一种常见的方法,但会导致mvc层与自定义代码的重复。i、 e.用户的INameProvider、控制器的服务类、会话。。。Cookie、请求、响应和对抽象的更友好的态度是行动的结果。