【问题标题】:How to Mock Session variables in ASP.net core unit testing project?如何在 ASP.net 核心单元测试项目中模拟会话变量?
【发布时间】:2017-02-16 09:26:55
【问题描述】:

如何在 ASP.net 核心单元测试项目中模拟 Session 变量?

1) 我创建了一个会话的模拟对象。

Mock<HttpContext> mockHttpContext = new Mock<HttpContext>();
Mock<ITestSession> mockSession = new Mock<ISession>().As<ITestSession>();

2) 设置GetString()方法

mockSession.Setup(s => s.GetString("ModuleId")).Returns("1");

3) 创建controllerContext 并分配mockhttpContext 对象

controller.ControllerContext.HttpContext = mockHttpContext.Object; 

4) 尝试从控制器读取数据。

HttpContext.Session.GetString("ModuleId")

而我得到一个空值“ModuleId”。请帮我模拟会话 GetString() 方法

示例:

        //Arrange
        //Note: Mock session 
        Mock<HttpContext> mockHttpContext = new Mock<HttpContext>();
        Mock<ITestSession> mockSession = new Mock<ISession>().As<ITestSession>();
        //Cast list to IEnumerable
        IEnumerable<string> sessionKeys = new string[] { };
        //Convert to list.
        List<string> listSessionKeys = sessionKeys.ToList();
        listSessionKeys.Add("ModuleId");
        sessionKeys = listSessionKeys;
        mockSession.Setup(s => s.Keys).Returns(sessionKeys);
        mockSession.Setup(s => s.Id).Returns("89eca97a-872a-4ba2-06fe-ba715c3f32be");
        mockSession.Setup(s => s.IsAvailable).Returns(true);
        mockHttpContext.Setup(s => s.Session).Returns(mockSession.Object);
     mockSession.Setup(s => s.GetString("ModuleId")).Returns("1");         

        //Mock TempData
        var tempDataMock = new Mock<ITempDataDictionary>();
        //tempDataMock.Setup(s => s.Peek("ModuleId")).Returns("1");

        //Mock service
        Mock<ITempServices> mockITempServices= new Mock<ITempServices>();
        mockITempServices.Setup(m => m.PostWebApiData(url)).Returns(Task.FromResult(response));

        //Mock Management class method
        Mock<ITestManagement> mockITestManagement = new Mock<ITestManagement>();
        mockITestManagement .Setup(s => s.SetFollowUnfollow(url)).Returns(Task.FromResult(response));

        //Call Controller method
        TestController controller = new TestController (mockITestManagement .Object, appSettings);
        controller.ControllerContext.HttpContext = mockHttpContext.Object;            
        controller.TempData = tempDataMock.Object;

        //Act
        string response = await controller.Follow("true");

        // Assert
        Assert.NotNull(response);
        Assert.IsType<string>(response);
 

【问题讨论】:

  • 显示被测方法。
  • 感谢 NKosi 的回复。
  • 我得到了这个问题的解决方案。我已经为会话创建了模拟类并从 ISession 继承。在这个mock类中实现了ISession的所有方法,并使用这个类来存储会话变量。

标签: asp.net unit-testing


【解决方案1】:

首先创建名为mockHttpSession的类并继承自ISession。

public class MockHttpSession : ISession
{
    Dictionary<string, object> sessionStorage = new Dictionary<string, object>();

    public object this[string name]
    {
        get { return sessionStorage[name]; }
        set { sessionStorage[name] = value; }
    }

    string ISession.Id
    {
        get
        {
            throw new NotImplementedException();
        }
    }

    bool ISession.IsAvailable
    {
        get
        {
            throw new NotImplementedException();
        }
    }

    IEnumerable<string> ISession.Keys
    {
        get { return sessionStorage.Keys; }
    }

    void ISession.Clear()
    {
        sessionStorage.Clear();
    }

    Task ISession.CommitAsync()
    {
        throw new NotImplementedException();
    }

    Task ISession.LoadAsync()
    {
        throw new NotImplementedException();
    }

