简体   繁体   English

在 ASP.NET WebApi 2 中测试 OWIN 中间件:如何设置 cookie?

[英]Testing OWIN Middleware in ASP.NET WebApi 2: How to set cookies?

I'm trying to test an OWIN Middleware component in an ASP.NET WebApi 2 application.我正在尝试在 ASP.NET WebApi 2 应用程序中测试 OWIN 中间件组件。 The middleware is supposed to look at the cookies of the incoming request, modify some stuff on the request before it's handed to the next component, and potentially set a cookie on the way out as well.中间件应该查看传入请求的 cookie,在将请求传递给下一个组件之前修改请求中的一些内容,并可能在出路时设置一个 cookie。

What's tripping me up is that the OwinRequest.Cookies property is of type RequestCookieCollection , which doesn't seem to allow modifying, and the property itself is read-only which means I can't use the RequestCookieCollection(IDictionary<string,string>) constructor to initialize a collection with cookies already in it and set that on the request.什么是绊倒我的是, OwinRequest.Cookies属性的类型的RequestCookieCollection ,这似乎并没有允许修改,而本身的属性只读这意味着我不能使用RequestCookieCollection(IDictionary<string,string>)构造函数来初始化一个已经包含 cookie 的集合,并在请求中设置它。

I want do do something like this:我想做这样的事情:

var context = new OwinContext();
// option 1:
context.Request.Cookies.Append("cookie-name", "cookie-value");
// option 2:
var cookies = new RequestCookieCollection(new Dictionary<string, string>{ { "cookie-name", "cookie-value" } });
context.Request.Cookies = cookies;

await myMiddleware.Invoke(context);

// Assert stuff about context.Response

but that doesn't work for aforementioned reasons.但由于上述原因,这不起作用。

I had hoped not to have to mock IOwinContext entierly, because it's quite convenient to have it set up with well-functioning Request and Response objects (I need to, among other things, look at Request.User.Identity.IsAuthenticated in my implementation).我曾希望不必完全模拟IOwinContext ,因为使用功能良好的RequestResponse对象设置它非常方便(除其他外,我需要在我的实现中查看Request.User.Identity.IsAuthenticated ) .

Using the following as the subject of an example middleware for unit testing使用以下作为单元测试示例中间件的主题

using RequestDelegate = Func<IOwinContext, Task>;

class MyMiddleware {
    private readonly RequestDelegate next;

    public MyMiddleware(RequestDelegate next) {
        this.next = next;
    }

    public async Task Invoke(IOwinContext context) {
        //The middleware is supposed to look at the cookies of the incoming request, 
        var request = context.Request;
        var authenticated = request.User.Identity.IsAuthenticated;

        var cookies = request.Cookies;
        if (cookies["cookie-name"] != null) {
            // 1. check for existence of a cookie on the incoming request. 
            //if the cookie exists, use its value to set a header, 
            //so that the pipline after this component thinks the header was always present.
            request.Headers.Append("header-name", "header-value");
        }

        //2. call the next component.
        await next.Invoke(context);

        //3. on the way out, check for some conditions, and possibly set a cookie value.
        if (authenticated) {
            context.Response.Cookies.Append("cookie-name", "cookie-value");
        }
    }
}

and using the details provided about the desired behavior,并使用提供的有关所需行为的详细信息,

it would be difficult to unit test the controller with all the plumbing needed for a full OwinContext .很难使用完整OwinContext所需的所有管道对控制器进行单元测试。

You have already stated a lot of the limitations around accessing some of the members.您已经说明了许多有关访问某些成员的限制。

Owin however provides many abstractions that would allow for the desired behavior to be mock/stubbed/faked for an isolated unit test.然而,Owin 提供了许多抽象,允许对孤立的单元测试进行模拟/存根/伪造所需的行为。

The following example is based on the subject provided above, and uses the Moq mocking framework along with concrete implementations to properly setup and exercise an isolated unit test for the subject middleware.以下示例基于上面提供的主题,并使用Moq模拟框架和具体实现来正确设置和执行主题中间件的隔离单元测试。

[TestClass]
public class OwinMiddlewareCookiesTest {
    [Test]
    public async Task MyMiddleware_Should_Set_RequestHeader_And_ResponseHeader() {
        //Arrange
        var cookieStore = new Dictionary<string, string> { { "cookie-name", "cookie-value" } };
        var cookies = new RequestCookieCollection(cookieStore);

        var request = Mock.Of<IOwinRequest>();
        var requestMock = Mock.Get(request);
        requestMock.Setup(_ => _.Cookies).Returns(cookies);
        requestMock.Setup(_ => _.User.Identity.IsAuthenticated).Returns(true);
        requestMock.Setup(_ => _.Headers.Append(It.IsAny<string>(), It.IsAny<string>()));

        var response = new OwinResponse();

        var context = Mock.Of<OwinContext>();
        var contextMock = Mock.Get(context);
        contextMock.CallBase = true;
        contextMock.Setup(_ => _.Request).Returns(request);
        contextMock.Setup(_ => _.Response).Returns(response);

        RequestDelegate next = _ => Task.FromResult((object)null);

        var myMiddleware = new MyMiddleware(next);

        //Act
        await myMiddleware.Invoke(context);

        //Assert
        requestMock.Verify(_ => _.Headers.Append("header-name", "header-value"));
        response.Headers.ContainsKey("Set-Cookie");
    }
}

Only the necessary dependencies needed to be mocked for the test to flow to completion and the expected behavior verified.只需要模拟必要的依赖项,测试就可以完成并验证预期的行为。

And since the request implementation's Cookies property was not able to be overridden, the abstraction had to be used.由于无法覆盖请求实现的Cookies属性,因此必须使用抽象。 This however provided greater flexibility in mocking the desired behavior.然而,这在模拟所需行为方面提供了更大的灵活性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM