Я реализую NUnit Integration Tests
конечных точек REST нашего контроллера в .NET Web API 2
проекте.Мы используем подход Entity Framework
code-first from database
для создания наших контроллеров и моделей.
У меня настроен проект myProjectIntegrationTests
с установленным NUnit
и ссылкой на myProject
.
Из моего исследования следующим шагом является создание сценария TestSetup
, который в каждом тесте создает Integration Tests Database
в LocalDb
.Это позволяет нам проводить интеграционное тестирование наших вызовов API, не затрагивая мастер dev database
.
Этот скрипт TestSetup
должен выполнять несколько вещей при каждом запуске теста:
- проверить, еслисоединение в настоящее время открыто в Integration Test Db
- если это так, закройте его.
- проверьте, существует ли существующее Integration Test db
- если да, разорвите его.
- запустите миграцию с моего мастера dev database
наmy Integration Test Db
чтобы загрузить его с реальными данными.
- создать новый экземпляр Integration Test Db
- выполнить интеграционные тесты ...
- закрыть Integration Test Db
соединения
- разорвать Integration Test Db
Создание этого TestSetup
класса - вот что доставляет мне неприятности.Я нашел учебные пособия о том, как сделать это для .NET MVC, .NET Core, а также Entity Framework - но, похоже, ни один из них не использует просто .Net Web API
, поэтому некоторые библиотеки и код, на которые делается ссылка, не работают длямне. Может ли кто-нибудь предоставить пример скрипта или ссылку на учебник, который может работать в .NET Web API 2
?
Вот пример того, как кто-то делает это для EntityFramework, используя, я полагаю, .Net Core
.Это часть замечательного учебника PluralSight по интеграционному тестированию в Entity Framework Майкла Перри, найденного здесь :
using Globalmantics.DAL;
using Globalmantics.DAL.Migrations;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Data.E ntity;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Reflection;
namespace Globalmantics.IntegrationTests
{
[SetUpFixture]
public class TestSetup
{
[OneTimeSetUp]
public void SetUpDatabase()
{
DestroyDatabase();
CreateDatabase();
}
[OneTimeTearDown]
public void TearDownDatabase()
{
DestroyDatabase();
}
private static void CreateDatabase()
{
ExecuteSqlCommand(Master, $@"
CREATE DATABASE [Globalmantics]
ON (NAME = 'Globalmantics',
FILENAME = '{Filename}')");
var migration = new MigrateDatabaseToLatestVersion<
GlobalmanticsContext, GlobalmanticsConfiguration>();
migration.InitializeDatabase(new GlobalmanticsContext());
}
private static void DestroyDatabase()
{
var fileNames = ExecuteSqlQuery(Master, @"
SELECT [physical_name] FROM [sys].[master_files]
WHERE [database_id] = DB_ID('Globalmantics')",
row => (string)row["physical_name"]);
if (fileNames.Any())
{
ExecuteSqlCommand(Master, @"
ALTER DATABASE [Globalmantics] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
EXEC sp_detach_db 'Globalmantics'");
fileNames.ForEach(File.Delete);
}
}
private static void ExecuteSqlCommand(
SqlConnectionStringBuilder connectionStringBuilder,
string commandText)
{
using (var connection = new SqlConnection(connectionStringBuilder.ConnectionString))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = commandText;
command.ExecuteNonQuery();
}
}
}
private static List<T> ExecuteSqlQuery<T>(
SqlConnectionStringBuilder connectionStringBuilder,
string queryText,
Func<SqlDataReader, T> read)
{
var result = new List<T>();
using (var connection = new SqlConnection(connectionStringBuilder.ConnectionString))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = queryText;
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
result.Add(read(reader));
}
}
}
}
return result;
}
private static SqlConnectionStringBuilder Master =>
new SqlConnectionStringBuilder
{
DataSource = @"(LocalDB)\MSSQLLocalDB",
InitialCatalog = "master",
IntegratedSecurity = true
};
private static string Filename => Path.Combine(
Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location),
"Globalmantics.mdf");
}
}
А вот более старый пример того, как кто-то делает это для .Net MVC
:
using System;
using System.Data.Entity;
using NUnit.Framework;
namespace BankingSite.IntegrationTests
{
[SetUpFixture]
public class TestFixtureLifecycle
{
public TestFixtureLifecycle()
{
EnsureDataDirectoryConnectionStringPlaceholderIsSet();
EnsureNoExistingDatabaseFiles();
}
private static void EnsureDataDirectoryConnectionStringPlaceholderIsSet()
{
// When not running inside MVC application the |DataDirectory| placeholder
// is null in a connection string, e.g AttachDBFilename=|DataDirectory|\TestBankingSiteDb.mdf
AppDomain.CurrentDomain.SetData("DataDirectory", NUnit.Framework.TestContext.CurrentContext.TestDirectory);
}
private void EnsureNoExistingDatabaseFiles()
{
const string connectionString = "name=DefaultConnection";
if (Database.Exists(connectionString))
{
Database.Delete(connectionString);
}
}
}
}