    void ISession.Remove(string key)
    {
        sessionStorage.Remove(key);
    }

    void ISession.Set(string key, byte[] value)
    {
        sessionStorage[key] = value;
    }

    bool ISession.TryGetValue(string key, out byte[] value)
    {
        if (sessionStorage[key] != null)
        {
            value = Encoding.ASCII.GetBytes(sessionStorage[key].ToString());
            return true;
        }
        else
        {
            value = null;
            return false;
        }
    }        
}

然后在实际控制器中使用这个会话:

     Mock<HttpContext> mockHttpContext = new Mock<HttpContext>();
        MockHttpSession mockSession = new MockHttpSession();           
        mockSession["Key"] = Value;
        mockHttpContext.Setup(s => s.Session).Returns(mockSession);
        Controller controller=new Controller();
        controller.ControllerContext.HttpContext = mockHttpContext.Object;

【讨论】:

  • 我们可以使用这个会话类来存储会话变量。例如:首先创建mockHttpSession对象,然后存储变量值。
  • 请不要使用此代码。 ASCII.GetBytes(sessionStorage[key].ToString()); - 它用不同的编码重新编码字节数组!!我刚刚花了半天时间调试某人通过复制粘贴这段代码编写的测试......我想知道“为什么我的字节数组会在会话中发生变化?”
【解决方案2】:

我最近刚遇到这个问题,唯一的解决方法是模拟 GetString 方法包装的函数,即 TryGetValue。

byte[] dummy = System.Text.Encoding.UTF8.GetBytes(Guid.NewGuid().ToString());
_mockSession.Setup(x => x.TryGetValue(It.IsAny<string>(),out dummy)).Returns(true).Verifiable();

所以你不需要模拟对 GetString 方法的调用,你只需模拟在幕后调用的方法。

【讨论】:

  • 我对此表示赞同,因为它确实为我指明了正确的方向,但缺少实际使用它所需的一些细节。
【解决方案3】:

Pankaj Dhote 提供的解决方案正在发挥作用。这是 ASP.NET CORE 2 MVC 的完整无错误代码:

public class MockHttpSession : ISession
{
    Dictionary<string, object> sessionStorage = new Dictionary<string, object>();
    public object this[string name]
    {
        get { return sessionStorage[name]; }
        set { sessionStorage[name] = value; }
    }

    string ISession.Id
    {
        get
        {
            throw new NotImplementedException();
        }
    }
    bool ISession.IsAvailable
    {
        get
        {
            throw new NotImplementedException();
        }
    }
    IEnumerable<string> ISession.Keys
    {
        get { return sessionStorage.Keys; }
    }
    void ISession.Clear()
    {
        sessionStorage.Clear();
    }
    Task ISession.CommitAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        throw new NotImplementedException();
    }

    Task ISession.LoadAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        throw new NotImplementedException();
    }

    void ISession.Remove(string key)
    {
        sessionStorage.Remove(key);
    }

    void ISession.Set(string key, byte[] value)
    {
        sessionStorage[key] = value;
    }

    bool ISession.TryGetValue(string key, out byte[] value)
    {
        if (sessionStorage[key] != null)
        {
            value = Encoding.ASCII.GetBytes(sessionStorage[key].ToString());
            return true;
        }
        else
        {
            value = null;
            return false;
        }
    }
}

然后在实际控制器中使用这个会话:

    Mock<HttpContext> mockHttpContext = new Mock<HttpContext>();
    MockHttpSession mockSession = new MockHttpSession();           
    mockSession["Key"] = Value;
    mockHttpContext.Setup(s => s.Session).Returns(mockSession);
    Controller controller=new Controller();
    controller.ControllerContext.HttpContext = mockHttpContext.Object;

