C# NET内核中的xUnit集成测试给出错误“;已添加具有相同密钥的项";

C# NET内核中的xUnit集成测试给出错误“;已添加具有相同密钥的项";,c#,.net-core,xunit,C#,.net Core,Xunit,我正在使用xUnit在.NET核心应用程序中执行集成测试。我正在关注的是的集成测试部分。它向我介绍了一个TestFixture,它将项目托管在内存服务器中。启动代码使用资产表中的两条记录初始化内存中的数据库。。测试如下所示 我遇到的问题是,当我运行这些测试时,它们会失败并出现错误:已经添加了具有相同密钥的项 我认为这是因为启动程序试图为每个测试在数据库中添加相同的两条记录。如果我再次运行测试,它们就会通过。当我第三次运行它们时,它们失败了,并且像这样交替运行。当我在初始化数据库的启动代码中放置断

我正在使用xUnit在.NET核心应用程序中执行集成测试。我正在关注的是的集成测试部分。它向我介绍了一个TestFixture,它将项目托管在内存服务器中。启动代码使用资产表中的两条记录初始化内存中的数据库。。测试如下所示

我遇到的问题是,当我运行这些测试时,它们会失败并出现错误:已经添加了具有相同密钥的项

我认为这是因为启动程序试图为每个测试在数据库中添加相同的两条记录。如果我再次运行测试,它们就会通过。当我第三次运行它们时,它们失败了,并且像这样交替运行。当我在初始化数据库的启动代码中放置断点时,看起来有两个线程试图执行断点行,因为当我按F10转到下一行时,执行保持在同一行。我如何确认这就是问题所在?如果真是这样的话

public class AssetControllerTests : IClassFixture<TestFixture<ManagementSystemCore.Startup>>
{
    private readonly HttpClient _client;

    public AssetControllerTests(TestFixture<ManagementSystemCore.Startup> fixture)
    {
        _client = fixture.Client;
    }

    private List<Asset> GetTestAssets()
    {
        return Startup.GetTestAssets();
    }

    [Fact]
    public async Task Assets_ReturnsAViewResult_WithListOfAssets()
    {
        // Arrange
        var mockRepo = new Mock<IAssetRepository>();
        mockRepo.Setup(repo => repo.ListAsync()).Returns(Task.FromResult(GetTestAssets()));

        try
        {
            var controller = new AssetController(mockRepo.Object);
            // Act
            var result = await controller.Assets();

            // Assert
            var viewResult = Assert.IsType<ViewResult>(result);
            var model = Assert.IsAssignableFrom<IEnumerable<AssetViewModel>>(viewResult.ViewData.Model);
            Assert.Equal(2, model.Count());
        }
        catch (Exception e)
        {
            var msg = e.Message;
        }
    }

    [Fact]
    public async Task Details_ReturnsViewResult_WithOneAsset()
    {
        // Arrange - get an asset known to exist
        var testAssets = Startup.GetTestAssets();
        var id = testAssets.Select(a => a.Id).First();

        // Act
        var response = await _client.GetAsync("/asset/details/" + id);

        // Assert
        response.EnsureSuccessStatusCode();
        var responseString = await response.Content.ReadAsStringAsync();
        Assert.True(responseString.Contains(testAssets.Select(a => a.SerialNumber).First()));
        Assert.True(responseString.Contains(testAssets.Select(a => a.PartNumber).First()));
    }        

