AspNetCore.Simple.MsTest.Sdk 2.0.2

This package has a SemVer 2.0.0 package version: 2.0.2+3.
dotnet add package AspNetCore.Simple.MsTest.Sdk --version 2.0.2
NuGet\Install-Package AspNetCore.Simple.MsTest.Sdk -Version 2.0.2
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.Simple.MsTest.Sdk" Version="2.0.2" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add AspNetCore.Simple.MsTest.Sdk --version 2.0.2
#r "nuget: AspNetCore.Simple.MsTest.Sdk, 2.0.2"
#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.
// Install AspNetCore.Simple.MsTest.Sdk as a Cake Addin
#addin nuget:?package=AspNetCore.Simple.MsTest.Sdk&version=2.0.2

// Install AspNetCore.Simple.MsTest.Sdk as a Cake Tool
#tool nuget:?package=AspNetCore.Simple.MsTest.Sdk&version=2.0.2

AspNetCore.Simple.MsTest.Sdk

Target of this package is to write more efficient clean test against your ASP.Net Core API's. You will save tons of Assert and will be able to write even more faster and better readable test as before. Main reason was to be more focused on the Test-First approach.

Getting started

Prerequisites

Install the package

dotnet add package AspNetCore.Simple.MsTest.Sdk

Samples

Basic concept

// Those assert helper are smart they do following things:
// - Assser the expected call for Sucess -> AssertPostAsync -> Ok AssertPostAsErrorAsync -> NOK
// - It compares the complete reponse structure for equality -> Not only IsSuccessStatusCode, Also deep object 
// - You can provide json as string, or like here in the sample a json file which is embedded in the test assembly
// - Why json -> it is that fomat which is used for communication, and you can directly use your payloads in curl, 
//   postman or anywhere -> if you have c# code -> transform first into json, not nice to handle comparison
// - With this syntax you see directly the route which will be called
// - It is all made for maximize productivity

