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

如何为建造者模式编写XUnit单元测试?含IDatabase模拟方案

针对UserBuilder的XUnit单元测试方案

1. 模拟第三方IDatabase接口

用Moq框架创建IDatabase的模拟实例,无需依赖真实数据库。先安装Moq NuGet包。

2. 测试SetName方法

推荐方案:调整User类暴露Name属性

如果有权限修改User类,把私有name字段改为公共属性,这样测试更简洁且符合设计原则:

public class User
{
    public string Name { get; set; }
    public IDataBase db { get; set; }
    // 其他成员
}

测试代码:

public class UserBuilderTest
{
    [Fact]
    public void SetName_ConvertsInputToLowercase()
    {
        // Arrange
        var mockDb = new Mock<IDataBase>();
        var builder = new UserBuilder(mockDb.Object);
        var expected = "john";

        // Act
        builder.SetName("John");
        // 模拟数据库不存在该名称,确保Build能通过验证
        mockDb.Setup(db => db.contains(expected)).Returns(false);
        var user = builder.Build();

        // Assert
        Assert.Equal(expected, user.Name);
    }
}

备选方案:用反射访问私有字段(无法修改User类时)

如果不能改动User类,通过反射读取私有字段:

[Fact]
public void SetName_ConvertsInputToLowercase_WithReflection()
{
    // Arrange
    var mockDb = new Mock<IDataBase>();
    var builder = new UserBuilder(mockDb.Object);
    var expected = "john";

    // Act
    builder.SetName("John");

    // 反射获取UserBuilder的私有_user字段
    var userField = typeof(UserBuilder).GetField("_user", BindingFlags.NonPublic | BindingFlags.Instance);
    var user = (User)userField.GetValue(builder);
    // 反射获取User的私有name字段
    var nameField = typeof(User).GetField("name", BindingFlags.NonPublic | BindingFlags.Instance);
    var actual = (string)nameField.GetValue(user);

    // Assert
    Assert.Equal(expected, actual);
}

3. 测试Validate方法

模拟IDatabase的contains方法,验证不同场景下的异常抛出逻辑:

[Fact]
public void Validate_ThrowsException_WhenNameIsNull()
{
    var mockDb = new Mock<IDataBase>();
    var builder = new UserBuilder(mockDb.Object);

    Assert.Throws<InvalidDataException>(() => builder.Validate());
}

[Fact]
public void Validate_ThrowsException_WhenNameExistsInDatabase()
{
    var testName = "john";
    var mockDb = new Mock<IDataBase>();
    mockDb.Setup(db => db.contains(testName)).Returns(true);

    var builder = new UserBuilder(mockDb.Object);
    builder.SetName(testName);

    Assert.Throws<InvalidDataException>(() => builder.Validate());
}

[Fact]
public void Validate_Passes_WhenNameIsValid()
{
    var testName = "john";
    var mockDb = new Mock<IDataBase>();
    mockDb.Setup(db => db.contains(testName)).Returns(false);

    var builder = new UserBuilder(mockDb.Object);
    builder.SetName(testName);

    // 不抛出异常则测试通过
    builder.Validate();
}

4. 测试Build方法

验证Validate被调用、persist执行,且返回正确的User对象:

[Fact]
public void Build_ReturnsValidUser_AndExecutesPersist()
{
    var testName = "john";
    var mockDb = new Mock<IDataBase>();
    mockDb.Setup(db => db.contains(testName)).Returns(false);

    var builder = new UserBuilder(mockDb.Object);
    builder.SetName(testName);

    var user = builder.Build();

    Assert.Equal(testName, user.Name); // 假设已暴露Name属性
    mockDb.Verify(db => db.persist(), Times.Once); // 验证persist被调用一次
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 03:51:41