DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • Selecting the Right Automated Tests
  • Supercharging Productivity in Microservice Development With AI Tools
  • Mastering Node.js: The Ultimate Guide
  • Improving Customer-Facing App Quality Using Tricentis Testim

Trending

  • Anomaly Detection: Leveraging Rule Engines to Minimize False Alarms
  • A Continuous Testing Approach to Performance
  • Three Ways AI Is Reshaping DevSecOps
  • Snowflake vs. Data Bricks: Compete To Create the Best Cloud Data Platform
  1. DZone
  2. Software Design and Architecture
  3. Cloud Architecture
  4. Ingredients for Well-Designed OWIN Middleware Components (Part 3)

Ingredients for Well-Designed OWIN Middleware Components (Part 3)

Now that you've learned about decoupling the pipeline, learn more about testing with OWIN middleware.

Dennis Doomen user avatar by
Dennis Doomen
·
Aug. 28, 15 · Tutorial
Like (1)
Save
Tweet
Share
4.75K Views

Join the DZone community and get the full member experience.

Join For Free

In my last post, I talked about decoupling the construction of the OWIN pipeline from the definition of the OWIN middleware component. In this post, I'm going to talk about the next ingredient, the testing story.

Ingredient 4: Testing your entire HTTP pipeline

In most projects that are building ASP.NET MVC or WebAPI controllers, I've observed two testing strategies:

  1. Test the controller class directly by passing in strongly typed message objects and analyzing the resulting objects.
  2. Keep the controller code as thin as possible and test the underlying service code.

There used to be a WebAPI self-host that you could use from inside your unit test, but it still requires an actual network port. Although slow, it can work, provided that you don't run your unit tests in parallel (the default for XUnit 2).

The beauty of OWIN is that it doesn't have a direct dependency on an actual network stack. It just defines an abstraction based on simple types like dictionaries, tasks and funcs. It's Microsoft's Katana that provides an IIS host, a console host and even a Windows Service host. So ideally, your unit test could cover the entire OWIN pipeline without the need of real network ports, nor has any restrictions when they run in parallel with other unit tests. If you use OWIN, I wrote a unit test that asserts that Piercer returns its own assemblies when run from inside the unit test runner AppDomain like this.

 [Fact]
public async Task When_specifying_an_explicit_route_it_should_be_reachable_through_that_route()
{
    var appBuilder = new AppBuilder();
    appBuilder.UsePiercer(new PiercerSettings().AtRoute("/myroute"));
    AppFunc app = appBuilder.Build();

    var httpClient = new HttpClient(new OwinHttpMessageHandler(appFunc));

    var result = await httpClient
      .GetStringAsync("http://localhost/myroute/piercer/assemblies");

    result.Should().Contain("Piercer.Middleware");
}

Using an independent AppBuilder allows you to build an in-memory OWIN pipeline accessible through an AppFunc. To send actual HTTP requests into that pipeline from a typical HttpClient object, you can use a nifty little library created by Damian Hickey, the OwinHttpMessageHandler. It will convert the HttpRequestMessage objects that the HttpClient client sends into the dictionary that the AppFunc expects, all without ever touching a network stack. You can check out its implementation here.

So imagine you're writing a middleware component that needs to communicate with the outside world. I assume you would probably add a property or method to your middleware's settings class (like PiercerSettings) taking the URL to communicate with. Well, don't do that. Instead, either take an HttpClient or a Uri and an optional HttpMessageHandler instance. If you take that optional HttpMessageHandler, callers can either pass in the actual URL to connect to or the aforementioned OwinHttpMessageHandler like this:

 appBuilder.UsePiercer(new PiercerSettings()
.ConnectingTo(new Uri("http://localhost"));

Or within unit tests:

appBuilder.UsePiercer(new PiercerSettings()
.ConnectingTo(new Uri("http://localhost", new OwinHttpMessageHandler(appFunc))
);

The definition of the ConnectingTo method might look like this:

public PiercerSettings ConnectingTo(Uri uri, HttpMessageHandler handler = null)
{
httpClient = new HttpClient(handler ?? new HttpClientHandler())
{
BaseAddress = uri
};

return this;
}

Now assume that within your unit test, the component that you're connecting to should also be hosted on the same AppBuilder. You can't pass in the AppFunc until you've completed building the pipeline, so we're at a kind of stand-off here. You can fix that by relying on the delayed execution of lambda expressions. Just redefine the ConnectingTo method so that it takes a Func:

 public PiercerSettings ConnectingTo(Uri uri, Func handlerFunc = null) { }

Then, you can do this:

AppFunc appFunc = null;

appBuilder.UsePiercer(new PiercerSettings()
.ConnectingTo(() => new OwinHttpMessageHandler(appFunc));

appBuilder.UseOtherService();

appFunc = appBuilder.Build();

The only caveat is that your middleware component shouldn't try to access the other service until the OWIN pipeline has been fully build. If that doesn't happen until your component is receiving its first HTTP request, you'll be fine. But if your component is doing some kind of background processing, you'll have to delay that explicitly, something I'll discuss in the next ingredient.

unit test Middleware

Published at DZone with permission of Dennis Doomen, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Selecting the Right Automated Tests
  • Supercharging Productivity in Microservice Development With AI Tools
  • Mastering Node.js: The Ultimate Guide
  • Improving Customer-Facing App Quality Using Tricentis Testim

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: