如何在ASP.Net Web表单中模拟/伪造会话对象?

时间:2021-04-28 15:15:35

Is there a way to mock/fake the session object in ASP.Net Web forms when creating unit tests?

有没有办法在创建单元测试时在ASP.Net Web表单中模拟/伪造会话对象?

I am currently storing user details in a session variable which is accessed by my business logic.

我目前正在将用户详细信息存储在会话变量中,该变量由我的业务逻辑访问。

When testing my business logic in isolation, the session is not available. This seems to indicate a bad design (though I'm not sure). Should the business logic layer be accessing session variables in the first place?

在单独测试我的业务逻辑时,会话不可用。这似乎表明一个糟糕的设计(虽然我不确定)。业务逻辑层是否应该首先访问会话变量?

If so, then how would I go about swapping the user details with a fake object for testing?

如果是这样,那么我将如何用假对象交换用户详细信息进行测试?

5 个解决方案

#1


9  

In ASP.NET, you can't create a Test Double of HttpSessionState because it is sealed. Yes, this is bad design on the part of the original designers of ASP.NET, but there's not a lot to do about it.

在ASP.NET中,您无法创建HttpSessionState的Test Double,因为它是密封的。是的,这是ASP.NET原始设计者的糟糕设计,但是没有太多可做的事情。

This is one of many reasons why TDD'ers and other SOLID practictioners have largely abandonded ASP.NET in favor of ASP.NET MVC and other, more testable frameworks. In ASP.NET MVC, the HTTP session is modelled by the abstract HttpSessionStateBase class.

这是为什么TDD'ers和其他SOLID实践者已经基本上放弃了ASP.NET而支持ASP.NET MVC和其他更可测试的框架的众多原因之一。在ASP.NET MVC中,HTTP会话由抽象的HttpSessionStateBase类建模。

You could take a similar approach and let your objects work on an abstract session, and then wrap the real HttpSessionState class when you are running in the ASP.NET environment. Depending on circumstances, you may even be able to reuse the types from System.Web.Abstractions, but if not, you can define your own.

您可以采用类似的方法,让您的对象在抽象会话上工作,然后在ASP.NET环境中运行时包装真正的HttpSessionState类。根据具体情况,您甚至可以重用System.Web.Abstractions中的类型,但如果没有,您可以定义自己的类型。

In any case, your business logic is your Domain Model and it should be modeled independently of any particular run-time technology, so I would say that it shouldn't be accessing the session object in the first place.

在任何情况下,您的业务逻辑都是您的域模型,它应该独立于任何特定的运行时技术建模,所以我想说它不应该首先访问会话对象。

If you absolutely need to use Test Doubles for unit tets involving HttpSessionState, this is still possible with certain invasive dynamic mocks, such as TypeMock or Moles, althought these carry plenty of disadvantages as well (see this comparison of dynamic mocks).

如果你绝对需要将测试双打用于涉及HttpSessionState的单位Tets,那么对于某些入侵动态模拟,例如TypeMock或Moles,这仍然是可能的,尽管这些模型也有很多缺点(参见动态模拟的这种比较)。

#2


26  

You can do it with essentially 4 lines of code. Although this doesn't speak to the previous comment of moving session out of your business logic layer, sometimes you might need to do this anyway if you're working with legacy code that is heavily coupled to the session (my scenario).

你可以用4行代码完成它。虽然这与先前关于将会话移出业务逻辑层的注释没有说明,但如果您正在处理与会话紧密耦合的遗留代码(我的方案),有时您可能需要这样做。

The namespaces:

名称空间:

using System.Web;
using System.IO;
using System.Web.Hosting;
using System.Web.SessionState;

The code:

代码:

HttpWorkerRequest _wr = new SimpleWorkerRequest(
    "/dummyWorkerRequest", @"c:\inetpub\wwwroot\dummy",
    "default.aspx", null, new StringWriter());

HttpContext.Current = new HttpContext(_wr);

var sessionContainer = new HttpSessionStateContainer(
    "id", new SessionStateItemCollection(),
    new HttpStaticObjectsCollection(), 10, true,
    HttpCookieMode.AutoDetect, SessionStateMode.InProc, false);

SessionStateUtility.AddHttpSessionStateToContext(
    HttpContext.Current, sessionContainer);

You can then refer to the session without getting a NullReferenceException error:

然后,您可以在不收到NullReferenceException错误的情况下引用会话:

HttpContext.Current.Session.Add("mySessionKey", 1);

This is a combination of code I compiled from the articles below:

这是我从以下文章编译的代码的组合:

#3


1  

Your instincts are correct---you shouldn't be accessing pieces of the ASP.NET framework from your business logic. This would include Session.

你的直觉是正确的---你不应该从业务逻辑中访问ASP.NET框架的各个部分。这包括会话。

To answer your first question, you can mock static classes using a product like Typemock Isolator, but you'll be better off if you refactor your code to wrap access to Session in an interface (i.e., IHttpSession.) You can then mock IHttpSession.

要回答你的第一个问题,你可以使用像Typemock Isolator这样的产品来模拟静态类,但是如果重构代码以在界面中包装对Session的访问(例如,IHttpSession),你会更好。然后你可以模拟IHttpSession。

#4


1  

In Asp.Net webforms, you cannot escape the fact that framework's entry into your code comes from aspx pages. I agree that your business layer should not touch the asp.net specific components directly but you have to have a model's storage container and session in asp.net is a good area. Thus, one possible approach is to create ISessionManager for purpose of interacting inside your business layer. Then, implement the concrete type by using HttpSessionState ... btw, a good trick is to use HttpContext.Current.Session to implement accessors/getters out of the HttpSessionState. Your next challenge would be how to wire it all together.

在Asp.Net webforms中,您无法摆脱框架进入代码的过程来自aspx页面的事实。我同意你的业务层不应直接触及asp.net特定组件,但你必须有一个模型的存储容器,而asp.net中的会话是一个很好的领域。因此,一种可能的方法是创建ISessionManager,以便在业务层内进行交互。然后,通过使用HttpSessionState ... btw实现具体类型,一个好的技巧是使用HttpContext.Current.Session来实现HttpSessionState中的访问器/ getter。您的下一个挑战将是如何将它们连接在一起。

#5


0  

One approach is to pass a lambda expression to your code that takes a string (or some other object) as input, and uses it to set either the Session object or a test container.

一种方法是将lambda表达式传递给代码,该代码将字符串(或其他对象)作为输入,并使用它来设置Session对象或测试容器。

However, as others have said, it's a good idea to move access to the Session object out of your BLL.

但是,正如其他人所说的那样,从BLL中移出对Session对象的访问是个好主意。

#1


9  

In ASP.NET, you can't create a Test Double of HttpSessionState because it is sealed. Yes, this is bad design on the part of the original designers of ASP.NET, but there's not a lot to do about it.

在ASP.NET中,您无法创建HttpSessionState的Test Double,因为它是密封的。是的,这是ASP.NET原始设计者的糟糕设计,但是没有太多可做的事情。

This is one of many reasons why TDD'ers and other SOLID practictioners have largely abandonded ASP.NET in favor of ASP.NET MVC and other, more testable frameworks. In ASP.NET MVC, the HTTP session is modelled by the abstract HttpSessionStateBase class.

这是为什么TDD'ers和其他SOLID实践者已经基本上放弃了ASP.NET而支持ASP.NET MVC和其他更可测试的框架的众多原因之一。在ASP.NET MVC中,HTTP会话由抽象的HttpSessionStateBase类建模。

You could take a similar approach and let your objects work on an abstract session, and then wrap the real HttpSessionState class when you are running in the ASP.NET environment. Depending on circumstances, you may even be able to reuse the types from System.Web.Abstractions, but if not, you can define your own.

您可以采用类似的方法,让您的对象在抽象会话上工作,然后在ASP.NET环境中运行时包装真正的HttpSessionState类。根据具体情况,您甚至可以重用System.Web.Abstractions中的类型,但如果没有,您可以定义自己的类型。

In any case, your business logic is your Domain Model and it should be modeled independently of any particular run-time technology, so I would say that it shouldn't be accessing the session object in the first place.

在任何情况下,您的业务逻辑都是您的域模型,它应该独立于任何特定的运行时技术建模,所以我想说它不应该首先访问会话对象。

If you absolutely need to use Test Doubles for unit tets involving HttpSessionState, this is still possible with certain invasive dynamic mocks, such as TypeMock or Moles, althought these carry plenty of disadvantages as well (see this comparison of dynamic mocks).

如果你绝对需要将测试双打用于涉及HttpSessionState的单位Tets,那么对于某些入侵动态模拟,例如TypeMock或Moles,这仍然是可能的,尽管这些模型也有很多缺点(参见动态模拟的这种比较)。

#2


26  

You can do it with essentially 4 lines of code. Although this doesn't speak to the previous comment of moving session out of your business logic layer, sometimes you might need to do this anyway if you're working with legacy code that is heavily coupled to the session (my scenario).

你可以用4行代码完成它。虽然这与先前关于将会话移出业务逻辑层的注释没有说明,但如果您正在处理与会话紧密耦合的遗留代码(我的方案),有时您可能需要这样做。

The namespaces:

名称空间:

using System.Web;
using System.IO;
using System.Web.Hosting;
using System.Web.SessionState;

The code:

代码:

HttpWorkerRequest _wr = new SimpleWorkerRequest(
    "/dummyWorkerRequest", @"c:\inetpub\wwwroot\dummy",
    "default.aspx", null, new StringWriter());

HttpContext.Current = new HttpContext(_wr);

var sessionContainer = new HttpSessionStateContainer(
    "id", new SessionStateItemCollection(),
    new HttpStaticObjectsCollection(), 10, true,
    HttpCookieMode.AutoDetect, SessionStateMode.InProc, false);

SessionStateUtility.AddHttpSessionStateToContext(
    HttpContext.Current, sessionContainer);

You can then refer to the session without getting a NullReferenceException error:

然后,您可以在不收到NullReferenceException错误的情况下引用会话:

HttpContext.Current.Session.Add("mySessionKey", 1);

This is a combination of code I compiled from the articles below:

这是我从以下文章编译的代码的组合:

#3


1  

Your instincts are correct---you shouldn't be accessing pieces of the ASP.NET framework from your business logic. This would include Session.

你的直觉是正确的---你不应该从业务逻辑中访问ASP.NET框架的各个部分。这包括会话。

To answer your first question, you can mock static classes using a product like Typemock Isolator, but you'll be better off if you refactor your code to wrap access to Session in an interface (i.e., IHttpSession.) You can then mock IHttpSession.

要回答你的第一个问题,你可以使用像Typemock Isolator这样的产品来模拟静态类,但是如果重构代码以在界面中包装对Session的访问(例如,IHttpSession),你会更好。然后你可以模拟IHttpSession。

#4


1  

In Asp.Net webforms, you cannot escape the fact that framework's entry into your code comes from aspx pages. I agree that your business layer should not touch the asp.net specific components directly but you have to have a model's storage container and session in asp.net is a good area. Thus, one possible approach is to create ISessionManager for purpose of interacting inside your business layer. Then, implement the concrete type by using HttpSessionState ... btw, a good trick is to use HttpContext.Current.Session to implement accessors/getters out of the HttpSessionState. Your next challenge would be how to wire it all together.

在Asp.Net webforms中,您无法摆脱框架进入代码的过程来自aspx页面的事实。我同意你的业务层不应直接触及asp.net特定组件,但你必须有一个模型的存储容器,而asp.net中的会话是一个很好的领域。因此,一种可能的方法是创建ISessionManager,以便在业务层内进行交互。然后,通过使用HttpSessionState ... btw实现具体类型,一个好的技巧是使用HttpContext.Current.Session来实现HttpSessionState中的访问器/ getter。您的下一个挑战将是如何将它们连接在一起。

#5


0  

One approach is to pass a lambda expression to your code that takes a string (or some other object) as input, and uses it to set either the Session object or a test container.

一种方法是将lambda表达式传递给代码,该代码将字符串(或其他对象)作为输入,并使用它来设置Session对象或测试容器。

However, as others have said, it's a good idea to move access to the Session object out of your BLL.

但是,正如其他人所说的那样,从BLL中移出对Session对象的访问是个好主意。