    [Fact]
    public async Task PostAsync_ReturnsBadRequest_GivenNullModel()
    {
        // Arrange & Act
        var mockRepo = new Mock<IAssetRepository>();
        var controller = new AssetController(mockRepo.Object);
        controller.ModelState.AddModelError("error", "some error");

        // Act
        var result = await controller.PostAsync(null);

        // Assert
        Assert.IsType<BadRequestObjectResult>(result);
    }
}
公共类AssetController测试:IClassFixture
{
私有只读HttpClient\u客户端;
公共资产控制器测试(测试夹具)
{
_client=fixture.client;
}
私有列表getestassets()
{
返回Startup.getestassets();
}
[事实]
公共异步任务资产\u返回SaviewResult\u和ListofAssets()
{
//安排
var mockRepo=new Mock();
Setup(repo=>repo.ListAsync()).Returns(Task.FromResult(getestAssets());
尝试
{
var控制器=新资产控制器(mockRepo.Object);
//表演
var result=await controller.Assets();
//断言
var viewResult=Assert.IsType(结果);
var model=Assert.IsAssignableFrom(viewResult.ViewData.model);
Assert.Equal(2,model.Count());
}
捕获(例外e)
{
var msg=e.消息;
}
}
[事实]
公共异步任务详细信息\u返回IEWresult\u WithOneAsset()
{
//安排-获取已知存在的资产
var testAssets=Startup.getestassets();
var id=testAssets.Select(a=>a.id).First();
//表演
var response=wait_client.GetAsync(“/asset/details/”+id);
//断言
response.EnsureSuccessStatusCode();
var responseString=await response.Content.ReadAsStringAsync();
True(responseString.Contains(testAssets.Select(a=>a.SerialNumber.First());
True(responseString.Contains(testAssets.Select(a=>a.PartNumber.First());
}        
[事实]
公共异步任务PostAsync_ReturnsAddressRequest_GivenNullModel()
{
//安排和行动
var mockRepo=new Mock();
var控制器=新资产控制器(mockRepo.Object);
controller.ModelState.AddModelError(“错误”,“某些错误”);
//表演
var result=await controller.PostAsync(null);
//断言
Assert.IsType(结果);
}
}
以下是启动代码:

    public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<AppDbContext>(options => options.UseInMemoryDatabase());

        services.AddMvc();
        services.AddScoped<ITodoRepository, TodoRepository>();
        services.AddScoped<IAssetRepository, AssetRepository>();
        services.AddScoped<IValuesRepository, ValuesRepository>();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();

            var repository = app.ApplicationServices.GetService<IAssetRepository>();

            InitializeDatabaseAsync(repository).Wait();
        }

        app.UseStaticFiles();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Asset}/{action=Index}/{id?}");
        });
    }

    // Load the database with sample assets
    public async Task InitializeDatabaseAsync(IAssetRepository repo)
    {
        var assetList = await repo.ListAsync();
        if (!assetList.Any())
        {
            foreach (var asset in GetTestAssets())
            {
                await repo.AddAsync(asset);
            }
        }
    }

    public static List<Asset> GetTestAssets()
    {
        return new List<Asset>()
        {
            new Asset("5eb6eb60-72d7-4f51-b209-22a4d97604bd", "SN1234", "PN1234"),
            new Asset("4dc5fd59-98d7-3g34-c310-47g5e84987cg", "SN5678", "PN5678")
        };
    }
}
公共类启动
{
公共启动(IHostingEnvironment环境)
{
var builder=new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile(“appsettings.json”,可选:true,重载更改:true)
.AddJsonFile($“appsettings.{env.EnvironmentName}.json”,可选:true)
.AddenEnvironmentVariables();
Configuration=builder.Build();
}
公共IConfigurationRoot配置{get;}
public void配置服务(IServiceCollection服务)
{
services.AddDbContext(options=>options.UseInMemoryDatabase());
services.AddMvc();
services.addScope();
services.addScope();
services.addScope();
}
公共void配置(IApplicationBuilder应用程序、IHostingEnvironment环境、iLogger工厂)
{
loggerFactory.AddConsole(Configuration.GetSection(“Logging”);
loggerFactory.AddDebug();
if(env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
var repository=app.ApplicationServices.GetService();
InitializeDatabaseAsync(repository.Wait();
}
app.UseStaticFiles();
app.UseMvc(路由=>
{
routes.MapRoute(
名称:“默认”,
模板:“{controller=Asset}/{action=Index}/{id?}”);
});
}
//用示例资产加载数据库
公共异步任务初始化DatabaseAsync(IAssetRepository repo)
{
var assetList=await repo.ListAsync();
如果(!assetList.Any())
{
foreach(getestassets()中的var资产)
{
等待回购AddAsync(资产);
}
}
}
公共静态列表getestassets()
{
返回新列表()
{
新资产(“5eb6eb60-72d7-4f51-b209-22a4d97604bd”、“SN1234”、“PN1234”),
新资产(“4dc5fd59-98d7-3g34-c310-47g5e84987cg”、“SN5678”、“PN5678”)
};
}
}

您找到解决方案了吗?@Groppe我学到的是DbContext不是线程安全的。我正在编写一个web应用程序,其中每个请求都会创建一个DbContext的新实例。在xUnit中,多个集成测试类在不同线程上创建多个启动实例。每个线程都创建了相同的数据,因此发生错误是因为每个线程都插入了相同的数据。我相信作为一个临时解决方案,我所做的是使DbContext成为一个单例,以便在后续的web请求中使用相同的实例。当我这么做的时候,它起了作用,但不确定它是否是最好的解决方案。