Git Product home page Git Product logo

umplify / xunit-dependency-injection Goto Github PK

View Code? Open in Web Editor NEW
38.0 3.0 12.0 314 KB

:fire: A small library to help .NET developers leverage Microsoft's dependency injection framework in their Xunit-powered test projects

Home Page: https://umplify.github.io/xunit-dependency-injection/

License: MIT License

C# 99.45% HTML 0.55%
xunit xunit-framework xunit-tests xunit-runner xunit-frameworks xunit-test dotnet-core dotnetcore csharp dependency-injection

xunit-dependency-injection's Introduction

Build Status Nuget Nuget

Xunit Dependency Injection framework - .NET 8.0

Xunit does not support any built-in dependency injection features, therefore developers have to come up with a solution to recruit their favourite dependency injection framework in their tests.

This library brings in Microsoft's dependency injection container to Xunit by leveraging Xunit's fixture.

Getting started

Nuget package

First add the following nuget package to your Xunit project:

Install-Package Xunit.Microsoft.DependencyInjection

Setup your fixture

The abstract class of Xunit.Microsoft.DependencyInjection.Abstracts.TestBedFixture contains the necessary functionalities to add services and configurations to Microsoft's dependency injection container. Your concrete test fixture class must derive from this abstract class and implement the following two abstract methods:

protected abstract IEnumerable<string> GetConfigurationFiles();
protected abstract void AddServices(IServiceCollection services, IConfiguration configuration);

GetConfigurationFiles(...) method returns a collection of the configuration files in your Xunit test project to the framework. AddServices(...) method must be used to wire up the implemented services.

Access the wired up services

There are two method that you can use to access the wired up service depending on your context:

public T GetScopedService<T>(ITestOutputHelper testOutputHelper);
public T GetService<T>(ITestOutputHelper testOutputHelper);

To access async scopes simply call the following method in the abstract fixture class:

public AsyncServiceScope GetAsyncScope<T>(ITestOutputHelper testOutputHelper)

Accessing the keyed wired up services in .NET 8.0

You can call the following method to access the keyed already-wired up services:

T? GetKeyedService<T>([DisallowNull] string key, ITestOutputHelper testOutputHelper);

Adding custom logging provider

Test developers can add their own desired logger provider by overriding AddLoggingProvider(...) virtual method defined in TestBedFixture class.

Preparing Xunit test classes

Your Xunit test class must be derived from Xunit.Microsoft.DependencyInjection.Abstracts.TestBed<T> class where T should be your fixture class derived from TestBedFixture.

Also, the test class should be decorated by the following attribute:

[CollectionDefinition("Dependency Injection")]

Clearing managed resources

To have managed resources cleaned up, simply override the virtual method of Clear(). This is an optional step.

Clearing managed resourced asynchronously

Simply override the virtual method of DisposeAsyncCore() for this purpose. This is also an optional step.

Running tests in order

The library also has a bonus feature that simplifies running tests in order. The test class does not have to be derived from TestBed<T> class though and it can apply to all Xunit classes.

Decorate your Xunit test class with the following attribute and associate TestOrder(...) with Fact and Theory:

[TestCaseOrderer("Xunit.Microsoft.DependencyInjection.TestsOrder.TestPriorityOrderer", "Xunit.Microsoft.DependencyInjection")]

Supporting configuration from UserSecrets

This library's TestBedFixture abstract class exposes an instance of IConfigurationBuilder that can be used to support UserSecrets when configuring the test projects:

public IConfigurationBuilder ConfigurationBuilder { get; private set; }

Examples

  • Please follow this link to view a couple of examples on utilizing this library.
  • Digital Silo's unit tests and integration tests are using this library.

One more thing

