Inti Bersih: Jalankan Semua Injeksi Ketergantungan di Tes Xunit untuk AppService, Repository, dll

Aug 03 2019

Saya mencoba menerapkan Injeksi Ketergantungan dalam tes Xunit untuk AppService. Tujuan ideal adalah menjalankan program aplikasi asli Startup / konfigurasi, dan menggunakan injeksi ketergantungan yang ada di Startup, alih-alih menginisialisasi ulang semua DI lagi dalam pengujian saya, itulah keseluruhan Sasaran yang dimaksud.

Pembaruan: Jawaban Mohsen sudah dekat. Perlu memperbarui beberapa kesalahan sintaks / persyaratan agar berfungsi.

Untuk beberapa alasan, aplikasi asli berfungsi dan dapat menghubungi Department App Service. Namun, tidak dapat memanggil Xunit. Akhirnya Testserver bekerja menggunakan Startup dan Konfigurasi dari aplikasi asli. Sekarang menerima kesalahan di bawah ini:

Message: The following constructor parameters did not have matching fixture data: IDepartmentAppService departmentAppService

namespace Testing.IntegrationTests
{
    public class DepartmentAppServiceTest
    {
        public DBContext context;
        public IDepartmentAppService departmentAppService;

        public DepartmentAppServiceTest(IDepartmentAppService departmentAppService)
        {
            this.departmentAppService = departmentAppService;
        }

        [Fact]
        public async Task Get_DepartmentById_Are_Equal()
        {
            var options = new DbContextOptionsBuilder<SharedServicesContext>()
                .UseInMemoryDatabase(databaseName: "TestDatabase")
                .Options;
            context = new DBContext(options);

            TestServer _server = new TestServer(new WebHostBuilder()
                .UseContentRoot("C:\\OriginalApplication")
                .UseEnvironment("Development")
                .UseConfiguration(new ConfigurationBuilder()
                    .SetBasePath("C:\\OriginalApplication")
                    .AddJsonFile("appsettings.json")
                    .Build()).UseStartup<Startup>());

            context.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" });
            context.SaveChanges();

            var departmentDto = await departmentAppService.GetDepartmentById(2);

            Assert.Equal("123", departmentDto.DepartmentCode);
        }
    }
}

Saya menerima kesalahan ini:

Message: The following constructor parameters did not have matching fixture data: IDepartmentAppService departmentAppService

Perlu menggunakan injeksi Ketergantungan dalam pengujian seperti aplikasi nyata. Aplikasi asli melakukan ini. Jawaban di bawah ini belum cukup, yang satu menggunakan ejekan yang bukan tujuan saat ini, sedangkan jawaban lainnya menggunakan Pengontrol yang mengabaikan tujuan pertanyaan.

Catatan: IDepartmentAppService memiliki ketergantungan pada IDepartmentRepository yang juga dimasukkan dalam kelas Startup, dan ketergantungan Automapper. Inilah mengapa memanggil seluruh kelas startup.

Sumber Daya yang Baik:

bagaimana untuk menguji aplikasi inti asp.net unit dengan injeksi ketergantungan konstruktor

Injeksi ketergantungan dalam proyek Xunit

Jawaban

14 MohsenEsmailpour Aug 04 2019 at 13:20

Anda mencampur uji unit dengan uji integrasi. TestServeradalah untuk pengujian integrasi dan jika Anda ingin menggunakan kembali Startupkelas untuk menghindari ketergantungan register lagi, Anda harus menggunakan HttpClientdan membuat panggilan HTTP ke pengontrol dan tindakan yang digunakan IDepartmentAppService.

Jika Anda ingin melakukan pengujian unit, Anda perlu menyiapkan DI dan mendaftarkan semua dependensi yang diperlukan untuk diuji IDepartmentAppService.

Menggunakan DI melalui Test Fixture:

public class DependencySetupFixture
{
    public DependencySetupFixture()
    {
         var serviceCollection = new ServiceCollection();
         serviceCollection.AddDbContext<SharedServicesContext>(options => options.UseInMemoryDatabase(databaseName: "TestDatabase"));
         serviceCollection.AddTransient<IDepartmentRepository, DepartmentRepository>();
         serviceCollection.AddTransient<IDepartmentAppService, DepartmentAppService>();

         ServiceProvider = serviceCollection.BuildServiceProvider();
    }

    public ServiceProvider ServiceProvider { get; private set; }
}

public class DepartmentAppServiceTest : IClassFixture<DependencySetupFixture>
{
    private ServiceProvider _serviceProvide;

    public DepartmentAppServiceTest(DependencySetupFixture fixture)
    {
        _serviceProvide = fixture.ServiceProvider;
    }

    [Fact]
    public async Task Get_DepartmentById_Are_Equal()
    {
        using(var scope = _serviceProvider.CreateScope())
        {   
            // Arrange
            var context = scope.ServiceProvider.GetServices<SharedServicesContext>();
            context.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" });
            context.SaveChanges();

            var departmentAppService = scope.ServiceProvider.GetServices<IDepartmentAppService>();

            // Act
            var departmentDto = await departmentAppService.GetDepartmentById(2);

            // Arrange
            Assert.Equal("123", departmentDto.DepartmentCode);           
        }
    }
}

