Внедрение зависимостей в ядро ​​Asp.net Интеграционное тестирование

testing dependency-injection integration-testing entity-framework-core asp.net-core-1.0

1386 просмотра

1 ответ

840 Репутация автора

Я успешно внедрил зависимости, используя Moq в моем проекте модульного тестирования. Но для интеграционного теста мне бы хотелось взаимодействовать с базой данных. Поэтому я не могу подделать репозитории / зависимости. У меня возникли проблемы, как добиться этого в отдельной библиотеке классов, представленной для интеграционного тестирования.

Я хотел бы сделать что-то вроде этого (данные должны поступать из базы данных):

public class CountryServiceIntegrationTest
{

    private ICountryService countryService;

    public CountryServiceIntegrationTest(ICountryService _countryService)
    {
        countryService = _countryService;                     
    }

    #endregion


    [Fact]
    public void Should_Return_ListOf_Countries()
    {
        //Act
        var myList = countryService.GetList("A");
        //Assert
        Assert.True(myList.Count > 0);
    }        
}

Мой CountryService Class:

public class CountryService : ICountryService
{
    // Note: Have to use Core.Domain.Country because of the namespace has Quantum.Service.Country
    protected IRepository<Core.Domain.Country> _countryRepository;
    protected IRepository<Core.Domain.State> _stateRepository;
    protected IRepository<Core.Domain.City> _cityRepository;

    public CountryService(IRepository<Core.Domain.Country> countryRepository, IRepository<Core.Domain.State> stateRepository, IRepository<Core.Domain.City> cityRepository)
    {
        _countryRepository = countryRepository;
        _stateRepository = stateRepository;
        _cityRepository = cityRepository;
    }


    public IList<CountryViewModel> GetList(string name)
    {
        var query = _countryRepository.Table.AsQueryable();
        if (string.IsNullOrEmpty(name) == false)
        {
            query = query.Where(i => i.CountryName.StartsWith(name));
        }
        return query.Select(i => new CountryViewModel()
        {
            CountryCode = i.CountryCode,
            CountryName = i.CountryName,
            Currency = i.Currency,
            CurrencyName = i.CurrencyName,
            CurrencySymbol = i.CurrencySymbol,
            TelephoneCountryCode = i.TelephoneCountryCode,
            UnitOfMeasure = i.UnitOfMeasure
        }).ToList();
    } }

У меня есть отдельный проект библиотеки классов IOC, где прописаны зависимости. Затем он регистрируется в классе Startup.cs. Поскольку класс Startup.cs не вызывается во время тестов, зависимости не внедряются. Так как я могу решить эту проблему?

------ ОБНОВЛЕНО В соответствии с руководящими указаниями в официальной документации здесь -----

Хорошо, теперь: я пошел по этой ссылке и сделал согласно ей. Мне кажется, что был вызван класс запуска, который также вызывает ConfigureDependency.RegisterDependencies (..).

Тестовый класс:

    public CountryServiceIntegrationTest()
    {
        _server = new TestServer(new WebHostBuilder()
            .UseStartup<Startup>());
        _client = _server.CreateClient();            
    }

    [Fact]
    public async Task ReturnHelloWorld()
    {
        //Act
        var response = await _client.GetAsync("/home/Test");
        response.EnsureSuccessStatusCode();

        var responseString = await response.Content.ReadAsStringAsync();

        //Assert
        Assert.Equal("test", responseString);
    }

Startup.ConfigureServices ():

    public IConfigurationRoot Configuration { get; }

    //gets called in the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {      

        //services.AddSingleton<ILogUserActivityService, LogUserActivityService>();
        services.AddSingleton<ActivityLog>();
        // Add framework services.
        services.AddMvc();
        // Register Database Connection String
        var connectionSetting = new ConnectionSetting(Configuration["Data:ConnectionStrings:DefaultConnection"]);
        services.AddSingleton<IConnectionSetting>(connectionSetting);
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
        // Fill other dependencies
        var configureDependency = new ConfigureDependency();
        configureDependency.RegisterDependencies(services, connectionSetting);          

    }

ConfigureDependency.RegisterDependency (..):

 public class ConfigureDependency
{
    public IDatabaseFactory DatabaseFactory { get; set; }
    public void RegisterDependencies(IServiceCollection services, IConnectionSetting connectionSetting)
    {

        services.AddDbContext<QuantumDbContext>(options => options.UseSqlServer(connectionSetting.Get()));

        services.AddTransient<IDatabaseFactory, DatabaseFactory>();
        services.AddTransient<IDbContext, TestDbContext>();
        services.AddTransient<IDbContext, QuantumDbContext>();

        ..................................................................
        ...........service n repositories  are registered here..............

  }
}

Но теперь, что происходит, я получаю эту ошибку: введите описание изображения здесь

Поскольку вызывается Startup.cs, который затем вызывает класс ConfigureDependency, не означает ли это, что параметры (services, connectionSetting) должны передаваться автоматически. Это (ConfigureDependency.RegisterDependencies (..)), где я получаю ошибку.

Автор: Avi-B Источник Размещён: 19.07.2016 08:52

Ответы (1)


1 плюс

7471 Репутация автора

Это ArgumentNullExceptionв useSqlServerметоде:

Кажется, что connectionSetting.Get()возвращается null.

В следующем коде

var connectionSetting = new ConnectionSetting(Configuration["Data:ConnectionStrings:DefaultConnection"]);
services.AddSingleton<IConnectionSetting>(connectionSetting);

Это предполагает, что ConnectionSettingреализует интерфейс, IConnectionSetting так почему бы вам не использовать экземпляр напрямую, а не вызывать Get()его?

Как ниже:

services.AddDbContext<QuantumDbContext>(options => options.UseSqlServer(connectionSetting))

Дополнительные замечания:
Это действительно зависит от того, что вы подразумеваете под интеграционным тестом . Это может относиться к:

  • модульные тесты более высокого уровня (в отличие от юнит-тестов, ограниченных одним классом, такие интеграционные тесты будут тестировать интеграцию между различными классами).
  • Интеграционные тесты на уровне пространства имен (тестирование одного или нескольких открытых интерфейсов из заданного пространства имен без проверки внутренних классов).
  • тесты интеграции на уровне сборки (то же самое, что и пространство имен, но с областью сборки)
  • тесты интеграции черного ящика (тестирование всего программного обеспечения на его взаимодействие с точки зрения внешних систем). Документация по интеграционному тестированию ASP.NET относится к такого рода тестам.

Часто лучше пройти несколько слоев, прежде чем пытаться выполнить тестирование Большого взрыва ... но это вопрос компромисса между временем и качеством .

Чтобы сделать / не так высокоуровневые интеграционные тесты можно / легко написать:

  • Вы не должны использовать одну и ту же среду базы данных между вашими тестами и рабочим кодом (поэтому не одну и ту же строку подключения).

  • Вы не должны использовать Startup, так как он предназначен для имитации всего сайта на тестовом сервере.

  • Регистрация и разрешение служб должны быть разделены на несколько согласованных конкретных классов, чтобы упростить интеграционные тесты для определенных частей.

Автор: Fab Размещён: 13.09.2016 01:03
Вопросы из категории :
32x32