Do not forget to include the following nuget packages to your Xunit project:

  • Microsoft.Extensions.DependencyInjection
  • Microsoft.Extensions.Configuration
  • Microsoft.Extensions.Options
  • Microsoft.Extensions.Configuration.Binder
  • Microsoft.Extensions.Configuration.FileExtensions
  • Microsoft.Extensions.Configuration.Json
  • Microsoft.Extensions.Logging
  • Microsoft.Extensions.Configuration.EnvironmentVariables

xunit-dependency-injection's People

Contributors

arash-sabet avatar chrisdoernen avatar chrisdoernenelderbrook avatar dependabot-preview[bot] avatar dependabot[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

xunit-dependency-injection's Issues

Adopt .NET 6.0

This has to start with using Microsoft's .NET 6.0's RC NuGet packages.

Test developers must be able to customize loggers

The current implementation of GetServiceProvider in TestBedFixture class needs to be modified to allow adding any logger providers.

The area to change is the following one:

_services.AddLogging(loggingBuilder => loggingBuilder.AddProvider(new OutputLoggerProvider(testOutputHelper)));

Making both Clear() and DisposeAsyncCore() virtual

  • Making both Clear() and DisposeAsyncCore() virtual methods
  • Update the document
  • Update the example project
  • Update the build pipeline's SDK

background

The reason for this change is that the mentioned methods are not usually implemented in the derived classes. We can still give developers the option of overriding them if they require it by making them virtual.

Associate the "optional" boolean flag with each app settings file

The provided app settings files may be optional and depending on the scenarios but the current code does not support it. We need to make the following enhancements:

  • The current implementation of GetConfigurationFiles() method to be marked as [Obsolete] to generate a proper message to direct developers to the new method
  • Introduce a new abstract method called GetTestAppSettings() to return a collection of the following class instead of strings:
public class TestAppSettings
{
   public string? Filename { get; set; }
   public bool IsOptional { get; set; } = false;
}

Some code improvements, and update NuGet specs

  • Remove the obsolete configuration file getter method
  • Update copyright year in the nuget config file
  • Upgrade NuGet packages
  • Reflect test order setup in the readme file
  • Wire up environment variables

Upgrade NuGet packages

The most important NuGet packages to upgrade are Microsoft's. version 3.1.12 to bump up to.

Some code amendments

  • Remove public IConfigurationBuilder ConfigurationBuilder{ get; private set; } but instead add an Action<IConfigurationBuilder> parameter to GetConfigurationRoot(IEnumerable configurationFiles)`
  • Deprecate GetConfigurationFile()
  • Enable nullable
  • Use latest SDK in the build pipelines

Microsoft.Azure.CosmosRepository.IRepository : cannot instatiate implementation type. Help?!

Hi, based on instructions here, I'm getting an error that you can perhaps help with. Any tips appreciated. I'm stuck!

Here is my fixture:

using Microsoft.Azure.CosmosRepository;
using Microsoft.Extensions.Options;
using OeQb.Modules.QbCoreProducts.BusSvc;
using OeQb.Modules.QbCoreProducts.GrpcDomain.Models;
using Xunit.Microsoft.DependencyInjection;

[CollectionDefinition("Dependency Injection")]
public class BaseTestFixture : TestBedFixture
{
    protected override void AddServices(IServiceCollection services, IConfiguration? configuration)
    {
        services.AddTransient<IRepository<OeProject>>();
        services.AddTransient<IOeProjectDbService, OeProjectDbService>()
            .Configure<TestOptions>(config => configuration?.GetSection("TestOptions").Bind(config));
        
    }

    protected override ValueTask DisposeAsyncCore()
        => new();

    protected override IEnumerable<TestAppSettings> GetTestAppSettings()
    {
        yield return new() { Filename = "appsettings.json", IsOptional = false };
    }
    
}


And here is the xunit test (using all newest NuGet versions):

using OeQb.Modules.QbCoreProducts;
using OeQb.Modules.QbCoreProducts.BusSvc;
using OeQb.Modules.QbCoreProducts.GrpcDomain.Requests;
using Shouldly;

//  https://dev.to/arashsabet/xunit-dependency-injection-nm2  
//  https://umplify.github.io/xunit-dependency-injection/

[TestCaseOrderer("Xunit.Microsoft.DependencyInjection.TestsOrder.TestPriorityOrderer", "Xunit.Microsoft.DependencyInjection")]
[CollectionDefinition("Dependency Injection")]
public class OeProjectTests : TestBed<BaseTestFixture>
{
    public OeProjectTests(ITestOutputHelper testOutputHelper, BaseTestFixture fixture)
        : base(testOutputHelper, fixture)
    {
    }
    
    [Fact, TestOrder(1)]
    public async Task SimpleCrud()
    {
        var oeProjectDbSvc = _fixture.GetService<IOeProjectDbService>(_testOutputHelper);
        var oeCoreProdManufDbSvc = _fixture.GetService<IOeCoreProdManufDbService>(_testOutputHelper);
        var seed = new SeedCoreProdDb(oeProjectDbSvc, oeCoreProdManufDbSvc);
        await seed.SeedAll();

        var oeProj1 = await oeProjectDbSvc.Get(new OeProjectRequest() { OeProjectGuid = "0000OEQB-3008-2023-0000-000000000001" });
        var t1 = oeProj1.ProjectName;
        t1.ShouldBe("230904202510--DefaultCategoryName");
        
    }
}

And here is the error:

System.ArgumentException: Cannot instantiate implementation type 'Microsoft.Azure.CosmosRepository.IRepository`1[OeQb.Modules.QbCoreProducts.GrpcDomai...

System.ArgumentException
Cannot instantiate implementation type 'Microsoft.Azure.CosmosRepository.IRepository`1[OeQb.Modules.QbCoreProducts.GrpcDomain.Models.OeProject]' for service type 'Microsoft.Azure.CosmosRepository.IRepository`1[OeQb.Modules.QbCoreProducts.GrpcDomain.Models.OeProject]'.
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.Populate()
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory..ctor(ICollection`1 descriptors)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection`1 serviceDescriptors, ServiceProviderOptions options)
   at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)
   at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services)
   at Xunit.Microsoft.DependencyInjection.Abstracts.TestBedFixture.GetServiceProvider(ITestOutputHelper testOutputHelper)
   at Xunit.Microsoft.DependencyInjection.Abstracts.TestBedFixture.GetService[T](ITestOutputHelper testOutputHelper)
   at OeDriveApi.Tests.Modules.QbCoreProducts.OeProjectTests.SimpleCrud() in 

