Je crée des tests Xunit, avec une base de données en mémoire. Les tests s'exécutent correctement s'ils sont exécutés séparément. Cependant, s'ils sont exécutés en parallèle, ils entrent en collision en raison d'un problème de clé primaire dans dbcontext.
Quelle est la meilleure option pour résoudre ce problème?
En essayant de rechercher la documentation de xunit, je viens de commencer à apprendre la programmation .net.
Erreur:
public class ProductAppServiceTest { public TestContext context; public IMapper mapper; public ProductAppServiceTest() { var options = new DbContextOptionsBuilder<TestContext>() .UseInMemoryDatabase(databaseName: "TestDatabase") .Options; context = new TestContext(options); ApplicationServicesMappingProfile applicationServicesMappingProfile = new ApplicationServicesMappingProfile(); var config = new MapperConfiguration(cfg => { cfg.AddProfile(applicationServicesMappingProfile); }); mapper = config.CreateMapper(); } [Fact] public async Task Get_ProductById_Are_Equal() { context.Product.Add(new Product { ProductId = 2, ProductCode = "123", ProductName = "ABC" }); context.SaveChanges(); var ProductRepository = new ProductRepository(context); var ProductAppService = new ProductAppService(ProductRepository, mapper); var ProductDto = await ProductAppService.GetProductById(2); Assert.Equal("123", ProductDto.ProductCode); } [Fact] public async Task Get_ProductPrice_Are_Equal() { context.Product.Add(new Product { ProductId = 2, ProductCode = "123", ProductName = "ABC" }); context.SaveChanges(); var ProductRepository = new ProductRepository(context); var ProductAppService = new ProductAppService(ProductRepository, mapper); var ProductDto = await ProductAppService.GetProductById(2); //Goes into Enum table to validate price is 5 Assert.Equal("5", ProductDto.Price); }
Code:
2 est la clé primaire, utilisée deux fois
"System.ArgumentException : An item with the same key has already been added. Key: 2"
3 Réponses :
Le problème ici est que vous utilisez la même base de données en mémoire pour chaque test. Bien que vous puissiez démolir et créer la base de données pour chaque test, il est généralement plus facile d'utiliser une base de données unique pour chaque test.
Remarque: la raison pour laquelle chaque test utilise la même base de données est que vous utilisez un nom de base de données statique.
XUnit appelle le constructeur de classe de test avant chaque test, vous pouvez donc créer une base de données en mémoire unique pour chaque test en utilisant un guid
pour le nom de la base de données. p >
var options = new DbContextOptionsBuilder<TestContext>() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; context = new TestContext(options);
Les exemples de tests ne s'exécuteront pas correctement en parallèle car ils sont membres de la même collection.
Par défaut, chaque classe de test est une collection de tests unique. Tests dans la même classe de test ne fonctionnera pas en parallèle les unes contre les autres. - https://xunit.net/docs/running-tests-in-parallel a>
Qu'est-ce que TestContext dans le code source, est-ce un singleton quelconque? Si tel est le cas, lors de l'exécution en parallèle puisque les deux tests sont dans la même collection, il sera partagé entre les 2 instances de la classe sur laquelle xUnit tourne. (Même si dans la même collection une nouvelle copie de chaque classe est créée).
Si vous avez besoin de partager au sein d'une collection ou d'un assembly, je vous recommande d'utiliser la fonctionnalité TestFixture intégrée ou d'implémenter un montage d'assemblage.
La configuration et le démontage dans xUnit sont implémentés via Constructor et IDisposable, ou en implémentant IAsyncLifetime sur la classe de test. Remarque: lors de l'exécution en parallèle, DisposeAsync attendra que les tests soient exécutés avant de démarrer chaque élimination.
public class UnitTest : IDisposable, IAsyncLifetime { public UnitTest() { //First } public Task InitializeAsync() { //Second } [Fact] public async Task Test1() { //Third } public Task DisposeAsync() { //Forth } public void Dispose() { //Fifth } }
Ma réponse simple pour éviter les problèmes avec les tâches multi-traitements modifiant les données que d'autres tâches utilisent est de développer une structure de données où les tâches sont chacune passées un index qui leur indique quelle partie de la structure utiliser, lorsque le parallèle les travaux sont tous terminés, les données peuvent ensuite être recombinées dans la structure de données principale d'origine que vous utilisez normalement.
Démontez ou créez un nouveau contexte pour chaque test.
Au lieu de définir
databaseName: "TestDatabase"
, avez-vous essayé de définirdatabaseName: $ "{Guid.NewGuid ()}"
afin que chacune de vos exécutions parallèles utilise une base de données différente pour le test? L'utilisation de la même base de données en mémoire peut expliquer pourquoi ils se marchent les uns sur les autres.