Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there way to get DashboardWebApplication or TracesViewModel service in aspire distributed testing? #4881

Closed
martasp opened this issue Jul 13, 2024 · 4 comments
Labels
area-app-testing Issues pertaining to the APIs in Aspire.Hosting.Testing untriaged New issue has not been triaged

Comments

@martasp
Copy link

martasp commented Jul 13, 2024

When I get the aspire TracesViewModel service from services it's null.

How can I get the aspire TracesViewModel service?

    [Fact]
    public async Task EndpointCall_ShouldGenerateDistributedTraces()
    {
        // Arrange
        var appHost = await DistributedApplicationTestingBuilder.CreateAsync<Projects.AspireShop_AppHost>();
        await using var app = await appHost.BuildAsync();
        await app.StartAsync();

        // getting aspire TracesViewModel service, but tracesViewModel is null ?
        var tracesViewModel = app.Services.GetService<TracesViewModel>();
        //how can i get aspire TracesViewModel service?

        // Act
        var httpClient = app.CreateHttpClient("frontend");
        var response = await httpClient.GetAsync("/");

        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        Assert.NotEmpty(tracesViewModel.GetTraces().Items);

        await app.StopAsync();
    }
@dotnet-issue-labeler dotnet-issue-labeler bot added the area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication label Jul 13, 2024
@martasp
Copy link
Author

martasp commented Jul 13, 2024

I managed to get the TracesViewModel service by adding a hosted service DashboardWebApplication:

services.AddHostedService<DashboardWebApplication>();

Here is the full code snippet:

using System.Net;
using Microsoft.Extensions.DependencyInjection;
using Aspire.Dashboard;
using Aspire.Dashboard.Model;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Builder;
using System.Reflection;
using Microsoft.Extensions.Hosting;

namespace SamplesIntegrationTests.Works;

public class MyServiceConfigurator
{
    public void Configure(IServiceCollection services)
    {
    }
}
public static class ServiceCollectionExtensions
{
    public static object GetService(this IServiceProvider provider, Type serviceType, Type implementationType)
    {
        if (provider is null)
            throw new ArgumentNullException(nameof(provider));

        if (serviceType is null)
            throw new ArgumentNullException(nameof(serviceType));

        if (implementationType is null)
            throw new ArgumentNullException(nameof(implementationType));

        var service = provider.GetServices(serviceType)
                              .FirstOrDefault(s => s.GetType() == implementationType);

        if (service == null)
            throw new InvalidOperationException($"No service for type '{serviceType}' and implementation '{implementationType}' has been registered.");

        return service;
    }

    public static TService GetService<TService>(this IServiceProvider provider, Type implementationType) where TService : class
    {
        if (provider is null)
            throw new ArgumentNullException(nameof(provider));

        if (implementationType is null)
            throw new ArgumentNullException(nameof(implementationType));

        var service = provider.GetServices<TService>()
                              .FirstOrDefault(s => s.GetType() == implementationType);

        if (service == null)
            throw new InvalidOperationException($"No service for type '{typeof(TService)}' and implementation '{implementationType}' has been registered.");

        return service;
    }
}
public static class DashboardWebApplicationExtensions
{
    public static IServiceProvider GetAppServices(this DashboardWebApplication dashboardWebApplication)
    {
        // Use reflection to get the private _app field
        FieldInfo appField = typeof(DashboardWebApplication).GetField("_app", BindingFlags.NonPublic | BindingFlags.Instance);
        if (appField == null)
        {
            throw new InvalidOperationException("The DashboardWebApplication class does not contain a private field named '_app'.");
        }

        // Get the _app instance from the dashboardWebApplication instance
        WebApplication appInstance = appField.GetValue(dashboardWebApplication) as WebApplication;
        if (appInstance == null)
        {
            throw new InvalidOperationException("Unable to retrieve the WebApplication instance from the DashboardWebApplication instance.");
        }

        // Return the IServiceProvider from the WebApplication instance
        return appInstance.Services;
    }
}

public static class IDistributedApplicationTestingBuilderExtensions
{
    public static IServiceCollection AddDashboardWebApplication(this IServiceCollection services)
    {
        var loggerFactory = LoggerFactory.Create(builder =>
        {
            builder.AddConsole();
        });

        var myServiceConfigurator = new MyServiceConfigurator();
        var logger = loggerFactory.CreateLogger<DashboardWebApplication>();
        services.AddTransient(sp => logger);
        services.AddTransient<Action<IServiceCollection>>(sp => myServiceConfigurator.Configure);
        services.AddHostedService<DashboardWebApplication>();
        return services;
    }

    public static IServiceProvider GetDashboardWebApplication(this IServiceProvider services)
    {
        var dashboardWebApplication = (DashboardWebApplication)services.GetService<IHostedService>(typeof(DashboardWebApplication));
        var serviceProvider = dashboardWebApplication.GetAppServices();
        return serviceProvider;
    }


}

public class Works()
{
    [Fact]
    public async Task EndpointCall_ShouldGenerateDistributedTracesFails()
    {
        // Arrange
        var appHost = await DistributedApplicationTestingBuilder.CreateAsync<Projects.AspireShop_AppHost>();
        await using var app = await appHost.BuildAsync();
        await app.StartAsync();

        // getting aspire TracesViewModel service, but tracesViewModel is null ?
        var tracesViewModel = app.Services.GetService<TracesViewModel>();
        //how can i get aspire TracesViewModel service?

        // Act
        var httpClient = app.CreateHttpClient("frontend");
        var response = await httpClient.GetAsync("/");

        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        Assert.NotEmpty(tracesViewModel.GetTraces().Items);

        await app.StopAsync();
    }

    [Fact]
    public async Task EndpointCall_ShouldGenerateDistributedTracesWorks()
    {
        // Arrange
        var appHost = await DistributedApplicationTestingBuilder.CreateAsync<Projects.AspireShop_AppHost>();
        appHost.Services.AddDashboardWebApplication();
        await using var app = await appHost.BuildAsync();
        await app.StartAsync();

        var tracesViewModel = app.Services.GetDashboardWebApplication().GetService<TracesViewModel>();

        // Act
        var httpClient = app.CreateHttpClient("frontend");
        var response = await httpClient.GetAsync("/");

        // Assert
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        Assert.NotEmpty(tracesViewModel.GetTraces().Items);

        await app.StopAsync();
    }

}

Are there any better ways to get traces, and logs for testing?

@martasp
Copy link
Author

martasp commented Jul 13, 2024

Adding full poc repo: dotnet/aspire-samples#361

@davidfowl davidfowl added area-app-testing Issues pertaining to the APIs in Aspire.Hosting.Testing feature and removed area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication labels Sep 15, 2024
@davidfowl
Copy link
Member

This is a feature request. Ability to collect traces/structured logs/metrics in testing.

@joperezr joperezr added the untriaged New issue has not been triaged label Oct 15, 2024
@davidfowl davidfowl removed the feature label Oct 16, 2024
@davidfowl
Copy link
Member

Related to #2897

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-app-testing Issues pertaining to the APIs in Aspire.Hosting.Testing untriaged New issue has not been triaged
Projects
None yet
Development

No branches or pull requests

3 participants