Sincere thanks!

The logger (OutputLogger) only logs exceptions

The following method should accommodate state in all log levels:

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception, string> formatted)

  • Apply the fix
  • Augment the test project to observe the fix in Calculator test service

Remove DependencyInjectionCollection

Apparently it's not easily feasible to use collection fixtures as explained in #48. So, we'd better focus on class fixture implementation and remove the collection fixture to have concise code.

Add support for loading environment depending appsetings.json file

At the moment, we are able to specify the name of the JSON configuration file, which is typically appsettings.json.
We should be able to load configuration settings from other files like appsettings.dev.json
We usually do this by chaining AddJsonFile(...
If you could expose the builder object, then we could implement this directly in our project.

Expose `IConfigurationBuilder`

This enhancement is required to let developers access the configuration builder object to be able to add their environment variables too.

Add support to add configuration from UserSecrets

Is your feature request related to a problem? Please describe.
There is no way to read the configuration from the UserSecret, I can only write the secret directly in the appsettings in plain text.

Describe the solution you'd like
Adding a configuration from UserSecrets is very simple.
The only problem that needs to be solved is that you need to expose the ConfigurationBuilder in TestFixture, rather than adding the Configuration by filename, I don't know if the test project has restrictions on this aspect of the implementation, but exposing the ConfigurationBuilder is clearly a better choice.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.