AspNetCore.Simple.MsTest.Sdk 5.0.0

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

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

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

Simple object comparisons

[TestMethod]
public void Simple_Object_Comparison()
{
    var person1 = new Person("Son", "Goku", 29);
    var person2 = new Person("Muten", "Roshi", 63);

    Assert.That.ObjectsAreEqual(person1, person2, title: "Persons are not equal");
}

[TestMethod]
public void Simple_Object_Comparison()
{
    var firstNumber = 1;
    var secondNumber = 2;

    Assert.That.ObjectsAreEqual(firstNumber,secondNumber, title: "Persons are not equal");
}
Assert.IsTrue failed. 

Persons are not equal

 ---------------------------------- 
 | MemberPath | person1 | person2 |
 ---------------------------------- 
 | Name       | Son     | Muten   |
 ---------------------------------- 
 | FamilyName | Goku    | Roshi   |
 ---------------------------------- 
 | Age        | 29      | 63      |
 ---------------------------------- 

 Count: 3

Current result:

{"Name":"Muten","FamilyName":"Roshi","Age":63}

Expected result:

{"Name":"Son","FamilyName":"Goku","Age":29}

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;
    }
}

Ignore functionality on error response

[TestMethod]
public Task Should_Handle_Error_Response_With_Filter_Func()
{
    // 1. Call endpoint which will return an error response
    return Client.AssertPostAsErrorAsync<ProblemDetails>("api/tests/v1/errors/not-implemented",
                                                         "ErrorResponse.json",
                                                         DifferenceFunc);

    // 2. Intercept difference detection also for error response
    static IEnumerable<Difference> DifferenceFunc(IImmutableList<Difference> differences)
    {
        foreach (var difference in differences)
        {
            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();
}

Whole 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");
}

Replacements

[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<GetUserByIdResponse>($"api/v1/users/{addedUserResponse.User.Id}"
                                                     "Users.V1.Results.AddedUser.json",
                                                     [("{Id}", addedUserResponse.User.Id)]); // New, will replace in the Users.V1.Results.AddedUser.json the {Id} with the value of addedUserResponse.User.Id

    // 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 net9.0 is compatible. 
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
5.0.0 44 11/20/2024
4.0.13 160 11/18/2024
4.0.12 158 11/13/2024
4.0.11 383 11/6/2024
4.0.10 6,422 9/6/2024
4.0.9 114 9/5/2024
4.0.8 378 9/3/2024
4.0.7 4,411 8/28/2024
4.0.6 448 8/27/2024
4.0.5 51 8/27/2024
4.0.4 41 8/27/2024
4.0.3 81 8/26/2024
4.0.2 41 8/26/2024
4.0.1 46 8/26/2024
4.0.0 57 8/26/2024
3.1.1 383 8/21/2024
3.1.0 119 8/20/2024
3.0.4 367 8/5/2024
3.0.3 3,653 6/20/2024
3.0.2 567 5/26/2024
3.0.1 1,339 4/29/2024
3.0.0 195 4/23/2024
2.0.2 484 4/7/2024
2.0.1 2,015 2/7/2024
2.0.0 3,692 11/28/2023
1.1.8 1,449 10/29/2023
1.1.7 77 10/29/2023
1.1.6 207 10/24/2023
1.1.5 4,891 7/20/2023
1.1.4 244 7/20/2023
1.1.3 1,738 5/31/2023
1.1.2 94 5/29/2023
1.1.1 753 5/20/2023
1.1.0 94 5/20/2023
1.0.9 1,574 4/30/2023
1.0.8 214 4/19/2023
1.0.7 99 4/18/2023
1.0.6 97 4/18/2023
1.0.5 1,183 3/23/2023
1.0.4 113 3/23/2023
1.0.3 3,742 1/1/2023
1.0.2 122 1/1/2023
1.0.1 112 1/1/2023
1.0.0 143 11/24/2022
0.7.0 2,503 11/21/2022
0.7.0-alpha.24 126 11/21/2022
0.7.0-alpha.23 2,187 10/23/2022
0.7.0-alpha.22 100 10/23/2022
0.7.0-alpha.20 170 10/20/2022
0.7.0-alpha.18 478 10/7/2022
0.7.0-alpha.16 119 10/3/2022
0.7.0-alpha.14 112 10/3/2022
0.7.0-alpha.13 118 10/3/2022
0.7.0-alpha.12 117 10/3/2022
0.7.0-alpha.11 103 10/3/2022
0.7.0-alpha.8 107 10/3/2022
0.7.0-alpha.7 113 10/3/2022
0.6.1 100 10/1/2022
0.6.0 79 10/1/2022
0.5.1 1,158 9/4/2022
0.5.0 85 9/4/2022
0.4.0 90 9/3/2022
0.3.1 118 8/24/2022
0.3.0 228 8/4/2022
0.2.0 205 7/25/2022
0.2.0-alpha.89 231 7/8/2022
0.2.0-alpha.87 123 7/8/2022
0.2.0-alpha.85 224 7/4/2022
0.2.0-alpha.83 179 6/30/2022
0.2.0-alpha.82 155 6/21/2022
0.2.0-alpha.81 269 6/19/2022
0.2.0-alpha.79 123 6/19/2022
0.2.0-alpha.77 237 5/25/2022
0.2.0-alpha.71 446 4/28/2022
0.2.0-alpha.70 306 3/11/2022
0.2.0-alpha.69 129 3/11/2022
0.2.0-alpha.68 979 7/2/2021
0.2.0-alpha.67 2,138 5/17/2021
0.2.0-alpha.66 702 4/26/2021
0.2.0-alpha.63 183 4/25/2021
0.2.0-alpha.54 318 4/22/2021
0.2.0-alpha.53 161 4/22/2021
0.2.0-alpha.51 188 4/20/2021
0.2.0-alpha.49 252 4/18/2021
0.2.0-alpha.48 204 4/18/2021
0.2.0-alpha.47 166 4/18/2021
0.2.0-alpha.46 154 4/17/2021
0.2.0-alpha.45 159 4/17/2021
0.2.0-alpha.44 161 4/17/2021
0.2.0-alpha.43 170 4/17/2021
0.1.0-alpha.39 167 4/17/2021
0.1.0-alpha.37 183 4/17/2021
0.1.0-alpha.35 181 4/17/2021
0.1.0-alpha.34 183 4/17/2021
0.1.0-alpha.31 176 4/16/2021
0.1.0-alpha.30 190 4/14/2021
0.1.0-alpha.29 159 4/14/2021
0.1.0-alpha.28 243 4/10/2021
0.1.0-alpha.20 169 4/9/2021
0.1.0-alpha.18 176 4/9/2021
0.1.0-alpha.17 195 4/9/2021
0.1.0-alpha.16 199 4/9/2021
0.1.0-alpha.14 181 4/9/2021
0.1.0-alpha.13 167 4/8/2021
0.1.0-alpha.12 356 4/5/2021

Remove ObjectComparer package to compare objects. Comparison is now done by json. Add new difference func to all error assert helpers.