- Lab
-
Libraries: If you want this lab, consider one of these libraries.
- Core Tech
Guided: Testing ASP.NET Core 10 Web APIs
In this lab, you will write and run automated tests for an existing ASP.NET Core 10 Web API to validate routing, validation, authentication, and error-handling behavior without relying on fragile mocks. You will use in-memory hosting and realistic HTTP pipelines to ensure that the API behaves correctly under real-world conditions.
Lab Info
Table of Contents
-
Challenge
Introduction
In this lab, you will learn how to test an ASP.NET Core Web API using both unit tests and integration tests. Automated testing helps ensure that your API behaves correctly as the application evolves and new features are added.
You will go through the below points in this lab:
- Analyze the risks of relying only on manual API testing
- Write unit tests to validate controller decision logic
- Implement integration tests using in-memory hosting
- Verify routing, validation, and HTTP responses
- Test authentication and authorization behavior
- Evaluate how to balance isolated tests with realistic API testing
Use Case
You are working as a developer on a Product Management Web API used by internal business applications.
The API provides endpoints to:
- Retrieve products
- Create products
- Update product information
- Delete products
The application currently works correctly when tested manually using Swagger or Postman. However, the project does not include any automated unit or integration tests for the API.
Without these automated tests, several issues may go unnoticed when the system changes, such as:
- Routing misconfigurations
- Validation rules not being enforced
- Middleware or authentication changes affecting access
- Incorrect HTTP responses being returned
Automated testing ensures that these behaviors are verified every time the code changes.
What You Will Build
In this lab, you will progressively introduce a testing strategy for the API. You will start by writing unit tests that validate controller logic in isolation. Then, you will introduce integration tests that execute the full HTTP request pipeline to verify routing, validation, and authentication behavior.
By the end of the lab, your API will be supported by a structured test suite that verifies both application logic and framework-integrated behavior.
Note: Each step contains tasks clearly marked with comments like
// Task 3.1info> If you get stuck on a task, you can view the
solutionin the solution folder in your Filetree, or click the Task Solution link at the bottom of each task after you've attempted it. -
Challenge
Understanding the API Testing Strategy
In this step, you will review the existing ASP.NET Core application and understand how it will be tested throughout this lab.
The application is available in the
/ProductApi/Apiproject and exposes endpoints to manage products.You will work with the
/ProductApi/Testsproject, which contains automated tests that validate the behavior of the API.
Overview of the API
The
/ProductApi/Apifolder contains a controller namedProductsControllerthat provides the following endpoints:GET /products→ Retrieve all productsPOST /products→ Create a new productDELETE /products/{id}→ Delete a product
These endpoints will be tested in the upcoming steps.
Controller and Dependency Setup
The controller depends on a repository:
IProductRepositoryThis repository is responsible for:
- Storing product data
- Retrieving products
- Deleting products
It is injected using ASP.NET Core’s dependency injection system.
Validation Behavior
The
POST /productsendpoint includes validation logic.If invalid input is provided, the API returns:
400 BadRequestYou will write tests to verify this behavior.
Authentication Behavior
Some endpoints require authentication.
-
If a request is sent without authentication:
401 Unauthorized -
If the request is authenticated:
200 OK
In later steps, you will simulate authentication using a test-specific handler.
Understanding the API Testing Strategy
In this step, you will also understand how automated API testing is structured and why different types of tests are needed.
Why Automated API Testing Matters
When an API grows and multiple developers contribute to it, several types of changes can affect behavior:
- A developer modifies routing attributes
- Validation attributes are added or removed
- Middleware configuration changes
- Authorization policies are updated
- Response structures change
Even though the application still compiles and starts successfully, the API behavior might have changed.
Without automated tests, these changes may only be discovered later by users or systems that depend on the API.
Automated tests provide a reliable way to verify that the API continues to behave as expected.
Two Common Types of API Tests
When testing ASP.NET Core Web APIs, two types of automated tests are commonly used:
Unit Tests
Unit tests validate small pieces of application logic in isolation.
For a Web API, unit tests typically verify:
- Controller decision logic
- Business rule branching
- Returned result types
For example, a controller might return:
NotFound()if an item does not existOk()if the item is successfully retrieved
Unit tests are fast and focused but do not verify the full HTTP request pipeline.
Integration Tests
Integration tests validate how the API behaves when a real HTTP request is executed.
These tests run the full ASP.NET Core request pipeline and verify:
- Routing behavior
- HTTP status codes
- Model binding and validation
- Middleware execution
- Authentication and authorization rules
Integration tests simulate real client requests and ensure the API behaves correctly end-to-end.
Combining Both Testing Approaches
A well-designed API test suite typically includes both types of tests:
| Test Type | Purpose | | ----------------- | ------------------------------------------------ | | Unit Tests | Validate controller logic and decision branches | | Integration Tests | Validate real HTTP behavior and request pipeline |
Using both approaches provides better coverage while keeping tests fast and maintainable.
What You Will Be Testing
Based on this setup, you will validate:
- Correct API responses for valid requests
- Validation failures for invalid input
- Authorization behavior for protected endpoints
- API responses and status codes for valid requests
- Validation behavior for invalid input
- Authorization behavior for protected endpoints
In the next step, you will begin implementing your first unit tests to validate the decision logic inside the
ProductsController. -
Challenge
Writing Unit Tests for Controller Logic
In this step, you will create your first unit tests to verify the decision logic inside the
ProductsController. ### Understanding Controller Decision LogicFile:
/ProductApi/Api/ProductController.csConsider the following controller method used to retrieve a product by its ID.
[HttpGet("{id}")] public IActionResult GetById(int id) { var product = _repository.Get(id); if (product == null) { return NotFound(); } return Ok(product); }This method produces two outcomes:
- If the repository returns
null, the controller returnsNotFound()(HTTP 404) - If a product is found, the controller returns
Ok(product)(HTTP 200)
To isolate the controller logic, you will mock the repository dependency so that the controller receives predictable input. Now that you have configured the mock repository to simulate a missing product, the controller will receive
nullwhen it attempts to retrieve the product.Next, you will verify how the controller responds to this scenario by asserting the returned result. Review the test method
GetById_ReturnsOk_WhenProductExistsinProductsControllerTests, which covers the scenario where the product exists.
In this step, you implemented
ProductsControllerTestsclass.These tests are designed to verify that:
- The controller returns
404 Not Foundwhen a product does not exist. - The controller returns
200 OKwhen a product is found.
These unit tests validate the controller’s decision logic in isolation. However, they do not verify how the API behaves when accessed through HTTP requests.
In the next step, you will introduce integration tests to validate routing and the full ASP.NET Core request pipeline.
- If the repository returns
-
Challenge
Testing the API with Integration Tests
In this step, you will write an integration test that sends an actual HTTP request to the API and verifies the response.
Understanding Integration Testing in ASP.NET Core
Open
Tests/CustomWebApplicationFactory.cs.This class uses
WebApplicationFactoryto start the ASP.NET Core application during test execution.Using this:
- The API runs without starting a real web server
- The full request pipeline is executed
- Tests can interact with the API using HTTP
In this setup:
CustomWebApplicationFactorystarts the API during the test runCreateClient()creates anHttpClientconnected to the test server- The test uses
_clientto send HTTP requests to the API endpoints
With this change, the integration test now sends an HTTP request to the/productsendpoint and verifies that the API returns a successful response. This helps confirm that the endpoint is reachable and that the API can process the request correctly through the configured request pipeline.
In the next step, you will test how the API handles invalid input and verify that it returns the expected validation response.
-
Challenge
Validation and Error Testing
APIs must also handle invalid input correctly. When a request does not contain the required data, the API should reject it and return the appropriate HTTP response.
In this step, you will complete a validation test that sends invalid input to the API and verifies that the API returns a
400 Bad Requestresponse. With this change, the validation test now verifies that the API rejects invalid input and returns a400 Bad Requestresponse. This helps confirm that the API handles invalid requests correctly during test execution.In the next step, you will test how the API handles unauthorized access and verify that protected endpoints return the expected response.
-
Challenge
Authentication and Authorization Tests
APIs may also protect certain endpoints so that only authenticated users can access them. When a request is sent without the required credentials, the API should reject it and return the appropriate HTTP response.
In this lab, authentication is simplified using a custom handler implemented in
TestAuthHandler.cs. This handler extends the ASP.NET Core authentication pipeline and overrides the default behavior to simulate authentication during tests. Instead of validating real tokens, it inspects incoming requests for a predefined test header.- If the expected header is present, the handler creates a valid
ClaimsPrincipaland marks the request as authenticated. - If the header is missing, the handler does not authenticate the request, and the API responds with
401 Unauthorized.
This approach allows you to control authentication behavior directly within your tests, without depending on external systems like identity providers or token services.
In this step, you will complete a security test that sends a request to a protected endpoint without authentication and verifies that the API correctly rejects it by returning a
401 Unauthorizedresponse. With this change, the security test now verifies that the protected endpoint rejects unauthenticated requests and returns the expected HTTP response.With these changes, the security tests now verify both authorization outcomes:
- unauthenticated requests are rejected
- authenticated requests are allowed
This helps confirm that the API enforces access rules correctly during test execution.
Moment of truth! Now run all the tests using the
dotnet test Testscommand. Once all tests pass successfully, it confirms that the API logic, validation, and authentication behavior are working correctly.In the next step, you will review the completed test suite and evaluate how unit tests and integration tests work together to provide reliable API coverage.
- If the expected header is present, the handler creates a valid
-
Challenge
Evaluating Test Design
At this point, your tests cover controller logic, endpoint behavior, validation, and authorization. You can now review how these tests work together and evaluate whether the overall testing approach has the right balance between isolation, realism, and performance.
In this step, you will review the completed tests, compare the role of unit tests and integration tests, and identify practical guidelines for building a maintainable API testing strategy.
Reviewing the Completed Tests
Open the test project and review the following files:
ProductsControllerTests.csProductsIntegrationTests.csProductValidationTests.csProductAuthTests.cs
Each file focuses on a different aspect of API behavior.
What Each Test Type Validates
The completed tests include both unit tests and integration tests.
| Test File | Test Type | What It Validates | |---|---|---| |
ProductsControllerTests.cs| Unit Test | Controller decision logic | |ProductsIntegrationTests.cs| Integration Test | Endpoint accessibility and successful HTTP responses | |ProductValidationTests.cs| Integration Test | Validation behavior for invalid input | |ProductAuthTests.cs| Integration Test | Authorization behavior for protected endpoints |This separation ensures that each part of the API is tested at the appropriate level.
Comparing Unit Tests and Integration Tests
- Unit tests verify controller logic in isolation and are typically faster to run.
- Integration tests verify routing, validation, authorization, and real HTTP behavior, but they usually take longer because they execute more of the application.
A balanced testing strategy uses unit tests for isolated decision logic and integration tests for behavior that must be validated through the full request pipeline.
Guidelines for Sustainable API Testing
To keep API tests maintainable over time, follow these guidelines:
- Use unit tests for controller logic and decision branches
- Use integration tests for routing, validation, and authorization behavior
- Test observable behavior instead of internal implementation details
- Avoid tightly coupling tests to framework internals
- Keep each test focused on one clear outcome
These guidelines help ensure that tests remain reliable, readable, and useful as the API evolves.
In the next step, you will review what you completed and identify practical next steps for expanding your API testing strategy.
-
Challenge
Conclusion and next steps
You have now completed the lab and built a practical testing foundation for an ASP.NET Core Web API.
Lab Summary
In this lab, you learned how to:
- Write unit tests for ASP.NET Core Web API controller logic
- Run integration tests using
WebApplicationFactoryandHttpClient - Verify validation and authorization behavior through automated tests
- Evaluate the balance between isolation, realism, and maintainability in a test suite
You now have a practical approach for testing ASP.NET Core Web APIs using a combination of focused unit tests and realistic integration tests.
Next Steps
You can continue improving this testing strategy by exploring the following areas:
- Add tests for update and delete scenarios with different outcomes
- Verify response payloads, not only status codes
- Test custom error responses and problem details payloads
- Add more authorization scenarios for different roles or policies
- Integrate the test suite into a CI pipeline so tests run automatically on every change
These next steps will help you build a stronger and more maintainable API testing practice.
About the author
Real skill practice before real-world application
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Learn by doing
Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.
Follow your guide
All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.
Turn time into mastery
On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.