Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
YouTube Security VideosGoogle Cloud Tech: Gemini is coming to your city(24.09.2026 um 15:00 Uhr)
AI & KI NachrichtenGoogle’s latest moonshot to put machine learning in space(24.09.2026 um 15:12 Uhr)
Windows Tipps & SecurityPoll: What's your favorite Surface of 2026?(24.09.2026 um 14:58 Uhr)
Sichere ProgrammierungStreaming Materialized Views for Live Read Models (2026)(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungA Day Is Not 86400 Seconds: The DST Bug in Your Date Math(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungSetting up Traefik: reverse proxy with automatic HTTPS(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungA 200 OK response does not prove a secret leak(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungHow hot do you like it?(24.09.2026 um 15:05 Uhr)
YouTube Security VideosGoogle Cloud Tech: Gemini is coming to your city(24.09.2026 um 15:00 Uhr)
AI & KI NachrichtenGoogle’s latest moonshot to put machine learning in space(24.09.2026 um 15:12 Uhr)
Windows Tipps & SecurityPoll: What's your favorite Surface of 2026?(24.09.2026 um 14:58 Uhr)
Sichere ProgrammierungStreaming Materialized Views for Live Read Models (2026)(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungA Day Is Not 86400 Seconds: The DST Bug in Your Date Math(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungSetting up Traefik: reverse proxy with automatic HTTPS(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungA 200 OK response does not prove a secret leak(24.09.2026 um 15:02 Uhr)
Sichere ProgrammierungHow hot do you like it?(24.09.2026 um 15:05 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Rewriting My First NUnit API Tests: Cleaner, Faster, Better

Have you ever caught yourself thinking: “What am I even doing?” Well, that’s exactly how I feel right now. I was looking back at my previous tutorials and thought: “Why not review them, critique them, and make a better version?” And here we…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

Have you ever caught yourself thinking: “What am I even doing?” Well, that’s exactly how I feel right now. I was looking back at my previous tutorials and thought: “Why not review them, critique them, and make a better version?” And here we are. In this first article, I’ll revisit my initial tutorial: NUNit and C# - Tutorial to automate your API Tests from scratch.



I remember this project was born out of a real need at work. We had a lot of APIs with Postman tests, and we decided to migrate them to NUnit and C#. Now, looking back after over two years, I see many ways to improve it.






Review of the tests that will be automated and the project



Starting from the tests that will be done. For this new project, we will use the https://dummyjson.com/ just to test some concepts, since the API are simple, and after that, we will refactor the tests for NASA API Tests.

For the new endpoint we will automate the following tests:























Endpoint Test Expected results
/users Get all users Should return status 200 and a list with user information
/users/add Create a new user Should return status 200 and user data information


The main goal is to make the project more robust, easier to replicate across scenarios, improve request handling, make it reusable for different projects, enable future CI/CD integration, and ensure sensitive data are protected.





Creating the automated tests



The first major improvement is how we set up API requests. In my first article, I had a separate method for each API request. Now, I’ve made them more abstract and maintainable:




using RestSharp;

namespace ApiTestsWithNUnit.Common;

public class Requests(string baseUrl)
{
private readonly RestClient _client = new(baseUrl);

public async Task<RestResponse<T>> GetAsync<T>(
string endpoint,
Dictionary<string, string>? headers = null)
{
var request = new RestRequest(endpoint, Method.Get);

if (headers != null)
{
foreach (var header in headers)
request.AddHeader(header.Key, header.Value);
}

return await _client.ExecuteAsync<T>(request);
}

public async Task<RestResponse<T>> PostAsync<T>(
string endpoint,
object? body = null,
Dictionary<string, string>? headers = null)
{
var request = new RestRequest(endpoint, Method.Post);

if (body != null)
request.AddJsonBody(body);

if (headers != null)
{
foreach (var header in headers)
request.AddHeader(header.Key, header.Value);
}

return await _client.ExecuteAsync<T>(request);
}
}






And for the tests, they will be used in the following way:




using System.Net;
using ApiTestsWithNUnit.Entities;
using FluentAssertions;
using NUnit.Framework;
using RestSharp;

namespace ApiTestsWithNUnit.Tests;

public class Users
{
private Common.Requests _api;
private Dictionary<string, string> _headers;

[SetUp]
public void Setup()
{
_api = new Common.Requests("https://dummyjson.com");
_headers = new Dictionary<string, string>
{
{ "Content-Type", "application/json" }
};
}

[Test]
public async Task GetAllUsers()
{
var response = await _api.GetAsync<object>("/users");
response.Should().NotBeNull();
response.StatusCode.Should().Be(HttpStatusCode.OK);
var users = response.Content;
users.Should().NotBeNull();
}

[Test]
public async Task CreateNewUser()
{
var bodyRequest = new UsersRequestBody
{
firstName = "John",
lastName = "Doe",
email = "[email protected]",
age = 20
};

var response = await _api.PostAsync<object>("/users/add", bodyRequest, _headers);
response.Should().NotBeNull();
response.StatusCode.Should().Be(HttpStatusCode.Created);
var users = response.Content;
users.Should().Contain(bodyRequest.firstName);
users.Should().Contain(bodyRequest.lastName);
users.Should().Contain(bodyRequest.email);
}

}






Updating the NASA API tests, we will make two changes, first, move all the validations to a new class called CheckBodyResponse.cs:




using FluentAssertions;
using LearningNUnit.BackEnd.Entities;

namespace ApiTestsWithNUnit.Common;

public class CheckBodyResponse
{
public void CheckBodyResponseNasaApi(NasaApiEntity response)
{
response.Date.Should().NotBeNullOrEmpty();
response.Explanation.Should().NotBeNullOrEmpty();
response.Media_type.Should().NotBeNullOrEmpty();
response.Service_version.Should().NotBeNullOrEmpty();
response.Title.Should().NotBeNullOrEmpty();
response.Url.Should().NotBeNullOrEmpty();
}

}






And refactoring our tests, they will look like:




using System.Net;
using System.Text.Json;
using ApiTestsWithNUnit.Common;
using ApiTestsWithNUnit.Config;
using ApiTestsWithNUnit.Entities;
using FluentAssertions;
using LearningNUnit.BackEnd.Entities;
using NUnit.Framework;
using RestSharp.Serializers;

namespace ApiTestsWithNUnit.Tests;

public class NasaApiTest
{
private Common.Requests _api;
private Dictionary<string, string> _headers;
private string _api_key;
private CheckBodyResponse CheckBodyResponse = new();

[SetUp]
public void Setup()
{
_headers = new Dictionary<string, string>
{
{ "Content-Type", "application/json" }
};
_config = TestConfig.GetConfig();
string nasa_url= "https://api.nasa.gov";
_api = new Common.Requests(nasa_url);
_api_key = $"api_key=API_KEY";
}

[Test]
public async Task SearchApodSucess()
{
var response = await _api.GetAsync<string>($"/planetary/apod?{_api_key}", _headers);
response.StatusCode.Should().Be(HttpStatusCode.OK);
if (response.Content != null)
{
var data = JsonSerializer.Deserialize<NasaApiEntity>(
response.Content,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
);
CheckBodyResponse.CheckBodyResponseNasaApi(data);
}
}
[Test]
public async Task SearchApodWithDate()
{
var queryParameters = "date=2023-05-01";
var response = await _api.GetAsync<string>($"/planetary/apod?{_api_key}&{queryParameters}", _headers);
response.StatusCode.Should().Be(HttpStatusCode.OK);
if (response.Content != null)
{
var data = JsonSerializer.Deserialize<NasaApiEntity>(
response.Content,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
);
CheckBodyResponse.CheckBodyResponseNasaApi(data);
}
}

[Test]
public async Task SearchApodWithDateWrongFormat()
{
var queryParameters = "date=2023/05/01";
var response = await _api.GetAsync<string>($"/planetary/apod?{_api_key}&{queryParameters}", _headers);
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}

[Test]
public async Task SearchApodWithStartDateEndDate()
{
const string startDate = "2023-05-01";
const string endDate = "2023-06-01";
var queryParameters = $"start_date={startDate}&end_date={endDate}";
var response = await _api.GetAsync<string>($"/planetary/apod?{_api_key}&{queryParameters}", _headers);
response.StatusCode.Should().Be(HttpStatusCode.OK);
}

[Test]
public async Task SearchApodWithStartDateBiggerThanEndDate()
{
const string startDate = "2023-12-01";
const string endDate = "2023-11-01";
var queryParameters = $"start_date={startDate}&end_date={endDate}";
var response = await _api.GetAsync<string>($"/planetary/apod?{_api_key}&{queryParameters}", _headers);
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
[Test]
public async Task SearchApodWithInvalidToken()
{
var token = "invalidToken";
var response = await _api.GetAsync<string>($"/planetary/apod?{token}", _headers);
response.StatusCode.Should().Be(HttpStatusCode.Forbidden);
}
}






Improvements in This Version:




  • Setup notation: Makes tests cleaner and avoids repeating variables.

  • Abstract requests: One method for multiple endpoints instead of duplicating code.

  • Easier maintenance: The tests are more readable and modular.

  • Making the tests asyncronos.






Beware of sensitive data



Testing user data safely is always tricky. Here, we’ll leverage Rider/Visual Studio’s .runsettings file:




.runsettings is an XML file that allows you to configure test parameters, environments, timeouts, and other TestRunner behaviors in Visual Studio or dotnet test.





<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<TestRunParameters>
<Parameter name="BaseUrl" value="https://dummyjson.com" />
<Parameter name="UserName" value="John" />
<Parameter name="LastName" value="do" />
<Parameter name="Email" value="[email protected]" />
<Parameter name="ApiToken" value="YOUR_API_KEY" />
<Parameter name="NasaBaseUrl" value="https://api.nasa.gov" />
</TestRunParameters>
<RunConfiguration>
<TargetFrameworkVersion>net7.0</TargetFrameworkVersion>
<ResultsDirectory>TestResults</ResultsDirectory>
</RunConfiguration>
</RunSettings>








⚠️ Do not commit this file to version control! Include it in .gitignore.




We can then create a config class to handle local vs. CI environments:




using NUnit.Framework;

namespace ApiTestsWithNUnit.Config;

public class Config
{
public string BaseUrl { get; set; } = string.Empty;
public string UserName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string ApiToken { get; set; } = string.Empty;
public string NasaBaseUrl { get; set; } = string.Empty;
}

public class TestConfig
{
public static Config GetConfig()
{
// Check if it is running in the CI
bool isCi = Environment.GetEnvironmentVariable("GITHUB_ACTIONS") == "true";

if (isCi)
{
return new Config
{
BaseUrl = Environment.GetEnvironmentVariable("BASE_URL")
?? throw new Exception("BASE_URL not found in the CI"),
UserName = Environment.GetEnvironmentVariable("API_KEY")
?? throw new Exception("USER_NAME not found in the CI"),
LastName = Environment.GetEnvironmentVariable("LAST_NAME")
?? throw new Exception("LAST_NAME not found in the CI"),
Email = Environment.GetEnvironmentVariable("LAST_NAME")
?? throw new Exception("EMAIL not found in the CI"),
ApiToken = Environment.GetEnvironmentVariable("API_TOKEN")
?? throw new Exception("API_TOKEN not found in the CI"),
NasaBaseUrl = Environment.GetEnvironmentVariable("NASA_BASE_URL")
?? throw new Exception("NASA_BASE_URL not found in the CI")
};
}
return new Config
{
BaseUrl = TestContext.Parameters["BaseUrl"] ?? "",
UserName = TestContext.Parameters["UserName"] ?? "",
LastName = TestContext.Parameters["LastName"] ?? "",
Email = TestContext.Parameters["Email"] ?? "",
ApiToken = TestContext.Parameters["ApiToken"] ?? "",
NasaBaseUrl = TestContext.Parameters["NasaBaseUrl"] ?? ""
};
}
}






This lets us run the same tests locally or in CI/CD pipelines seamlessly.



Updating our tests, they will look like this:




{}

namespace ApiTestsWithNUnit.Tests;

public class NasaApiTest
{
private Common.Requests _api;
private Dictionary<string, string> _headers;
private Config.Config _config;
private string _api_key;
private CheckBodyResponse CheckBodyResponse = new();

[SetUp]
public void Setup()
{
_headers = new Dictionary<string, string>
{
{ "Content-Type", "application/json" }
};
_config = TestConfig.GetConfig();
_api = new Common.Requests(_config.NasaBaseUrl);
_api_key = $"api_key={_config.ApiToken}";
}

[Test]
public async Task SearchApodSucess()
{
var response = await _api.GetAsync<string>($"/planetary/apod?{_api_key}", _headers);
response.StatusCode.Should().Be(HttpStatusCode.OK);
if (response.Content != null)
{
var data = JsonSerializer.Deserialize<NasaApiEntity>(
response.Content,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
);
CheckBodyResponse.CheckBodyResponseNasaApi(data);
}
}

}






To use the runsettings file in Rider, you need to:




  • File / Settings → Build, Execution, Deployment → Unit Testing → Test Runner

  • Go to Use specific .runconfig/.testsettings settings file and select your .runsettings file.



In the Visual Studio you go to:




  • Go to Test → Configure Run Settings and check if Visual Studio is using the .runsettings file.






Adding Allure report to the tests



We will be using the Allure report to see the results of our tests, you can check how to install here - Allure install.



To add Allure to the project, is pretty simple. You can go directly through Nuget Packages management of your IDE and Search for Allure and install the latest version, or you can run:




dotnet add ⟨PATH TO PROJECT⟩ package Allure.NUnit






Now let's start to add some notations to our project:




  • Let's add to our test class the notation [AllureNunit] so we can use the features of allure.



Here are some notations that will be useful for this example:




























Notation Description
AllureSeverity The severity of the test
AllureOwner The responsible for the test
AllureFeature The feature that will be tested
AllureBefore Setup that is made before the test


This are some of notations that can make the report more clear, so the tests will looks like:




{}

[TestFixture]
[AllureNUnit]
[AllureFeature("Nasa API Tests")]
public class NasaApiTest
{
private Common.Requests _api;
private Dictionary<string, string> _headers;
private Config.Config _config;
private string _api_key;
private CheckBodyResponse CheckBodyResponse = new();

[SetUp]
[AllureBefore("Loading the test variables")]
public void Setup()
{
_headers = new Dictionary<string, string>
{
{ "Content-Type", "application/json" }
};
_config = TestConfig.GetConfig();
_api = new Common.Requests(_config.NasaBaseUrl);
_api_key = $"api_key={_config.ApiToken}";
}

[Test]
[AllureOwner("@m4rri4nne")]
[AllureSeverity(SeverityLevel.critical)]
[AllureDescription("Search for APOD")]
public async Task SearchApodSucess()
{
var response = await _api.GetAsync<string>($"/planetary/apod?{_api_key}", _headers);
response.StatusCode.Should().Be(HttpStatusCode.OK);
if (response.Content != null)
{
var data = JsonSerializer.Deserialize<NasaApiEntity>(
response.Content,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
);
CheckBodyResponse.CheckBodyResponseNasaApi(data);
}
}
}






To configure the allure we will need to add a few steps before run again the tests. First, we will create an json file, with the minimal information needed for Allure:




{
"allure": {
"directory": "allure-results"
}
}






This file should be created on the root of the project. And after that, add to the .csproj the follwing information:




<ItemGroup>
<None Update="allureConfig.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>






And create a global setup to always register the executions, in a AllureConfig.cs file:




using NUnit.Framework;
using Allure.Net.Commons;

[SetUpFixture]
public class AllureSetup
{
[OneTimeSetUp]
public void GlobalSetup()
{
AllureLifecycle.Instance.CleanupResultDirectory();
}
}






After that, remember to: Clean, restore and build your project before run the tests.

After running the tests you can see the results using this command:




allure serve ./bin/Debug/net8.0/allure-results






And there is our report:



Allure report



You can check the notations from allure to add more that can be useful in your context.






Final thoughts



It was fun revisiting a project I built a few years ago. There are more improvements I could make, but I’ve focused on clarity, maintainability, and introducing Allure reports.



One key takeaway: .runsettings for environment variables is great, but for large projects, managing many variables can get tricky. A future improvement could be to orchestrate credentials via JSON files and add to CI/CD workflow.



You can explore the complete solution in my Git repository.

SOC Incident Playbook: Vulnerability Remediation & Verification
title: Detect Exploitation - Rewriting My First NUnit API Tests: Cleaner, Faster, Better
id: d71c30e5-8aab-4359-8cbf-5abf55e7acdc
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-24
logsource:
  category: network_connection
  product: any
detection:
  selection:
      CommandLine|contains:
        - 'exploit'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-24"
        description = "YARA Signature for "
    strings:
        $str = "Rewriting My First NUnit API T" ascii wide
    condition:
        any of them
}
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Kognitive Analyse für identifizierte Bedrohung: Erhöhte Bedrohungslage im Bereich Rewriting My First NUnit API Tests: Clea.... Basierend auf 368k Vektor-Korrelationen werden sofortige Isolationsmaßnahmen für betroffene Endpunkte empfohlen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
🔗 Semantisch verwandte Zero-Days MariaDB 11.7 VEC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Rewriting My First NUnit API Tests: Cleaner, Faster, Better

Thematisch verwandte Begriffe: Rewriting, First, NUnit, Tests · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-97179 | A security vulnerability has been detected in O2OA up to 9.5.3/10.0.2. T…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel TTP ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick