AspNetCore.IntegrationTesting 2.0.0

The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package AspNetCore.IntegrationTesting --version 2.0.0
                    
NuGet\Install-Package AspNetCore.IntegrationTesting -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AspNetCore.IntegrationTesting" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AspNetCore.IntegrationTesting" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="AspNetCore.IntegrationTesting" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AspNetCore.IntegrationTesting --version 2.0.0
                    
#r "nuget: AspNetCore.IntegrationTesting, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AspNetCore.IntegrationTesting@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AspNetCore.IntegrationTesting&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=AspNetCore.IntegrationTesting&version=2.0.0
                    
Install as a Cake Tool

AspNetCore.IntegrationTesting

AspNetCore.IntegrationTesting is a simple library allowing you to run integration tests against your controllers in a strongly typed manner. No more magic route strings and determining what HttpMethod to invoke. You can invoke your controller with one line of code!

Our goal is to refactor this:

  [Theory]
 [MemberData(nameof(CommonData))]
 public async Task GetWeatherForPostalCode(int postalCode, int expectedResult){
  
    var response = await Client.GetAsync($"/api/v1/Weather?postalCode={postalCode}");
    var json = await response.Content.ReadAsStringAsync();
    var data = JsonConvert.DeserializeObject<int>(json);
    data.Should().Equal(expectedResult);
   
  } 

Into this:

 [Theory]
 [MemberData(nameof(CommonData))]
 public  async Task GetWeatherForPostalCode(int postalCode, int expectedResult){
    (await Fixture.SingleAsync<WeatherController, int>(controller => controller.Get(postalCode)))
        .Should().Equal(expectedResult);
   
}

Let's get started by installing Xunit.AspNetCore.Integration. This package provides a few abstractions that should make authoring Xunit integration tests easier.

XUnit Fixtures

Xunit provides two mechanisms for shared state, the class fixture and the collection fixture.� Read more here about both.� If we need to share context across tests (classes) we must use a collection fixture.� Collection fixtures are particulary important when running integration tests across multiple classes.� You don't want the overhead of constructing and destroying a TestServer for every class in your test project.

Building Your First Fixture

Due to the way XUnit implements collection fixtures, there is a slight amount of boring code we must write to get things going.

First we will start with the fixture.

public class AwesomeApiIntegrationTestFixture : AbstractIntegrationTestFixture<Startup>{
	public AwesomeApiIntegrationTestFixture(){
	}
}

All we are doing here is stating that we want a fixture that uses the default Startup.cs file residing in your main .net core application. If your integration test needs to override any behavior in this class, simply extend it and use your new class instead.

public class IntegrationTestStartup : Startup{
}

public class AwesomeApiIntegrationTestFixture : AbstractIntegrationTestFixture<IntegrationTestStartup>{
	public AwesomeApiIntegrationTestFixture(){
	}
}

You can also override the default baseAddress used by the HttpClient by using a different base constructor:

public class AwesomeApiIntegrationTestFixture : AbstractIntegrationTestFixture<Startup>{
	public AwesomeApiIntegrationTestFixture() : base( ()=> "http://localhost:8080"){
	}
}

Great. Now our fixture is good to go. Next we need to implement an XUnit collection fixture.

  /// <summary>
    /// This attribute is how you run startup/teardown code in xunit
    /// </summary>
    [CollectionDefinition(nameof(AwesomeApiFixtureCollection))]
    public class AwesomeApiFixtureCollection : ICollectionFixture<AwesomeApiIntegrationTestFixture>
    {
        // This class has no code, and is never created. Its purpose is simply
        // to be the place to apply [CollectionDefinition] and all the
        // ICollectionFixture<> interfaces.
    }

Lastly, let's create a common base class for all our tests. This will serve two purposes:

  1. In order to use the collection fixture, we must decorate our class with a CollectionAttribute. Having a base class eliminates the need to apply for every test class.
  2. The collection fixture gets injected by Xunit at runtime. Now that we define our attribute on a common base class we can also define a default constructor accepting our fixture. Now we don't need to think to much when creating our test classes.
[Collection(nameof(AwesomeApiFixtureCollection))]
public class AwesomeApiTest : AbstractTest<AwesomeApiIntegrationTestFixture>
{
	public AwesomeApiTest(AwesomeApiIntegrationTestFixture fixture) : base(fixture){
	}
}

Our First Integration Test

My tests use FluentAssertions. It's awesome.

Assume we have the following controller defined:

[Route("api/v1/[controller]")]
public class WeatherController : Controller{
  
 [HttpGet()]
 public Task<int> Get([FromQuery] int postalCode){
  {
     switch(postalCode){
        case 19106 : return Task.FromResult(80);
        case 30317 : return Task.FromResult(70);
        default : return Task.FromResult(1);
     }

   }
}

And now for the test.

public class WeatherControllerTests : AwesomeApiTest{
	public WeatherControllerTests(AwesomeApiIntegrationTestFixture fixture) 
           : base(fixture){}
 [Theory]
 [MemberData(nameof(CommonData))]
 public async Task GetWeatherForPostalCode(int postalCode, int expectedResult){
                 (await Fixture.SingleAsync<WeatherController, int>(controller => controller.Get(postalCode)))
                      .Should().Equal(expectedResult);
   
}
   public static TheoryData CommonData
        {
            get
            {
                var data = new TheoryData<int,int>();
                data.Add(19106, 80);
                data.Add(30317, 70);
                data.Add(1, -1);
                return data;
            }
        }
}

Not too bad?

Customization

For most scenarios, the default model binders should be able to map your action parameters back to a URI. However, in some cases, like custom model binders, you will need to write a bit of code to help out.

First create the route binder:

/// <summary>
    /// Pulls  an id from an inbound model and sets it on the route
    /// </summary>
    /// <seealso cref="AbstractRouteBinder" />
    public class CustomFromModelRouteBinder : AbstractRouteBinder
    {
        /// <summary>
        /// Determines whether this instance can bind the specified parameter.
        /// </summary>
        /// <param name="parameter">The parameter.</param>
        /// <returns>
        /// <c>true</c> if this instance can decompose the specified parameter; otherwise, <c>false</c>.
        /// </returns>
        public override bool CanBind(IControllerActionParameter parameter)
        {
            var model = parameter.ParameterValue as MyModel;
            return model != null;
        }

        /// <summary>
        /// Binds the parameter to the route.
        /// </summary>
        /// <param name="parameter">The parameter.</param>
        /// <param name="controllerActionRoute">The controller action route.</param>
        protected override void BindParameter(IControllerActionParameter parameter, IControllerActionRoute controllerActionRoute)
        {
            var model = parameter.ParameterValue as MyModel;
            controllerActionRoute.SetRouteValue("id", model.PersonId);
        }
    } 

Now let's register our binder. You do this in our IntegrationTestFixture constructor.

public class IntegrationTestStartup : Startup{
}

public class AwesomeApiIntegrationTestFixture : AbstractIntegrationTestFixture<IntegrationTestStartup>{
	public AwesomeApiIntegrationTestFixture(){
        ControllerActionParameterBinders.AddBinders(new CustomFromModelBinder());
       
	}
}

That's it! Enjoy.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated

Initial Release