#c# #unit-testing #entity-framework-core #xunit #asp.net-core-5.0
#c# #модульное тестирование #сущность-структура-ядро #xunit #asp.net-core-5.0
Вопрос:
Я работаю над ASP.NET Проект Core 5, в котором я провожу модульное тестирование базы данных SQLite. Когда я запускаю тесты по отдельности, все тесты проходят, но когда я запускаю все тесты одновременно, я получаю эту ошибку:
Система.Исключение InvalidOperationException : экземпляр объекта типа ‘Contact’ не может быть отслежен, поскольку другой экземпляр с тем же значением ключа для {‘Id’} уже отслеживается. При подключении существующих объектов убедитесь, что подключен только один экземпляр объекта с заданным значением ключа.
Трассировка стека: [xUnit.net 00:00:03.06] в Microsoft.EntityFrameworkCore.Отслеживание изменений.Внутренний.Карта
1.ThrowIdentityConflict(InternalEntityEntry entry) [xUnit.net 00:00:03.06] at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap
идентификаторов 1.Добавить (ключ TKey, запись InternalEntityEntry, логическое значение updateDuplicate) [xUnit.net 00:00:03.06] в Microsoft.EntityFrameworkCore.Отслеживание изменений.Внутренний.IdentityMap1.Add(TKey key, InternalEntityEntry entry) [xUnit.net 00:00:03.06] at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap
1.Добавить (запись InternalEntityEntry) [xUnit.net 00:00:03.06] в Microsoft.EntityFrameworkCore.Отслеживание изменений.Internal.StateManager.Отслеживание запуска (запись InternalEntityEntry) [xUnit.net 00:00:03.06] в Microsoft.EntityFrameworkCore.Отслеживание изменений.Внутренний.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, логические AcceptChanges, логические modifyProperties) [xUnit.net 00:00:03.06] в Microsoft.EntityFrameworkCore.Отслеживание изменений.Внутренний.InternalEntityEntry.SetEntityState(EntityState EntityState, логические AcceptChanges, логические modifyProperties, обнуляемый1 forceStateWhenUnknownKey) [xUnit.net 00:00:03.06] at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode
1 узел) [xUnit.net 00:00:03.06] в Microsoft.EntityFrameworkCore.Отслеживание изменений.Внутренний.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode1 node, Func
2 handleNode) [xUnit.net 00:00:03.06] в Microsoft.EntityFrameworkCore.Отслеживание изменений.Внутренний.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode1 node, Func
2 handleNode) [xUnit.net 00:00:03.06] в Microsoft.EntityFrameworkCore.Отслеживание изменений.Внутренний.EntityEntryGraphIterator.TraverseGraph[TState](EntityEntryGraphNode1 node, Func
2 handleNode) [xUnit.net 00:00:03.06] в Microsoft.EntityFrameworkCore.Отслеживание изменений.Внутренний.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, логическое значение forceStateWhenUnknownKey) [xUnit.net 00:00:03.06] в Microsoft.EntityFrameworkCore.Внутренний.InternalDbSet1.SetEntityState(InternalEntityEntry entry, EntityState entityState) [xUnit.net 00:00:03.06] at Microsoft.EntityFrameworkCore.Internal.InternalDbSet
1.Добавить (объект TEntity)
В каждом модульном тестировании я использую оператор ‘using’, поэтому контекст базы данных должен быть удален, как я понимаю.
У меня есть класс TestBase:
using System;
using Project.Data;
using Microsoft.EntityFrameworkCore;
namespace Project.Connections.Test
{
public class TestBase
{
private bool useSqlite;
public DataContext GetDbContext()
{
var builder = new DbContextOptionsBuilder<DataContext>();
if (useSqlite)
{
// Use Sqlite DB.
builder.UseSqlite("DataSource=:memory:", x => { });
}
else
{
// Use In-Memory DB.
builder.UseInMemoryDatabase(Guid.NewGuid().ToString());
}
var dbContext = new DataContext(builder.Options);
if (useSqlite)
{
// SQLite needs to open connection to the DB.
// Not required for in-memory-database and MS SQL.
dbContext.Database.OpenConnection();
}
dbContext.Database.EnsureCreated();
TestData.Seed(dbContext);
dbContext.SaveChanges();
return dbContext;
}
public void UseSqlite()
{
useSqlite = true;
}
}
}
One of the objects that I’m seeding is causing the problem. Note that the customer of the project contains a contacts and addresses list, and the project object itself has one of the customer’s contact and address:
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Project.Data.Customers;
using Project.Data.Projects;
using Microsoft.EntityFrameworkCore;
namespace Project.Data
{
public class TestData
{
public static void Seed(DataContext context)
{
// Other data...
CreateProjects(context);
}
private static void CreateProjects(DataContext context)
{
var project1 = new Project()
{
Name = "Lorem",
ProjectNumber = "2020/10/0001",
DeadLine = System.DateTime.Now.AddDays(10),
Customer = microsoft,
State = ProjectState.Accepted,
Contact = microsoft.Contacts.First(),
DeliveryAddress = microsoft.DeliveryAddresses.First(),
EndSize = new Size()
{
Height = 300,
Width = 2000,
},
};
context.Projects.Add(project1);
context.SaveChanges();
// Other project objects...
}
private static Customer microsoft = new Customer
{
IsCompany = true,
CompanyName = "Microsoft",
Contacts = new List<Contact>()
{
new Contact
{
FirstName = "Bill",
LastName = "Gates",
PhoneNumber = " 16506815000",
Email = "bill.gates@microsoft.com",
Other = "CEO, in case of emergency :)",
}
},
DeliveryAddresses = new List<Address>()
{
new Address
{
Country = "USA",
ZipCode = "WS 98052-6399",
City = "Redmond",
Street = "One Microsoft Way",
Other = "Microsoft HQ"
},
new Address
{
Country = "Norway",
ZipCode = "0194",
City = "Oslo",
Street = "Dronning Eufemias gate 71",
Other = "Microsoft Norway"
}
}
};
}
}
И вот как выглядит мой модульный тест:
using System.Linq;
using System.Threading.Tasks;
using Project.Connections.Services.Projects;
using Project.Data.Customers;
using Project.Data.Projects;
using Project.Data.Projects.Dtos;
using Xunit;
namespace Project.Connections.Test
{
public class ProjectServiceTests : TestBase
{
[Fact]
public async Task GetAll_ShouldReturn()
{
UseSqlite();
using (var context = GetDbContext())
{
var ProjectService = new ProjectService(context);
var result = await ProjectService.GetAll();
Assert.Equal(3, result.Count()); // I made 3 dummy projects originally
}
}
// Other tests...
За последние пару дней я просмотрел много статей, но так и не смог понять, в чем причина проблемы.
Комментарии:
1. Вы нашли решение? У меня такая же проблема
2. @CedricArnould На самом деле нет, я просто создавал новые объекты вместо ссылок, поскольку это были фиктивные данные. Однако я узнал, как EF может настроить, чтобы не отслеживать объект. Например.: вместо
microsoft.Contacts.First();
попыткиmicrosoft.Contacts.AsNoTacking().First();
, возможно, это может помочь 🙂3. Дело в том, что я хочу удалить элемент из базы данных, поэтому мне нужно сохранить отслеживание (насколько я понимаю). Я решаю, выполнив новое действие в API, чтобы удалить его, не совсем то, что я хотел, но это работает.