Menggunakan injeksi ketergantungan dengan unit test bukanlah ide yang baik dan Anda harus menghindarinya. Ngomong-ngomong, jika Anda ingin tidak mengulang diri Anda untuk mendaftarkan dependensi, Anda dapat menggabungkan konfigurasi DI Anda di kelas lain dan menggunakan kelas itu di mana pun Anda mau.

Menggunakan DI melalui Startup.cs:

public class IocConfig
{
    public static IServiceCollection Configure(IServiceCollection services, IConfiguration configuration)
    {
         serviceCollection
            .AddDbContext<SomeContext>(options => options.UseSqlServer(configuration["ConnectionString"]));
         serviceCollection.AddScoped<IDepartmentRepository, DepartmentRepository>();
         serviceCollection.AddScoped<IDepartmentAppService, DepartmentAppService>();
         .
         .
         .

         return services;
    }
}

di Startupkelas dan ConfigureServicesmetode cukup gunakan IocConfigkelas:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
         IocConfig.Configure(services, configuration);

         services.AddMvc();
         .
         .
         .

jika Anda tidak ingin menggunakan IocConfigkelas, perubahan ConfigureServicesdi Startupkelas:

public IServiceCollection ConfigureServices(IServiceCollection services)
{
     .
     .
     .
     return services;

dan dalam proyek uji coba IocConfigatau Startupkelas:

public class DependencySetupFixture
{
    public DependencySetupFixture()
    {
          var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", false, true));
         configuration = builder.Build();

         var services = new ServiceCollection();

         // services = IocConfig.Configure(services, configuration)
         // or
         // services = new Startup(configuration).ConfigureServices(services);

         ServiceProvider = services.BuildServiceProvider();
    }

    public ServiceProvider ServiceProvider { get; private set; }
}

dan dalam metode pengujian:

[Fact]
public async Task Get_DepartmentById_Are_Equal()
{
    using (var scope = _serviceProvider.CreateScope())
    {
        // Arrange
        var departmentAppService = scope.ServiceProvider.GetServices<IDepartmentAppService>();

        // Act
        var departmentDto = await departmentAppService.GetDepartmentById(2);

        // Arrange
        Assert.Equal("123", departmentDto.DepartmentCode);
    }
}
12 Noname Aug 07 2019 at 11:48

Gunakan Pabrik Aplikasi Web Khusus dan di ServiceProvider.GetRequiredServicebawahnya, silakan edit dan optimalkan jawabannya

CustomWebApplicationFactory:

public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureAppConfiguration((hostingContext, configurationBuilder) =>
        {
            var type = typeof(TStartup);
            var path = @"C:\\OriginalApplication";

            configurationBuilder.AddJsonFile($"{path}\\appsettings.json", optional: true, reloadOnChange: true);
            configurationBuilder.AddEnvironmentVariables();
        });

        // if you want to override Physical database with in-memory database
        builder.ConfigureServices(services =>
        {
            var serviceProvider = new ServiceCollection()
                .AddEntityFrameworkInMemoryDatabase()
                .BuildServiceProvider();

            services.AddDbContext<ApplicationDBContext>(options =>
            {
                options.UseInMemoryDatabase("DBInMemoryTest");
                options.UseInternalServiceProvider(serviceProvider);
            });
        });
    }
}

Tes integrasi:

public class DepartmentAppServiceTest : IClassFixture<CustomWebApplicationFactory<OriginalApplication.Startup>>
{
    public CustomWebApplicationFactory<OriginalApplication.Startup> _factory;
    public DepartmentAppServiceTest(CustomWebApplicationFactory<OriginalApplication.Startup> factory)
    {
        _factory = factory;
        _factory.CreateClient();
    }

    [Fact]
    public async Task ValidateDepartmentAppService()
    {      
        using (var scope = _factory.Server.Host.Services.CreateScope())
        {
            var departmentAppService = scope.ServiceProvider.GetRequiredService<IDepartmentAppService>();
            var dbtest = scope.ServiceProvider.GetRequiredService<ApplicationDBContext>();
            dbtest.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" });
            dbtest.SaveChanges();
            var departmentDto = await departmentAppService.GetDepartmentById(2);
            Assert.Equal("123", departmentDto.DepartmentCode);
        }
    }
}

Sumber:

https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2

https://fullstackmark.com/post/20/painless-integration-testing-with-aspnet-core-web-api

1 YigitTanriverdi Aug 03 2019 at 01:04

Saat Anda menguji. Anda perlu menggunakan perpustakaan tiruan atau menyuntikkan layanan Anda secara langsung pada kontraktor yaitu.

public DBContext context;
public IDepartmentAppService departmentAppService;

/// Inject DepartmentAppService here
public DepartmentAppServiceTest(DepartmentAppService departmentAppService)
{
    this.departmentAppService = departmentAppService;
}