You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单元测试执行时出现“Key has already been added”错误的排查求助

问题分析与解决方案

错误原因

  1. InMemory数据库数据冲突:你使用了固定名称的InMemory数据库"iSMS",每个测试方法执行前都会运行Initialize方法,第二次执行时尝试添加主键Adrid=1的重复实体,导致主键冲突抛出异常。
  2. 错误Mock测试目标类:你Mock了AddressService本身,但单元测试的核心是验证这个类的真实业务逻辑,应该直接实例化该类,而非通过Mock对象调用方法(Mock对象默认返回默认值,无法覆盖真实逻辑的验证需求)。

修复步骤

1. 为每个测试创建独立的InMemory数据库

将数据库名称改为动态生成的唯一值(比如Guid),确保每个测试的数据库完全隔离,避免数据交叉污染:

var options = new DbContextOptionsBuilder<iSMSContext>()
    .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
    .Options;

2. 直接实例化AddressService而非Mock

删除_addressMock字段,改为直接创建AddressService的真实实例:

private AddressService _addressService;

// 在Initialize方法中替换Mock为真实实例
_addressService = new AddressService(_mockCache.Object, _context, _mockLogger.Object);

3. 修改测试方法中的调用对象

将测试方法中_addressMock.Object的调用替换为_addressService。

完整修复后的测试代码

public class AddressServiceTest
{
    private Mock<IAppCache> _mockCache;
    private Mock<ILogger<AddressService>> _mockLogger;
    private iSMSContext _context;
    private AddressService _addressService;

    [TestInitialize]
    public void Initialize()
    {
        _mockCache = new Mock<IAppCache>();
        _mockLogger = new Mock<ILogger<AddressService>>();

        // 生成唯一名称的InMemory数据库
        var options = new DbContextOptionsBuilder<iSMSContext>()
            .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
            .Options;

        _context = new iSMSContext(options);
        _context.Adresses.Add(new Adresse
        {
            Adrid = 1,
            Projid = 1,
            Lfd = 1,
            Sortid = 1,
            Aquelle = 1,
            Istfirma = 1,
            Titel = "TestTitle",
            Anrede = "TestAnrede",
            Vorname = "TestVorname",
            Strhnr = "TestStreet 123",
            Plz = "12345",
            Ort = "TestCity",
            Land = "TestLand",
        });
        _context.SaveChanges();

        // 直接实例化待测试的服务类
        _addressService = new AddressService(_mockCache.Object, _context, _mockLogger.Object);
    }

    [TestMethod]
    public void GetStreetAndHouseNumber_Test()
    {
        string street = "TestStreet";
        string houseNumber = "123";
        int lfd = 1;
        int projid = 1;

        var address = _addressService.GetStreetAndHouseNumber(street, houseNumber, lfd, projid);
        Assert.IsNotNull(address);
        Assert.AreEqual($"{street} {houseNumber}", address);
    }
    
    [TestMethod]
    public void GetPostalCode_Test()
    {
        string postalCode = "12345";
        int lfd = 1;
        int projid = 1;

        var result = _addressService.GetPostalCode(postalCode, lfd, projid);
        Assert.IsNotNull(result);
        Assert.AreEqual(result, postalCode);
    }
}

额外建议

  • 可以在TestCleanup方法中添加_context.Dispose();,测试完成后销毁DbContext实例,避免不必要的资源占用。
  • 如果AddressService依赖的其他服务(比如IAppCache)需要特定行为,可以通过_mockCache.Setup(...)配置模拟逻辑,确保测试环境完全可控。

内容的提问来源于stack exchange,提问作者Sascha Manns

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 12:02:16