W skrócie: tak warto testować CRUDa, zdecydowanie nie warto używać EF in memory, na którym nie działa sporo rzeczy które działają na normalnej bazie danych, SQLite in memory jest zdecydowanie mniej problematyczne.
I preferuje takie podejście:
- integracyjne, testujemy wystawiony endpoint: host w pamięci + baza w pamięci -> testujemy szczęśliwą ścieżkę + czy uwierzytelnianie działa
- jednostkowo z bazą w pamięci, testujemy serwisy aplikacyjne czy też kontrolery czy gdzie tam orkiestrujemy nasz use casy na wszystkie wyjątkowe ścieżki
Uwagi:
- mają powyższe testy można całe api napisać ani razu go nie odpalając, odchodzi potrzeba używana narzędzi pokroju postmana
- używanie bazy w pamięci jest niesamowicie wygodne
- ważne żeby nie współdzielić dbcontextu pomiędzy którymś A z Arrange, Act, Asser
Przykłady:
[TestClass]
public class Questions_HappyPath : BaseFixture
{
private const string EndpointName = "Questions";
private HttpClient client;
[TestInitialize]
public void TestInitialize()
{
factory = new ApiFactory(ApiFactory.DatabaseType.SQLiteInMemory);
client = factory.CreateClient(ValidToken);
SeedDatabase(factory);
}
[TestMethod]
[DataRow(1)]
[DataRow(2)]
[DataRow(3)]
public async Task ReadQuestionWithAnswers(long questionId)
{
var response = await client.GetAsync($"{EndpointName}/{questionId}/");
var actualQuestion = response.GetContent<QuestionDTO>().Value;
var context = factory.GetContext<TestCreationDbContext>();
var expectedQuestion = context.Questions.Include(x => x.Answers).FirstOrDefault(x => x.QuestionId == questionId);
AssertExt.AreEquivalent(expectedQuestion, actualQuestion);
}
[TestMethod]
public async Task CreateQuestionWithAnswers()
{
var command = new CreateQuestion()
{
CatalogId = 1,
Content = "Who is your dady?",
Answers = new List<CreateAnswer>()
{
new CreateAnswer() { Content = "Adam", IsCorrect = true },
new CreateAnswer() { Content = "Peter", IsCorrect = false}
}
};
var response = await client.PostAsync(EndpointName, command);
var createdId = response.GetContent<long>().Value;
var context = factory.GetContext<TestCreationDbContext>();
var actualQuestion = context.Questions.Include(x => x.Answers).FirstOrDefault(x => x.QuestionId == createdId);
AssertExt.AreEquivalent(command, actualQuestion);
}
}
[TestClass]
public class QuestionsServiceTests : BaseFixture
{
private TestCreationDbContext testCreationDbContext;
private QuestionsService serviceUnderTest;
private protected override DatabaseType GetDatabaseType()
{
return DatabaseType.SQLiteInMemory;
}
[TestInitialize]
public void TestInitialize()
{
testCreationDbContext = CreateTestCreationDbContext();
var uow = TestUtils.CreateTestCreationUoW(testCreationDbContext);
serviceUnderTest = new QuestionsService(new QuestionReader(CreateReadOnlyTestCreationDbContext()), uow);
}
[TestCleanup]
public void TestCleanup()
{
testCreationDbContext.Dispose();
}
[TestMethod]
[DataRow(ValidQuestionId, ResultStatus.Ok)]
[DataRow(NotExisitngQuestionId, ResultStatus.NotFound)]
[DataRow(DeletedQuestionId, ResultStatus.NotFound)]
[DataRow(OtherOwnerQuestionId, ResultStatus.Unauthorized)]
public void ReadQuestionWithAnswers(long questionId, ResultStatus expectedResult)
{
Result result = serviceUnderTest.ReadQuestionWithAnswers(OwnerId, questionId);
Assert.AreEqual(expectedResult, result.Status);
}
[TestMethod]
[DataRow(ValidQuestionsCatalogId, ResultStatus.Ok)]
[DataRow(DeletedQuestionsCatalogId, ResultStatus.Error)]
[DataRow(ValidTestsCatalogId, ResultStatus.Error)]
[DataRow(NotExisitngQuestionsCatalogId, ResultStatus.Error)]
[DataRow(OtherOwnerQuestionsCatalogId, ResultStatus.Unauthorized)]
public void CreateQuestionWithAnswers(long catalogId, ResultStatus expectedResult)
{
var command = new CreateQuestion()
{
Content = "Dani Carvajal",
CatalogId = catalogId,
};
Result result = serviceUnderTest.CreateQuestionWithAnswers(OwnerId, command);
Assert.AreEqual(expectedResult, result.Status);
}
}