.net core 如何做UnitOfWork+;具有实体框架核心和单元测试的存储库模式
我在单元测试时遇到了一个问题,如果我同时运行多个测试,.net core 如何做UnitOfWork+;具有实体框架核心和单元测试的存储库模式,.net-core,entity-framework-core,xunit,.net Core,Entity Framework Core,Xunit,我在单元测试时遇到了一个问题,如果我同时运行多个测试,DbContext将丢失我在单元测试期间添加的记录,我认为这可能与如何在我的servicecolection中注册服务有关 我有以下设置: i工作内容: public interface IUnitOfWork : IDisposable { IUserRepository Users { get; } int Complete(); } 工作单元 public class
DbContext
将丢失我在单元测试期间添加的记录,我认为这可能与如何在我的servicecolection
中注册服务有关
我有以下设置:
i工作内容:
public interface IUnitOfWork : IDisposable
{
IUserRepository Users { get; }
int Complete();
}
工作单元
public class UnitOfWork : IUnitOfWork
{
private readonly MyDbContext _context;
public IUserRepository Users { get; }
public UnitOfWork(MyDbContext context,
IUserRepository userRepository)
{
_context = context;
Users = userRepository;
}
public void Dispose() => _context.Dispose();
public int Complete() => _context.SaveChanges();
}
用户存储库
public class UserRepository : Repository<User>, IUserRepository
{
public UserRepository(MyDbContext context) : base(context) { }
public MyDbContext MyDbContext => Context as MyDbContext;
public Task<User?> GetUserDetailsAsync(int userID)
{
var user = MyDbContext.Users.Where(user => user.Id == userID)
.Include(user => user.Emails)
.Include(user => user.PhoneNumbers).FirstOrDefault();
return Task.FromResult(user);
}
}
在服务层,我会看到用户很好,但一旦我进入存储库层,我就会失去用户:/我怀疑这与依赖注入AddScoped
vsAddTransient
有关,以及所有这些与运行多个单元测试的工作方式有关
编辑2:
尝试了更多的东西…在每个测试类上使用IClassFixture
,然后确保每个测试类都实现了IDisposable
,在那里我将确保删除上下文数据库;还确保在测试类构造函数中创建了它。因此,我最终出现了以下错误:
无法跟踪实体类型的实例,因为已在跟踪另一个具有{'Id'}相同键值的实例
因此我添加了.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
,但问题仍然存在
这对安装程序来说非常烦人。这就是我现在解决这个问题的方法 摘要:创建了一个新的
ServiceFixture
。此ServiceFixture
作为IClassFixture
应用于BaseTest
类。ServiceFixture
负责初始化服务集合,并允许它在不同的测试类之间重用。BaseTest
的目的是允许在每次测试后处理数据库和进行其他必要的清理。此类的Dispose
方法将分离实体状态并删除数据库
ServiceFixture.cs
public class ServiceFixture
{
public ServiceProvider ServiceProvider { get; }
public ServiceFixture()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddScoped<IUserService, UserService>()
.AddScoped<ICertificationService, CertificationService>()
.AddScoped<IOrganizationService, OrganizationService>()
.AddScoped<ICertificateService, CertificateService>()
.AddScoped<IUnitOfWork, UnitOfWork>()
.AddScoped<IUserRepository, UserRepository>()
.AddScoped<IExercisePostRepository, ExercisePostRepository>()
.AddScoped<IEmailRepository, EmailRepository>()
.AddScoped<IPhoneNumberRepository, PhoneNumberRepository>()
.AddScoped<ICertificationRepository, CertificationRepository>()
.AddScoped<ICertificateRepository, CertificateRepository>()
.AddScoped<IOrganizationRepository, OrganizationRepository>()
.AddTransient<IRestClient, RestClient>()
.AddSingleton<NASMCertificationValidator>()
.AddAutoMapper(typeof(Startup).Assembly)
.AddDbContext<SpotcheckrCoreContext>(options =>
options.UseInMemoryDatabase("Spotcheckr-Core")
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.EnableSensitiveDataLogging());
ServiceProvider = serviceCollection.BuildServiceProvider();
}
}
公共类ServiceFixture
{
公共服务提供程序服务提供程序{get;}
公共服务设施()
{
var servicecolection=新servicecolection();
serviceCollection.AddScoped()
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.addScope()文件
.AddTransient()
.AddSingleton()
.AddAutoMapper(类型(启动).Assembly)
.AddDbContext(选项=>
选项。使用MemoryDatabase(“Spotcheckr Core”)
.UseQueryTrackingBehavior(QueryTrackingBehavior.NotTracking)
.EnableSensitiveDataLogging());
ServiceProvider=serviceCollection.BuildServiceProvider();
}
}
BaseTest.cs
public abstract class BaseTest : IClassFixture<ServiceFixture>, IDisposable
{
protected readonly ServiceProvider ServiceProvider;
protected readonly IUnitOfWork UnitOfWork;
private readonly SpotcheckrCoreContext Context;
public BaseTest(ServiceFixture serviceFixture)
{
ServiceProvider = serviceFixture.ServiceProvider;
Context = serviceFixture.ServiceProvider.GetRequiredService<SpotcheckrCoreContext>();
UnitOfWork = serviceFixture.ServiceProvider.GetRequiredService<IUnitOfWork>();
Context.Database.EnsureCreated();
}
public void Dispose()
{
Context.ChangeTracker.Entries().ToList().ForEach(entry => entry.State = EntityState.Detached);
Context.Database.EnsureDeleted();
}
}
公共抽象类BaseTest:IClassFixture,IDisposable
{
受保护的只读服务提供程序服务提供程序;
受保护的只读IUnitOfWork;
私有只读SpotcheckrCoreContext上下文;
公共基础测试(ServiceFixture ServiceFixture)
{
ServiceProvider=serviceFixture.ServiceProvider;
Context=serviceFixture.ServiceProvider.GetRequiredService();
UnitOfWork=serviceFixture.ServiceProvider.GetRequiredService();
Context.Database.recreated();
}
公共空间处置()
{
Context.ChangeTracker.Entries().ToList().ForEach(entry=>entry.State=EntityState.Detached);
Context.Database.EnsureDeleted();
}
}
UserServiceTests.cs
public class UserServiceTests : BaseTest
{
private readonly IUserService Service;
public UserServiceTests(ServiceFixture serviceFixture) : base(serviceFixture)
{
Service = serviceFixture.ServiceProvider.GetRequiredService<IUserService>();
}
[Fact]
public async void GetUserAsync_WithInvalidUser_ThrowsException()
{
Assert.ThrowsAsync<InvalidOperationException>(() => Service.GetUserAsync(-1));
}
[Fact]
public void CreateUser_UserTypeAthlete_CreatesAthleteUser()
{
var result = Service.CreateUser(Models.UserType.Athlete);
Assert.IsType<Athlete>(result);
}
[Fact]
public void CreateUser_UserTypePersonalTrainer_CreatesPersonalTrainerUser()
{
var result = Service.CreateUser(Models.UserType.PersonalTrainer);
Assert.IsType<PersonalTrainer>(result);
}
}
公共类UserServiceTests:BaseTest
{
专用只读IUserService服务;
公共UserServiceTests(ServiceFixture ServiceFixture):基础(ServiceFixture)
{
Service=serviceFixture.ServiceProvider.GetRequiredService();
}
[事实]
public async void GetUserAsync\u with invalidUser\u ThrowsException()
{
Assert.ThrowsAsync(()=>Service.GetUserAsync(-1));
}
[事实]
public void CreateUser\u用户类型运动员\u CreateSathletueser()
{
var result=Service.CreateUser(Models.UserType.athleter);
Assert.IsType(结果);
}
[事实]
public void CreateUser_用户类型PersonalTrainer_CreatesPersonalTrainerUser()
{
var result=Service.CreateUser(Models.UserType.PersonalTrainer);
Assert.IsType(结果);
}
}
这就是我目前所能解决的问题 摘要:创建了一个新的
ServiceFixture
。此ServiceFixture
作为IClassFixture
应用于BaseTest
类。ServiceFixture
负责初始化服务集合,并允许它在不同的测试类之间重用。BaseTest
的目的是允许在每次测试后处理数据库和进行其他必要的清理。此类的Dispose
方法将分离实体状态并删除数据库
ServiceFixture.cs
public class ServiceFixture
{
public ServiceProvider ServiceProvider { get; }
public ServiceFixture()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddScoped<IUserService, UserService>()
.AddScoped<ICertificationService, CertificationService>()
.AddScoped<IOrganizationService, OrganizationService>()
.AddScoped<ICertificateService, CertificateService>()
.AddScoped<IUnitOfWork, UnitOfWork>()
.AddScoped<IUserRepository, UserRepository>()
.AddScoped<IExercisePostRepository, ExercisePostRepository>()
.AddScoped<IEmailRepository, EmailRepository>()
.AddScoped<IPhoneNumberRepository, PhoneNumberRepository>()
.AddScoped<ICertificationRepository, CertificationRepository>()
.AddScoped<ICertificateRepository, CertificateRepository>()
.AddScoped<IOrganizationRepository, OrganizationRepository>()
.AddTransient<IRestClient, RestClient>()
.AddSingleton<NASMCertificationValidator>()
.AddAutoMapper(typeof(Startup).Assembly)
.AddDbContext<SpotcheckrCoreContext>(options =>
options.UseInMemoryDatabase("Spotcheckr-Core")
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.EnableSensitiveDataLogging());
ServiceProvider = serviceCollection.BuildServiceProvider();
}
}
公共类ServiceFixture
{
公共服务提供程序服务提供程序{get;}
公共服务设施()
{
var servicecolection=新servicecolection();
public abstract class BaseTest : IClassFixture<ServiceFixture>, IDisposable
{
protected readonly ServiceProvider ServiceProvider;
protected readonly IUnitOfWork UnitOfWork;
private readonly SpotcheckrCoreContext Context;
public BaseTest(ServiceFixture serviceFixture)
{
ServiceProvider = serviceFixture.ServiceProvider;
Context = serviceFixture.ServiceProvider.GetRequiredService<SpotcheckrCoreContext>();
UnitOfWork = serviceFixture.ServiceProvider.GetRequiredService<IUnitOfWork>();
Context.Database.EnsureCreated();
}
public void Dispose()
{
Context.ChangeTracker.Entries().ToList().ForEach(entry => entry.State = EntityState.Detached);
Context.Database.EnsureDeleted();
}
}
public class UserServiceTests : BaseTest
{
private readonly IUserService Service;
public UserServiceTests(ServiceFixture serviceFixture) : base(serviceFixture)
{
Service = serviceFixture.ServiceProvider.GetRequiredService<IUserService>();
}
[Fact]
public async void GetUserAsync_WithInvalidUser_ThrowsException()
{
Assert.ThrowsAsync<InvalidOperationException>(() => Service.GetUserAsync(-1));
}
[Fact]
public void CreateUser_UserTypeAthlete_CreatesAthleteUser()
{
var result = Service.CreateUser(Models.UserType.Athlete);
Assert.IsType<Athlete>(result);
}
[Fact]
public void CreateUser_UserTypePersonalTrainer_CreatesPersonalTrainerUser()
{
var result = Service.CreateUser(Models.UserType.PersonalTrainer);
Assert.IsType<PersonalTrainer>(result);
}
}