NBomber load tests with token authentication

Na vytváranie load testov som používal jMeter. Ide o skutočne komplexný nástroj, s ktorým zvládnete v podstate všetko, na čo si spomeniete. Vizuálna definícia záťažových testov však nemusí vyhovovať každému.

Aj preto som hľadal iné možnosti. Našiel som NBomber, lightweight framework na vytváranie záťažových testov. Zapáčil sa mi natoľko, že sme ho začali používať.

Tento článok nebude o tom, čo sú záťažové testy, ani to nebude všeobecný návod na NBomber (majú to pekne popísané vo svojej dokumentácii). Bude to o tom ako autentifikovať virtuálnych používateľov pri dotazovaní na API server.

Testujete záťaž, takže chcete desiatky, stovky, tisíce, ... virtuálnych používateľov. Ale na dané požiadavky zvyčajne nepotrebujete (alebo nechcete) toľko reálanych používateľov (kont).

Najprv načítame používateľov, ktorých chceme použiť:

List<User> users = new()
{
    new("user1", "pwd1"), new("user2", "pwd2"), new("user3", "pwd3"),
    new("user4", "pwd4"), new("user5", "pwd5")
};

V reálnom svete hodnoty pochádzajú z nejakého konfiguračného súboru.

Definujeme, ako získať autorizačný token z vášho autentifikačného servera.

Napr.:

async Task<string> GetUserToken(User user)
{
    using var client = new HttpClient();
    client.BaseAddress = new Uri("https://your_authentication_server_uri");

    // call your authentication server
    var response = await client.PostAsJsonAsync("/api/login", user);

    return await response.Content.ReadAsStringAsync();
}

Toto je kľúčové. Definujeme, ako sa vytvorí klient použitý v každom kroku. Použijeme na to ClientFactory.

// Create 5 http clients for 5 real users with token
var httpFactory = ClientFactory.Create(
    "http_factory",
    clientCount: 5,
    initClient: async (number, _) =>
    {
        var client = new HttpClient();
        client.BaseAddress = new Uri("http://api_server_uri");
        client.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue(
                "Bearer",
                await GetUserToken(users[number]));
        return client;
    });

clientCount: 5 musí zodpovedať počtu skutočných užívateľov.

V inicializačnej časti získame token a nastavíme ho ako predvolenú autorizačnú hlavičku:

client.DefaultRequestHeaders.Authorization = 
    new AuthenticationHeaderValue(
        "Bearer", 
        await GetUserToken(users[number])

Každý virtuálny používateľ potom používa token jedného z týchto piatich skutočných používateľov.

Všetky kroky v rámci jednej iterácie scenára používajú rovnakého klienta.

A teraz môžeme použiť tohto klienta s wait context.Client....

var getListStep = Step.Create("Get projects list", httpFactory, async context =>
{
    var response = await context.Client.GetAsync("/api/projects", context.CancellationToken);

    if (!response.IsSuccessStatusCode)
    {
        return Response.Fail(statusCode: (int)response.StatusCode);
    }

    var projects = await response.Content.ReadFromJsonAsync<IEnumerable<Project>>();

    return Response.Ok(statusCode: (int)response.StatusCode, payload: projects!.First().Id);
});

Celý demo projekt si môžete pozrieť na Github.