Я следую официальной документации MS для интеграционного тестирования .Net Core (https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.1).
Мне удалось выполнить первую часть интеграционного теста, где я не переопределял класс запускаприложения, которое я тестирую (т. е. я использовал фабрику веб-приложений, которая не переопределяла никакие сервисы).
Я хочу переопределить настройку базы данных, чтобы использовать базу данных в памяти для интеграционного теста. Проблема Iя сталкиваюсь с тем, что конфигурация продолжает пытаться использовать сервер SQL для services.AddHangfire()
.
Как переопределить только выше определенного элемента в моем интеграционном тесте? Я хочу переопределить только настройку AddHangfire
ине services.AddScoped<ISendEmail, SendEmail>().
Любая помощь приветствуется.
Тестовый класс с фабрикой пользовательских веб-приложений
public class HomeControllerShouldCustomFactory : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<Startup> _factory;
public HomeControllerShouldCustomFactory(CustomWebApplicationFactory<Startup> factory)
{
_factory = factory;
_client = factory.CreateClient();
}
[Fact]
public async Task IndexRendersCorrectTitle()
{
var response = await _client.GetAsync("/Home/Index");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("Send Email", responseString);
}
}
Фабрика пользовательских веб-приложений
public class CustomWebApplicationFactory<TStartup>: WebApplicationFactory<SendGridExample.Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
var inMemory = GlobalConfiguration.Configuration.UseMemoryStorage();
services.AddHangfire(x => x.UseStorage(inMemory));
// Build the service provider.
var sp = services.BuildServiceProvider();
});
}
}
Мой файл startup.cs, который я тестирую в своем приложении
public IConfiguration Configuration {get;} public IHostingОкружающая среда Окружающая среда}
public void ConfigureServices(IServiceCollection services)
{
services.AddHangfire(x => x.UseSqlServerStorage(Configuration.GetConnectionString("ASP_NetPractice")));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddScoped<ISendEmail, SendEmail>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseHangfireServer();
app.UseHangfireDashboard();
RecurringJob.AddOrUpdate<ISendEmail>((email) => email.SendReminder(), Cron.Daily);
app.UseMvc();
Обновление
Я не вижу этой проблемы в моем другом примере проекта, где я использую только платформу сущностей.У меня есть простое приложение с контекстом базы данных приложения, которое использует сервер SQL.В моем тестовом классе я перезаписываю его базой данных в памяти, и все работает.Я затрудняюсь понять, почему это будет работать в моем примере приложения, но не будет работать в моем основном приложении.Это как-то связано с тем, как работает HangFire?
В моем тестовом приложении (пример кода ниже) я могу удалить свою базу данных sql, запустить мой тест, и тест проходит, потому что контекст БД приложения не просматриваетсядля экземпляра сервера SQL, но использует базу данных в памяти.В моем приложении служба HangFire продолжает пытаться использовать базу данных sql-сервера (если я удаляю базу данных и пытаюсь использовать базу данных в памяти для теста - происходит сбой, потому что не удается найти экземпляр, к которому она пытается подключиться),Почему такая большая разница в том, как работают два проекта, когда для обоих используется одинаковый путь?
Я пробежал отладчик для моего интеграционного теста, который вызывает метод index на контроллере home выше (используяCustomWebApplicationFactory).Когда я инициализирую тестовый сервер, он проходит через мой класс запуска, который вызывает ниже в ConfigureServices:
services.AddHangfire(x => x.UseSqlServerStorage(Configuration.GetConnectionString("ASP_NetPractice")));
После этого метод Configure пытается вызвать оператор ниже:
app.UseHangfireServer();
В этот момент тест не пройден, поскольку он не может найти БД.БД размещается на Azure, поэтому я пытаюсь заменить ее сервером в памяти для некоторых интеграционных тестов.Подход, который я использую, неверен?
Мой пример приложения, где он работает
Контекст БД приложения в моем примере приложения
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public virtual DbSet<Message> Messages { get; set; }
public async Task<List<Message>> GetMessagesAsync()
{
return await Messages
.OrderBy(message => message.Text)
.AsNoTracking()
.ToListAsync();
}
public void Initialize()
{
Messages.AddRange(GetSeedingMessages());
SaveChanges();
}
public static List<Message> GetSeedingMessages()
{
return new List<Message>()
{
new Message(){ Text = "You're standing on my scarf." },
new Message(){ Text = "Would you like a jelly baby?" },
new Message(){ Text = "To the rational mind, nothing is inexplicable; only unexplained." }
};
}
}
Startup.cs в моем примере приложения
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
CustomWebApplicationFactory - в моем проекте модульного тестирования
public class CustomWebApplicationFactory<TStartup>
: WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Add a database context (ApplicationDbContext) using an in-memory
// database for testing.
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
options.UseInternalServiceProvider(serviceProvider);
});
// Build the service provider.
var sp = services.BuildServiceProvider();
});
}
}
Мой модультест в моем модульном тестовом проекте
public class UnitTest1 : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<Startup> _factory;
public UnitTest1(CustomWebApplicationFactory<Startup> factory)
{
_factory = factory;
_client = factory.CreateClient();
}
[Fact]
public async System.Threading.Tasks.Task Test1Async()
{
var response = await _client.GetAsync("/");
//response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("Home", responseString);
}
Обновление 2
Я думаю, что нашел альтернативу попытке переопределить всю мою конфигурацию в моем классе интеграционных тестов.Поскольку переопределить HangFire намного сложнее, чем ApplicationDBContext, я предложил следующий подход:
Startup.cs
if (Environment.IsDevelopment())
{
var inMemory = GlobalConfiguration.Configuration.UseMemoryStorage();
services.AddHangfire(x => x.UseStorage(inMemory));
}
else
{
services.AddHangfire(x => x.UseSqlServerStorage(Configuration["DBConnection"]));
}
Затем в моем CustomWebApplicationBuilderЯ переопределяю тип среды для тестирования:
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<SendGridExample.Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Development"); //change to Production for alternate test
builder.ConfigureServices(services =>
{
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
});
}
}
При таком подходе мне не нужно беспокоиться о необходимости выполнения дополнительной логики для выполнения проверки Hangfire на активную БД.Это работает, но я не на 100% убежден, что это лучший подход, так как я внедряю ветвление в своем классе запуска производства.