Chapter 6: Integration testing and monitoring
The chapter introduction explains why monitoring a running application matters and places it alongside predeployment testing as a way to improve resilience.
After this chapter you will be able to
- Early detection and reliability. Explain how monitoring helps teams detect problems early and improve an application's reliability, availability, and user experience, while testing takes place before deployment.
- Three monitoring components. State that logging, metrics, and traces are essential components of monitoring distributed applications, and explain that each contributes a distinct kind of visibility into application behavior and performance.
- Logging and diagnostic purpose. Explain that logging captures and stores application or system events to track execution, record errors and exceptions, and help troubleshoot problems.
- Metrics and their uses. Explain what metrics measure, give examples, and describe how measurements over time help monitor applications and plan resources.
- Request journeys across services. Explain that a trace follows one request across the microservices it interacts with, providing end-to-end visibility. Describe how this helps identify dependencies, latency problems, and bottlenecks.
- Spans within a trace. Describe spans as the individual operations that make up a trace and state what information they capture.
- OpenTelemetry as Aspire monitoring foundation. Explain that Aspire uses OpenTelemetry to collect metrics, traces, and logs, and why the Aspire template chooses it without requiring it as the only option.
- OpenTelemetry collection configuration. Describe how the telemetry registration method configures logging, metrics, and traces in the orchestrated applications.
- Telemetry exporter endpoint configuration. Explain how the application chooses an OpenTelemetry export endpoint in development and how the section contrasts that with production.
- Dashboard telemetry views. Describe how the Aspire dashboard brings monitoring information together after the app launches: Console shows a service's console logs, Structured shows logs across services, and the Traces and Metrics tabs show their respective telemetry.
- Telemetry and current health. Explain that Aspire uses OpenTelemetry by default to collect telemetry, while logs, traces, and metrics show what happened in the past rather than whether each application is working correctly right now.
- Recovery and fault tolerance. Explain how monitoring individual services lets an orchestrator detect unhealthy instances, recover from failures, and limit the spread of failures between dependent services.
- Dashboard service states. Describe where Aspire displays each managed service’s current status and what a normally running service looks like.
- Health endpoint and startup waiting. Explain how Aspire checks a service’s health and distinguish the roles of WithHttpHealthCheck(), WithReference(), and WaitFor() in the example.
- Default health-check registration. Describe how AddDefaultHealthChecks() registers health-check logic: AddHealthChecks() is called on the builder’s Services, and AddCheck() adds a check whose lambda returns a healthy response.
- Default health endpoint mapping. Explain how MapDefaultEndpoints() adds health-check endpoints to HTTP-enabled Aspire applications and how /health serves as the basic health-check path.
- Production health-check availability. Explain why the Starter Project’s development-only endpoint registration does not mean health checks should be limited to development. State that health checks should run in every environment, especially production, using the same endpoints and paths.
- Production endpoint protection. Explain why production health-check endpoints may need protection and why the template does not prescribe one implementation. Describe when reusing the development health-check logic may be reasonable.
- Purpose of integration tests. Describe integration tests as automated checks that an application or module works with other components, potentially using real networks, HTTP requests, or database connections.
- Distributed testing with Aspire. Explain why integration testing orchestrated distributed applications has traditionally been difficult and how Aspire lowers the barrier, including its integration-testing project template.
- Aspire test project creation. Describe the two template-based ways to create an Aspire test project: select Create Test Project when creating an Aspire Starter Project in Visual Studio, or use the .NET CLI to create a test project with its dependencies.
- Testing framework flexibility. Explain that the Aspire test template defaults to xUnit, but Aspire testing libraries also work with NUnit and MSTest. Describe the option to start with a standard .NET test project and add Aspire integration-testing dependencies.
- Aspire test project dependencies. Describe how to prepare OnlineShop.Tests for integration testing by adding the Aspire.Hosting.Testing NuGet package and a reference to OnlineShop.AppHost.
- Aspire test host lifecycle. Explain how the test creates and runs a test version of the OnlineShop Aspire application and why it disposes of that application.
- Service discovered HTTP check. Trace how the test targets the web frontend and verifies its response.
- Testing framework differences. Distinguish the NUnit and MSTest versions of the xUnit integration test by naming their test attributes and assertions while explaining what stays the same.
- Response Content Versus Status. Explain why a 200 response from the home page is a limited test and how checking response content makes an integration test more informative.
- Products Page HTML Assertion. Describe how the expanded test uses AngleSharp to parse the products page response and checks the first h1 element for the Products title.
- Component versus integration tests. Explain why testing individual components complements integration testing in a distributed application.
- Counter component test checks. Describe the two behaviors tested on the Blazor Counter page and how the initial value is checked.
What it builds on
- Introducing service defaults (chapter 1) · Introduces the Service Defaults project.
- Service discovery in Aspire (chapter 1) · Service discovery in Aspire
- Adding extension methods (chapter 1) · The use of extension methods for shared service defaults.
- Testing the complete workflow (chapter 1) · The difficulty of testing a complete distributed workflow.
- Host application startup and service registration (chapter 1) · Aspire host application startup and service registration.
- Using service discovery (chapter 1) · Using Aspire service discovery to address a registered service.
- Hosting an LLM anywhere with Ollama (chapter 5) · The application’s Ollama-hosted model.
Section by section
Introduction
The chapter introduction explains why monitoring a running application matters and places it alongside predeployment testing as a way to improve resilience.
- Early detection and reliability: Explain how monitoring helps teams detect problems early and improve an application's reliability, availability, and user experience, while testing takes place before deployment.
Why it matters: Monitoring helps you spot trouble while there is still time to limit its effect on users. - Resource use and capacity planning: Explain how monitoring resource use and performance supports bottleneck detection, capacity planning, and cost-effective infrastructure decisions.
Why it matters: The same measurements that expose slowdowns can guide scaling and spending decisions. - Security and compliance monitoring: Explain why monitoring security events and required service measures helps organizations identify threats and demonstrate compliance.
Why it matters: Monitoring provides evidence both for responding to suspicious activity and for meeting reporting obligations.
Key terms: monitoring, reliability, availability, uptime, response times, error rates, CPU use, memory use, capacity planning, right-size
6.1 Components of monitoring systems
Introduces logging, metrics, and traces as distinct components that make distributed applications observable.
- Three monitoring components: State that logging, metrics, and traces are essential components of monitoring distributed applications, and explain that each contributes a distinct kind of visibility into application behavior and performance.
Why it matters: These three components provide the starting point for understanding monitoring in a distributed system.
Key terms: logging, metrics, traces, monitoring systems
6.1.1 Logging
This section explains what application logging records, how those records help troubleshooting, and where logs can be stored.
- Logging and diagnostic purpose: Explain that logging captures and stores application or system events to track execution, record errors and exceptions, and help troubleshoot problems.
Why it matters: Logs give you a record to consult when an application behaves unexpectedly. - Log contents and destinations: Describe the kinds of details a log entry can contain and the places logs are typically written.
Why it matters: The details in a log entry and where it is stored affect what you can consult during troubleshooting.
Key terms: logging, logs, diagnostic information, log entries, timestamps, severity levels, centralized logging platforms
6.1.2 Metrics
Defines metrics as regularly collected numerical measurements and explains how they help monitor application and system performance over time.
- Metrics and their uses: Explain what metrics measure, give examples, and describe how measurements over time help monitor applications and plan resources.
Why it matters: Metrics help you notice performance problems and changing resource needs rather than relying on a single snapshot.
Key terms: metrics, performance, resource use, throughput, error rates
6.1.3 Traces
This section explains how traces show a request’s path through a distributed system and how spans make that path useful for diagnosing performance.
- Request journeys across services: Explain that a trace follows one request across the microservices it interacts with, providing end-to-end visibility. Describe how this helps identify dependencies, latency problems, and bottlenecks.
Why it matters: Following a request across services helps locate where a distributed workflow slows down. - Spans within a trace: Describe spans as the individual operations that make up a trace and state what information they capture.
Why it matters: Spans provide the operation-level detail needed to examine a request’s journey.
Key terms: trace, end-to-end visibility, service dependencies, bottlenecks, spans, timing information, contextual data, metadata
6.2 Monitoring Aspire applications
This section explains why Aspire uses OpenTelemetry for monitoring and what the standard Service Defaults monitoring dependencies do.
- OpenTelemetry as Aspire monitoring foundation: Explain that Aspire uses OpenTelemetry to collect metrics, traces, and logs, and why the Aspire template chooses it without requiring it as the only option.
Why it matters: This explains what Aspire’s monitoring setup is built on and why it can work with different observability tools. - Telemetry export and tracing dependencies: Describe the roles of the standard Service Defaults packages for exporting telemetry and managing tracing startup and shutdown.
Why it matters: These dependencies show how the default project supports telemetry export and tracing. - Application and runtime instrumentation: Distinguish what the ASP.NET Core, HTTP, and runtime OpenTelemetry instrumentation packages contribute.
Why it matters: Each instrumentation dependency covers a different source or path for monitoring information.
Key terms: OpenTelemetry, metrics, traces, logs, vendor-agnostic, Service Defaults, OpenTelemetry.Exporter.OpenTelemetryProtocol, OTLP exporter, OpenTelemetry.Extensions.Hosting, OpenTelemetry.Instrumentation.AspNetCore
6.3 Configuring the telemetry
This section explains how the ServiceDefaults project configures OpenTelemetry collection and export for an orchestrated application.
- OpenTelemetry collection configuration: Describe how the telemetry registration method configures logging, metrics, and traces in the orchestrated applications.
Why it matters: Collecting all three telemetry types gives the application information it can export for monitoring. - Telemetry exporter endpoint configuration: Explain how the application chooses an OpenTelemetry export endpoint in development and how the section contrasts that with production.
Why it matters: The export destination determines where collected telemetry can be sent, and the development default is not a production address.
Key terms: OnlineShop.ServiceDefaults, OpenTelemetry logging, metrics, traces, AddOpenTelemetryExporters(), OTEL_EXPORTER_OTLP_ENDPOINT, OpenTelemetry endpoint, telemetry exporters
6.4 Viewing information in the dashboard
The section shows where to view an Aspire application's telemetry in the dashboard and explains why those views do not establish whether services are healthy right now.
- Dashboard telemetry views: Describe how the Aspire dashboard brings monitoring information together after the app launches: Console shows a service's console logs, Structured shows logs across services, and the Traces and Metrics tabs show their respective telemetry.
Why it matters: Knowing which dashboard view to open makes the application's monitoring information easier to find. - Telemetry and current health: Explain that Aspire uses OpenTelemetry by default to collect telemetry, while logs, traces, and metrics show what happened in the past rather than whether each application is working correctly right now.
Why it matters: Past telemetry is useful for investigation, but it cannot by itself answer whether an application is working correctly now.
Key terms: Aspire dashboard, Console tab, Structured tab, Traces tab, Metrics tab, OpenTelemetry, logging, traces, metrics
6.5 Health checks in Aspire
This section explains why an orchestrator should assess microservice health to keep a distributed system reliable, efficient, and observable.
- Recovery and fault tolerance: Explain how monitoring individual services lets an orchestrator detect unhealthy instances, recover from failures, and limit the spread of failures between dependent services.
Why it matters: A failure in one service need not become a failure of the whole application. - Scaling and resource allocation: Describe how service health, demand, and performance inform an orchestrator’s decisions about scaling instances and allocating resources.
Why it matters: Health information helps the system meet demand without wasting resources. - Service objectives and observability: Explain how monitoring and aggregating microservice health information helps the orchestrator track service-level objectives and understand the system’s overall state.
Why it matters: A combined view of service health helps operators spot problems and protect availability and performance commitments.
Key terms: health assessment, automatic recovery, fault tolerance, orchestrator, dynamic scaling, optimized resource allocation, SLA enforcement, enhanced observability, health metrics, status information
6.5.1 Service statuses in Aspire’s dashboard
This section shows how Aspire displays service status, checks service health through an endpoint, and uses health checks to delay dependent services until a dependency is healthy.
- Dashboard service states: Describe where Aspire displays each managed service’s current status and what a normally running service looks like.
Why it matters: The State column is a quick place to check whether Aspire-managed services are running as expected. - Health endpoint and startup waiting: Explain how Aspire checks a service’s health and distinguish the roles of WithHttpHealthCheck(), WithReference(), and WaitFor() in the example.
Why it matters: A service can wait for a dependency to become healthy rather than merely assuming it is ready. - Health-check state meanings: Distinguish the Healthy, Unhealthy, and Degraded states described by the standard health-check mechanism.
Why it matters: These states distinguish an app that works normally from one that works poorly or may not fulfill its primary purpose.
Key terms: Resources tab, State column, Running, health-check endpoint, WithHttpHealthCheck(), /health, WithReference(), WaitFor(), Healthy, Unhealthy
6.5.2 How health checks are configured inside Aspire apps
This section explains how the Aspire Starter Project registers a basic health check and what its healthy response does—and does not—indicate.
- Default health-check registration: Describe how AddDefaultHealthChecks() registers health-check logic: AddHealthChecks() is called on the builder’s Services, and AddCheck() adds a check whose lambda returns a healthy response.
Why it matters: This explains where the starter project’s health-check response comes from. - Basic check response limits: Explain what this simple check reports while the application is running and what happens when its endpoint becomes inaccessible.
Why it matters: A healthy response from this simple check has a narrower meaning than a thorough test of application behavior.
Key terms: AddDefaultHealthChecks(), AddHealthChecks(), AddCheck(), Services, health-check endpoint, healthy status, Aspire orchestrator
6.5.3 Mapping health-check endpoints
This section explains how Aspire maps health-check HTTP endpoints and how the /alive endpoint checks for a live tag.
- Default health endpoint mapping: Explain how MapDefaultEndpoints() adds health-check endpoints to HTTP-enabled Aspire applications and how /health serves as the basic health-check path.
Why it matters: It connects the health-check logic to an HTTP path that can be checked. - Aliveness endpoint and live tag: Distinguish the /alive endpoint from the basic /health endpoint by describing the additional live-tag check.
Why it matters: The /alive check requires more than a valid response: it also looks for the live tag.
Key terms: MapDefaultEndpoints(), MapHealthChecks(), /health, AlivenessEndpointPath, /alive, live tag, orchestrator
6.5.4 Monitoring app health in production
Explains why Aspire health checks should remain available in production, what to consider when exposing their endpoints, and how health checks differ from tests.
- Production health-check availability: Explain why the Starter Project’s development-only endpoint registration does not mean health checks should be limited to development. State that health checks should run in every environment, especially production, using the same endpoints and paths.
Why it matters: Development-only registration in the template should not be mistaken for a recommendation to omit production health checks. - Production endpoint protection: Explain why production health-check endpoints may need protection and why the template does not prescribe one implementation. Describe when reusing the development health-check logic may be reasonable.
Why it matters: Production health checks need an exposure decision suited to the application, rather than an assumed template default. - Health checks versus tests: Distinguish what running health checks tell you from what tests are intended to establish before production.
Why it matters: A healthy running service does not replace checking the application before release.
Key terms: health-check endpoints, development environment condition, production environment, appropriate protections, health-check logic, health checks, tests, real-time state
6.6 Setting up integration tests in Aspire
This section explains why automated integration tests matter for distributed applications and why Aspire makes them easier to run.
- Automated tests and regressions: Explain why automated tests are useful before release and as an application changes: they verify behavior efficiently and can reveal when a change breaks earlier functionality.
Why it matters: Automated tests help catch problems before users encounter them and make later changes safer. - Purpose of integration tests: Describe integration tests as automated checks that an application or module works with other components, potentially using real networks, HTTP requests, or database connections.
Why it matters: This distinction clarifies what the tests in this part of the chapter are meant to verify. - Distributed testing with Aspire: Explain why integration testing orchestrated distributed applications has traditionally been difficult and how Aspire lowers the barrier, including its integration-testing project template.
Why it matters: It explains why Aspire’s testing support is valuable for applications with several coordinated components.
Key terms: automated tests, test coverage, test-driven development (TDD), integration tests, components, real HTTP requests, real database connections, orchestrated distributed applications, integration testing project, component/unit tests
6.6.1 Adding a test project
This section outlines ways to add an Aspire integration test project and explains that Aspire testing is not tied to one test framework.
- Aspire test project creation: Describe the two template-based ways to create an Aspire test project: select Create Test Project when creating an Aspire Starter Project in Visual Studio, or use the .NET CLI to create a test project with its dependencies.
Why it matters: These options provide a starting point for integration testing an Aspire application. - Testing framework flexibility: Explain that the Aspire test template defaults to xUnit, but Aspire testing libraries also work with NUnit and MSTest. Describe the option to start with a standard .NET test project and add Aspire integration-testing dependencies.
Why it matters: An existing application can use a suitable .NET test project without being locked into the default template or xUnit.
Key terms: Create Test Project, Aspire Starter Project, .NET CLI, xUnit, NUnit, MSTest, framework-agnostic, Aspire integration testing
6.6.2 Installing required dependencies for testing
This section identifies the package and project references needed to prepare an Aspire integration-test project.
- Aspire test project dependencies: Describe how to prepare OnlineShop.Tests for integration testing by adding the Aspire.Hosting.Testing NuGet package and a reference to OnlineShop.AppHost.
Why it matters: These references put the test project in a position to test the Aspire application and its host. - Framework packages versus Aspire package: Distinguish the Aspire-specific testing package from the packages supplied for the chosen testing framework, such as xUnit.
Why it matters: It helps you tell which dependency is for Aspire and which dependencies come from your choice of test framework.
Key terms: OnlineShop.Tests, Aspire.Hosting.Testing, OnlineShop.AppHost, xUnit, NUnit, MSTest
6.6.3 Adding Aspire integration tests
The section walks through an Aspire integration test that starts the application, requests the web frontend, and checks its response.
- Arrange act assert structure: Describe how the example divides a test into setup, an action, and verification.
Why it matters: This structure makes it easier to see what the integration test sets up, does, and checks. - Aspire test host lifecycle: Explain how the test creates and runs a test version of the OnlineShop Aspire application and why it disposes of that application.
Why it matters: The test needs the orchestrated application running, and it must release resources such as network connections afterward. - Service discovered HTTP check: Trace how the test targets the web frontend and verifies its response.
Why it matters: This check exercises the hosted web frontend at its discovered address rather than requiring a hard-coded URL. - Whole host test cost: Explain why this seemingly simple HTTP test can take a long time to run.
Why it matters: A single HTTP assertion can still incur the startup cost of the application’s other hosted services.
Key terms: arrange-act-assert, DistributedApplicationTestingBuilder, CreateAsync<T>(), Projects.OnlineShop_AppHost, await using, HttpClient, Aspire service discovery, webfrontend, HTTP GET, OK (200)
6.6.4 Using different testing frameworks
Shows how to use NUnit or MSTest for the same Aspire integration test by changing framework-specific test attributes and assertions.
- Testing framework differences: Distinguish the NUnit and MSTest versions of the xUnit integration test by naming their test attributes and assertions while explaining what stays the same.
Why it matters: You can keep the integration test’s logic when using a different testing framework.
Key terms: xUnit, NUnit, MSTest, [Fact], [Test], [TestMethod]
6.6.5 Expanding test functionality
This section improves an Aspire integration test by checking the content of a products page as well as its HTTP response status.
- Response Content Versus Status: Explain why a 200 response from the home page is a limited test and how checking response content makes an integration test more informative.
Why it matters: A page can respond successfully without displaying what the test expects. - Products Page HTML Assertion: Describe how the expanded test uses AngleSharp to parse the products page response and checks the first h1 element for the Products title.
Why it matters: This gives the test a concrete assertion about what the products page displays.
Key terms: HTTP response, 200 response code, page content, AngleSharp, HTTP response object, h1 HTML element, Products title
6.6.6 What about component testing?
This section explains why component tests complement integration tests and uses bUnit tests of a Blazor Counter page to show the behavior they can check.
- Component versus integration tests: Explain why testing individual components complements integration testing in a distributed application.
Why it matters: The two kinds of tests check different things, so a fast component test does not replace a workflow test. - bUnit package preparation: Describe the package preparation the section recommends for Blazor component tests.
Why it matters: The recommended package setup avoids a dependency conflict before writing the component tests. - Counter component test checks: Describe the two behaviors tested on the Blazor Counter page and how the initial value is checked.
Why it matters: The example makes component testing concrete by checking a rendered value and a response to a click.
Key terms: component tests, integration tests, real networking, Bunit, Bunit.TestDoubles, AngleSharp, NuGet packages, Counter page, CounterStartsAtZero(), ClickingButtonIncrementsCounter()
6.7 Summary
Where this is used later
- Closing thoughts (chapter 10) · Monitoring Aspire applications
- Deploying to Azure Container Apps (chapter 9) · Production deployment must configure telemetry rather than use the local development endpoint.
Concepts explained in this chapter
- monitoringMonitoring is the continuous collection and analysis of application and infrastructure signals to detect problems and assess operational behavior.
- health checkA health check is an automated assessment of whether an application or dependency is ready and functioning sufficiently for interaction.
- health check endpointA health-check endpoint is an application endpoint that reports health or readiness information about the application.
- Aspire testing libraryAspire.Hosting.Testing is a .NET package that provides components for testing Aspire applications.
- Aspire dashboardThe web interface that presents the state and telemetry of resources in an Aspire application.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
