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

如何利用生产数据库数据为C#单元测试填充测试数据库?

从SQL Server生产库提取数据生成C#测试填充代码的实用方案

嘿,这个需求太接地气了——手动造测试数据真的能把人搞疯!我来分享几个实用方案,帮你自动生成C#代码,在测试运行时快速填充测试库,彻底告别手动录入。

方案一:自定义小工具(灵活可控,适合定制需求)

如果你需要完全掌控数据筛选和脱敏逻辑,自己写个简单的C#工具是最优解:

步骤1:从生产库提取目标数据(注意只读+脱敏)

首先,给你的工具账号只开生产库的只读权限,绝对不能让它有修改生产数据的权限。然后写SQL查询筛选有代表性的测试数据——不用全量导入几千条,测试只需要覆盖不同场景的样本(比如正常数据、边界值、异常案例):

-- 示例:提取50条活跃客户数据,同时脱敏邮箱
SELECT TOP 50 
    Id,
    Name,
    REPLACE(Email, '@yourcompany.com', '@test.com') AS Email,
    PhoneNumber,
    IsActive
FROM Customers
WHERE IsActive = 1

步骤2:读取数据并生成C#插入代码

用Dapper或EF Core读取生产库的数据,然后直接生成C#代码片段,或者在测试初始化时直接插入测试库。比如用Dapper的示例:

using Dapper;
using System.Data.SqlClient;
using System.Text;

// 生产库连接字符串(只读权限)
var prodConnStr = "Server=prod-server;Database=ProdDB;User Id=readonly-user;Password=xxx;";
// 测试库实体类命名空间
var namespaceStr = "YourTestProject.Models";

var sb = new StringBuilder();
sb.AppendLine($"using {namespaceStr};");
sb.AppendLine("using Microsoft.EntityFrameworkCore;");
sb.AppendLine();
sb.AppendLine("public static void SeedTestData(YourDbContext context)");
sb.AppendLine("{");

using (var conn = new SqlConnection(prodConnStr))
{
    conn.Open();
    var customers = conn.Query<Customer>(@"
        SELECT TOP 50 Id, Name, REPLACE(Email, '@yourcompany.com', '@test.com') AS Email 
        FROM Customers WHERE IsActive = 1");

    foreach (var customer in customers)
    {
        sb.AppendLine($"    context.Customers.Add(new Customer");
        sb.AppendLine("    {");
        sb.AppendLine($"        Id = {customer.Id},");
        sb.AppendLine($"        Name = \"{customer.Name.Replace("\"", "\\\"")}\",");
        sb.AppendLine($"        Email = \"{customer.Email}\"");
        sb.AppendLine("    });");
    }
}

sb.AppendLine("    context.SaveChanges();");
sb.AppendLine("}");

// 将生成的代码写入文件
File.WriteAllText("SeedData.cs", sb.ToString());

运行这个工具后,你会得到一个现成的SeedTestData方法,直接在测试项目里调用就行。

方案二:用T4模板自动生成代码

如果需要频繁更新测试数据,T4模板是个好选择——它可以自动帮你生成代码,每次只需要更新查询语句即可:

创建一个.tt文件,写入以下内容(记得替换连接字符串和实体类信息):

<#@ template language="C#" #>
<#@ assembly name="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.Text" #>

using YourTestProject.Models;
using Microsoft.EntityFrameworkCore;

public static void SeedTestData(YourDbContext context)
{
    <#
    var prodConnStr = "Server=prod-server;Database=ProdDB;User Id=readonly-user;Password=xxx;";
    using(var conn = new SqlConnection(prodConnStr))
    {
        conn.Open();
        var cmd = new SqlCommand(@"
            SELECT TOP 50 Id, Name, REPLACE(Email, '@yourcompany.com', '@test.com') AS Email 
            FROM Customers WHERE IsActive = 1", conn);
        var reader = cmd.ExecuteReader();
        
        while(reader.Read())
        {
            var id = reader.GetInt32(0);
            var name = reader.GetString(1).Replace("\"", "\\\"");
            var email = reader.GetString(2);
    #>
    context.Customers.Add(new Customer
    {
        Id = <#= id #>,
        Name = "<#= name #>",
        Email = "<#= email #>"
    });
    <#
        }
        reader.Close();
    }
    #>
    context.SaveChanges();
}

保存后,Visual Studio会自动生成对应的C#代码,每次修改T4模板里的SQL,就能快速更新种子数据代码。

方案三:测试初始化时直接插入数据(无需生成代码)

如果不想生成静态代码,也可以在测试初始化时直接从生产库读取脱敏数据,插入测试库:

public class CustomerServiceTests : IDisposable
{
    private readonly YourDbContext _testContext;

    public CustomerServiceTests()
    {
        // 初始化测试库(可以用内存数据库或本地SQL Server实例)
        var options = new DbContextOptionsBuilder<YourDbContext>()
            .UseSqlServer("Server=localhost;Database=TestDB;Trusted_Connection=True;")
            .Options;
        _testContext = new YourDbContext(options);
        _testContext.Database.EnsureCreated();

        // 从生产库读取并插入测试数据
        SeedFromProduction(_testContext);
    }

    private void SeedFromProduction(YourDbContext context)
    {
        var prodConnStr = "Server=prod-server;Database=ProdDB;User Id=readonly-user;Password=xxx;";
        using (var prodConn = new SqlConnection(prodConnStr))
        {
            prodConn.Open();
            var customers = prodConn.Query<Customer>(@"
                SELECT TOP 50 Id, Name, REPLACE(Email, '@yourcompany.com', '@test.com') AS Email 
                FROM Customers WHERE IsActive = 1");
            
            context.Customers.AddRange(customers);
            context.SaveChanges();
        }
    }

    [Fact]
    public void GetActiveCustomers_ReturnsExpectedCount()
    {
        var service = new CustomerService(_testContext);
        var result = service.GetActiveCustomers();
        Assert.Equal(50, result.Count());
    }

    public void Dispose()
    {
        // 测试后清理数据,保证测试独立性
        _testContext.Customers.RemoveRange(_testContext.Customers);
        _testContext.SaveChanges();
        _testContext.Dispose();
    }
}

关键注意事项

  • 脱敏!脱敏!脱敏!:生产数据中的敏感信息(手机号、邮箱、密码、身份证号等)必须替换成测试用假数据,绝对不能直接用真实敏感数据。
  • 控制数据量:测试库不需要几千条数据,只需要覆盖核心场景的样本即可,不然会拖慢测试速度。
  • 生产库权限隔离:给访问生产库的账号只开只读权限,避免误操作。
  • 测试数据隔离:每次测试前最好重置测试库(比如用事务回滚、清空表后重新插入),保证测试结果不受之前测试的影响。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:08:26