await Client.AssertPostAsync<AddUserReponse>($"api/v1/users/",                                                                        
                                             "Users.V1.Payloads.NewUser.json,
                                             "Users.V1.Results.NewUser.json);

Embedded json file or native json string

[TestMethod]
public Task Should_Return_No_Users_If_No_One_Was_Added()
{
    return Client.AssertGetAsync<GetAllUsersResponse>("v1/users", "EmptyUserResponse.json");
}
[TestMethod]
public Task Should_Return_No_Users_If_No_One_Was_Added()
{
    return Client.AssertGetAsync<GetAllUsersResponse>("v1/users", """{ "Users": [] }""");
}

Assert that GET all Users will returned 401 Unauthorized

[TestMethod]
public Task Should_Not_Return_All_Users_Without_Authentication()
{
    return Client.AssertGetAsUnauthorizedAsync("v1/users");
}

Assert that GET a user which not exists returns ProblemDetails

[TestMethod]
public Task Should_Return_Not_Found_Error_If_User_Does_Not_Exits()
{
    return Client.AssertGetAsErrorAsync<ProblemDetails>($"v1/users/{1234}", "UserGetByIdErrorResponse.json");
}

Assert that GET all Users is successful and checks that response is empty

[TestMethod]
public Task Should_Return_No_Users_If_No_One_Was_Added()
{
    return Client.AssertGetAsync<GetAllUsersResponse>("v1/users", "EmptyUserResponse.json");
}

Create a User and Ignore an Id which maybe is generated by the backend or database

[TestMethod]
public Task Should_Return_Expected_Result_For_Given_Payload_But_Ignore_Id()
{
    await Client.AssertPostAsync<AddUserReponse>($"api/v1/users/",                                                                        
                                                 "Users.V1.Payloads.NewUser.json",
                                                 "Users.V1.Results.NewUser.json",
                                                 differenceFunc: DifferenceFunc);
}

// Difference func can be used to intercept the object comparison in the background
private IEnumerable<Difference> DifferenceFunc(IImmutableList<Difference> differences)
{
    foreach (var difference in differences)
    {
        // Here we ignore the Id property. Real world scenario generated id by database as an example
        if (difference.MemberPath == nameof(User.Id))
        {
            continue;
        }

        yield return difference;
    }
}

Fetch data from an API and do a post order to bring the items in the right order

[TestMethod]
public Task Should_Return_Expected_Result_For_Given_Payload_And_Sorted()
{
    return Client.AssertGetAsync<IEnumerable<Person>>($"api/v1/users/",                                                                                                                             
                                                      "Users.V1.Results.AllUsers.json",
                                                      filterFunc: FilterFunc);
}

// The filter func can be used to sort or do some custom post filtering
// Sample: You get unsorted results from API so each call will provide
//         the users in different order. Why a something like a DB query
//         without sort action will not guarantee the order of the results.
//         If results does not match the expected results (order as well), 
//         the test will fail
private IEnumerable<Person> FilterFunc(IEnumerable<Person> arg)
{
    return arg.OrderBy(x => x.Id).ToImmutableList();
}

Hold create, get and delete scenario. Looks nice and clean

[TestMethod]    
public Task Should_Return_The_User_Which_Was_Added()
{
    // 1. Add an user
    var addedUserResponse = await Client.AssertPostAsync<AddUserReponse>($"api/v1/users/",
                                                                         "Users.V1.Payloads.NewUser.json",
                                                                         "Users.V1.Results.NewUser.json"); 

    // 2. Get the currently added user
    await Client.AssertGetAsync<GetAllUserResponse>($"api/v1/users/{addedUserResponse.User.Id}"
                                                     "Users.V1.Results.AddedUser.json");

    // 3. Delete the alrady added user -> Dependent on your test setup a test-tear down can also contain a cleanup step to remove all the created sources
    await Client.AssertDeleteAsync($"api/v1/users/{addedUserResponse.User.Id}"
                                   "Users.V1.Results.Deleteduser.json");
}

Curl for each Asserted call

How pratical can it be so share call scenarios with your consumers. For that reason you see in the test output the curl command for each asserted call.

-----------------------------------------------------------
Http call as curl
-----------------------------------------------------------
curl \
--location \
--request GET 'https://localhost:5001/api/tests/v1/persons'
-----------------------------------------------------------
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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. 
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
2.0.2 274 4/7/2024
2.0.1 1,771 2/7/2024
2.0.0 2,920 11/28/2023
1.1.8 1,418 10/29/2023
1.1.7 57 10/29/2023
1.1.6 181 10/24/2023
1.1.5 4,861 7/20/2023
1.1.4 221 7/20/2023
1.1.3 1,718 5/31/2023
1.1.2 78 5/29/2023
1.1.1 730 5/20/2023
1.1.0 69 5/20/2023
1.0.9 1,551 4/30/2023
1.0.8 195 4/19/2023
1.0.7 81 4/18/2023
1.0.6 79 4/18/2023
1.0.5 1,163 3/23/2023
1.0.4 93 3/23/2023
1.0.3 3,720 1/1/2023
1.0.2 101 1/1/2023
1.0.1 91 1/1/2023
1.0.0 116 11/24/2022
0.7.0 1,790 11/21/2022
0.7.0-alpha.24 105 11/21/2022
0.7.0-alpha.23 2,174 10/23/2022
0.7.0-alpha.22 87 10/23/2022
0.7.0-alpha.20 155 10/20/2022
0.7.0-alpha.18 459 10/7/2022
0.7.0-alpha.16 104 10/3/2022
0.7.0-alpha.14 97 10/3/2022
0.7.0-alpha.13 102 10/3/2022
0.7.0-alpha.12 96 10/3/2022
0.7.0-alpha.11 88 10/3/2022
0.7.0-alpha.8 92 10/3/2022
0.7.0-alpha.7 97 10/3/2022
0.6.1 86 10/1/2022
0.6.0 65 10/1/2022
0.5.1 1,142 9/4/2022
0.5.0 69 9/4/2022
0.4.0 77 9/3/2022
0.3.1 103 8/24/2022
0.3.0 217 8/4/2022
0.2.0 191 7/25/2022
0.2.0-alpha.89 218 7/8/2022
0.2.0-alpha.87 106 7/8/2022
0.2.0-alpha.85 212 7/4/2022
0.2.0-alpha.83 166 6/30/2022
0.2.0-alpha.82 144 6/21/2022
0.2.0-alpha.81 256 6/19/2022
0.2.0-alpha.79 110 6/19/2022
0.2.0-alpha.77 219 5/25/2022
0.2.0-alpha.71 433 4/28/2022
0.2.0-alpha.70 291 3/11/2022
0.2.0-alpha.69 115 3/11/2022
0.2.0-alpha.68 965 7/2/2021
0.2.0-alpha.67 2,125 5/17/2021
0.2.0-alpha.66 688 4/26/2021
0.2.0-alpha.63 166 4/25/2021
0.2.0-alpha.54 306 4/22/2021
0.2.0-alpha.53 147 4/22/2021
0.2.0-alpha.51 175 4/20/2021
0.2.0-alpha.49 237 4/18/2021
0.2.0-alpha.48 191 4/18/2021
0.2.0-alpha.47 151 4/18/2021
0.2.0-alpha.46 138 4/17/2021
0.2.0-alpha.45 143 4/17/2021
0.2.0-alpha.44 146 4/17/2021
0.2.0-alpha.43 155 4/17/2021
0.1.0-alpha.39 152 4/17/2021
0.1.0-alpha.37 161 4/17/2021
0.1.0-alpha.35 167 4/17/2021
0.1.0-alpha.34 168 4/17/2021
0.1.0-alpha.31 161 4/16/2021
0.1.0-alpha.30 174 4/14/2021
0.1.0-alpha.29 143 4/14/2021
0.1.0-alpha.28 228 4/10/2021
0.1.0-alpha.20 156 4/9/2021
0.1.0-alpha.18 160 4/9/2021
0.1.0-alpha.17 179 4/9/2021
0.1.0-alpha.16 184 4/9/2021
0.1.0-alpha.14 159 4/9/2021
0.1.0-alpha.13 155 4/8/2021
0.1.0-alpha.12 343 4/5/2021

Upgrade to .Net 8