【讨论】:

    【解决方案4】:

    我使用 Pankaj Dhote 的类来模拟 ISession。我必须改变一种方法:

    bool ISession.TryGetValue(string key, out byte[] value)
    {
        if (sessionStorage[key] != null)
        {
            value = Encoding.ASCII.GetBytes(sessionStorage[key].ToString());
            return true;
        }
        else
        {
            value = null;
            return false;
        }
    }  
    

    到下面的代码。否则,对 sessionStorage[key].ToString() 的引用返回类型的名称而不是字典中的值。

        bool ISession.TryGetValue(string key, out byte[] value)
        {
            if (sessionStorage[key] != null)
            {
                value = (byte[])sessionStorage[key]; //Encoding.UTF8.GetBytes(sessionStorage[key].ToString())
                return true;
            }
            else
            {
                value = null;
                return false;
            }
        }
    

    【讨论】:

      【解决方案5】:

      起初我创建了 ISession 的实现:

      public class MockHttpSession : ISession
      {
          readonly Dictionary<string, object> _sessionStorage = new Dictionary<string, object>();
          string ISession.Id => throw new NotImplementedException();
          bool ISession.IsAvailable => throw new NotImplementedException();
          IEnumerable<string> ISession.Keys => _sessionStorage.Keys;
          void ISession.Clear()
          {
              _sessionStorage.Clear();
          }
          Task ISession.CommitAsync(CancellationToken cancellationToken)
          {
              throw new NotImplementedException();
          }
          Task ISession.LoadAsync(CancellationToken cancellationToken)
          {
              throw new NotImplementedException();
          }
          void ISession.Remove(string key)
          {
              _sessionStorage.Remove(key);
          }
          void ISession.Set(string key, byte[] value)
          {
              _sessionStorage[key] = Encoding.UTF8.GetString(value);
          }
          bool ISession.TryGetValue(string key, out byte[] value)
          {
              if (_sessionStorage[key] != null)
              {
                  value = Encoding.ASCII.GetBytes(_sessionStorage[key].ToString());
                  return true;
              }
              value = null;
              return false;
          }
      }
      

      其次在控制器定义期间实现:

      private HomeController CreateHomeController()
              {
                  var controller = new HomeController(
                      mockLogger.Object,
                      mockPollRepository.Object,
                      mockUserRepository.Object)
                  {
                      ControllerContext = new ControllerContext
                      {
                          HttpContext = new DefaultHttpContext() {Session = new MockHttpSession()}
                      }
                  };
                  return controller;
              } 
      

      【讨论】:

        【解决方案6】:

        当我在使用 .net 5 的 Blazor 服务器上工作并想要测试使用 ProtectedSessionStorage 的组件时,以下步骤对我有用。

        //add ProtectedSessionStorage
        
                    testContext.Services.AddSingleton<Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage.ProtectedSessionStorage>();
        
        
        
                    //mock IJSRuntime
        
                    var js = new Mock<IJSRuntime>();
        
                    testContext.Services.AddSingleton(js.Object);
        
        
        
                    //mock IDataProtector
        
                    var mockDataProtector = new Mock<Microsoft.AspNetCore.DataProtection.IDataProtector>();
        
                    mockDataProtector.Setup(sut => sut.Protect(It.IsAny<byte[]>())).Returns(Encoding.UTF8.GetBytes("protectedText"));
        
                    mockDataProtector.Setup(sut => sut.Unprotect(It.IsAny<byte[]>())).Returns(Encoding.UTF8.GetBytes("originalText"));
        
                    testContext.Services.AddSingleton(mockDataProtector.Object);
        
        
        
                    //mock IDataProtectionProvider
        
                    var mockDataProtectionProvider = new Mock<Microsoft.AspNetCore.DataProtection.IDataProtectionProvider>();
        
                    mockDataProtectionProvider.Setup(s => s.CreateProtector(It.IsAny<string>())).Returns(mockDataProtector.Object);
        
                    testContext.Services.AddSingleton(mockDataProtectionProvider.Object);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-01-19
          • 1970-01-01
          • 1970-01-01
          • 2016-06-11
          • 2019-12-13
          • 1970-01-01
          • 1970-01-01
          • 2012-12-28
          相关资源
          最近更新 更多