C#代码测试、单元测试、性能测试、压力测试

C#代码测试全指南

一、测试基础概念

1. 测试类型分类

测试类型目的范围执行频率
​单元测试​验证单个代码单元功能类/方法级别开发阶段持续执行
​集成测试​验证模块间交互模块/服务级别集成阶段执行
​功能测试​验证业务功能端到端级别手动/自动化执行
​性能测试​评估系统性能指标系统级别发布前执行
​压力测试​测试系统极限承载能力系统级别发布前执行
​回归测试​验证变更未引入新缺陷全量/增量每次变更后执行

2. 测试金字塔

                ┌─────────────┐
                │  手动测试   │
                └─────────────┘
                       ▲
                       │
                       ▼
            ┌───────────────────┐
            │   端到端测试      │
            └───────────────────┘
                       ▲
                       │
                       ▼
        ┌───────────────────────┐
        │     集成测试          │
        └───────────────────────┘
                       ▲
                       │
                       ▼
    ┌─────────────────────────┐
    │       单元测试          │
    └─────────────────────────┘

二、单元测试实现

1. 基础单元测试框架

​使用xUnit框架示例​​:


using Xunit;

public class CalculatorTests
{
    [Fact]
    public void Add_TwoNumbers_ReturnsSum()
    {
        // Arrange
        var calculator = new Calculator();
        
        // Act
        var result = calculator.Add(2, 3);
        
        // Assert
        Assert.Equal(5, result);
    }
    
    [Theory]
    [InlineData(1, 1, 2)]
    [InlineData(0, 0, 0)]
    [InlineData(-1, 1, 0)]
    public void Add_VariousInputs_ReturnsCorrectSum(int a, int b, int expected)
    {
        var calculator = new Calculator();
        Assert.Equal(expected, calculator.Add(a, b));
    }
}

​测试类设计原则​​:

  • 测试类命名:[被测类名]Tests
  • 测试方法命名:[动作]_[条件]_[预期结果]
  • 每个测试方法包含Arrange-Act-Assert三部分

2. Mock与依赖注入

​使用Moq框架模拟依赖​​:


using Moq;
using Xunit;

public class OrderServiceTests
{
    [Fact]
    public void PlaceOrder_ValidOrder_CallsRepository()
    {
        // Arrange
        var mockRepo = new Mock<IOrderRepository>();
        var service = new OrderService(mockRepo.Object);
        
        var order = new Order { Id = 1 };
        
        // Act
        service.PlaceOrder(order);
        
        // Assert
        mockRepo.Verify(repo => repo.Save(order), Times.Once);
    }
}

​依赖注入测试模式​​:


public interface IDataService
{
    string GetData();
}

public class RealDataService : IDataService
{
    public string GetData() => "Real Data";
}

public class MockDataService : IDataService
{
    public string GetData() => "Mock Data";
}

public class Consumer
{
    private readonly IDataService _dataService;
    
    public Consumer(IDataService dataService)
    {
        _dataService = dataService;
    }
    
    public string Process() => _dataService.GetData();
}

// 测试中使用
var mockService = new MockDataService();
var consumer = new Consumer(mockService);
Assert.Equal("Mock Data", consumer.Process());

3. 测试覆盖率

​使用Visual Studio测试资源管理器​​:

  1. 右键项目 → "分析" → "计算代码覆盖率"
  2. 查看未覆盖的代码区域
  3. 补充测试用例

​提高覆盖率技巧​​:

  • 测试正常路径
  • 测试边界条件
  • 测试异常情况
  • 测试私有方法(通过公共接口间接测试)

三、性能测试实现

1. 基础性能测试

​使用Stopwatch测量执行时间​​:


using System.Diagnostics;

public class PerformanceTests
{
    [Fact]
    public void ProcessData_PerformanceTest()
    {
        // Arrange
        var data = Enumerable.Range(1, 10000).ToList();
        var processor = new DataProcessor();
        
        // Act & Assert
        var sw = Stopwatch.StartNew();
        processor.Process(data);
        sw.Stop();
        
        Assert.True(sw.ElapsedMilliseconds < 100); // 100ms内完成
    }
}

2. 压力测试框架

​使用Apache JMeter或k6​​:

  1. 创建测试计划
  2. 配置虚拟用户数
  3. 设置请求速率
  4. 监控响应时间/错误率

​C#压力测试示例(使用HttpClient)​​:


using System.Net.Http;
using System.Threading.Tasks;

public class ApiStressTests
{
    private readonly HttpClient _client = new HttpClient();
    
    [Fact]
    public async Task ApiEndpoint_HandleConcurrentRequests()
    {
        // Arrange
        int concurrentRequests = 100;
        var tasks = new List<Task>();
        
        // Act
        for (int i = 0; i < concurrentRequests; i++)
        {
            tasks.Add(MakeRequest());
        }
        
        await Task.WhenAll(tasks);
        
        // Assert
        // 检查是否有失败请求
    }
    
    private async Task MakeRequest()
    {
        var response = await _client.GetAsync("https://api.example.com/data");
        response.EnsureSuccessStatusCode();
    }
}

3. 性能计数器监控

​使用System.Diagnostics.PerformanceCounter​​:


using System.Diagnostics;

public class PerformanceMonitor
{
    public void MonitorCpuUsage()
    {
        var counter = new PerformanceCounter(
            categoryName: "Processor",
            counterName: "% Processor Time",
            instanceName: "_Total");
        
        float cpuUsage = counter.NextValue();
        System.Threading.Thread.Sleep(1000); // 需要等待1秒
        cpuUsage = counter.NextValue();
        
        Console.WriteLine($"CPU使用率: {cpuUsage}%");
    }
}

四、测试最佳实践

1. 测试驱动开发(TDD)

​TDD循环​​:

  1. 编写失败的测试
  2. 编写最小实现通过测试
  3. 重构代码

​示例​​:


// 第一步:编写测试
[Fact]
public void Calculator_ShouldAddNumbers()
{
    var calc = new Calculator();
    Assert.Equal(4, calc.Add(2, 2)); // 测试失败
}

// 第二步:实现功能
public class Calculator
{
    public int Add(int a, int b) => a + b; // 测试通过
}

// 第三步:重构(如有必要)

2. 测试数据管理

​使用测试数据生成器​​:


public static class TestData
{
    public static IEnumerable<object[]> AdditionData()
    {
        yield return new object[] { 1, 1, 2 };
        yield return new object[] { 0, 0, 0 };
        yield return new object[] { -1, 1, 0 };
        yield return new object[] { int.MaxValue, 0, int.MaxValue };
    }
    
    [Theory]
    [MemberData(nameof(AdditionData))]
    public void Add_ValidInputs_ReturnsCorrectSum(int a, int b, int expected)
    {
        var calc = new Calculator();
        Assert.Equal(expected, calc.Add(a, b));
    }
}

3. 测试隔离

​避免测试间依赖​​:


// 不好的做法 - 测试间共享状态
public class BadTests
{
    private static int _sharedValue;
    
    [Fact]
    public void Test1()
    {
        _sharedValue = 1;
        Assert.Equal(1, _sharedValue);
    }
    
    [Fact]
    public void Test2()
    {
        Assert.Equal(1, _sharedValue); // 可能失败
    }
}

// 好的做法 - 每个测试独立
public class GoodTests
{
    [Fact]
    public void Test1()
    {
        var value = 1;
        Assert.Equal(1, value);
    }
    
    [Fact]
    public void Test2()
    {
        var value = 1;
        Assert.Equal(1, value);
    }
}

4. 测试异常

​验证异常抛出​​:


[Fact]
public void Divide_ByZero_ThrowsException()
{
    // Arrange
    var calc = new Calculator();
    
    // Act & Assert
    Assert.Throws<DivideByZeroException>(() => calc.Divide(1, 0));
    
    // 或者更精确的断言
    var ex = Assert.Throws<DivideByZeroException>(() => calc.Divide(1, 0));
    Assert.Equal("Attempted to divide by zero.", ex.Message);
}

五、高级测试技术

1. 模糊测试(Fuzz Testing)

​随机输入测试​​:


[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
[InlineData("valid")]
public void ProcessInput_VariousInputs(string input)
{
    var processor = new InputProcessor();
    
    // 根据输入类型验证不同行为
    if (string.IsNullOrWhiteSpace(input))
    {
        Assert.Throws<ArgumentException>(() => processor.Process(input));
    }
    else
    {
        var result = processor.Process(input);
        Assert.NotNull(result);
    }
}

2. 属性测试(Property-Based Testing)

​使用FsCheck(需NuGet安装)​​:


using FsCheck;
using FsCheck.Xunit;

[Property]
public bool Add_IsCommutative(int a, int b)
{
    return new Calculator().Add(a, b) == new Calculator().Add(b, a);
}

[Property]
public bool Add_IsAssociative(int a, int b, int c)
{
    return new Calculator().Add(new Calculator().Add(a, b), c) 
           == new Calculator().Add(a, new Calculator().Add(b, c));
}

3. 并发测试

​验证线程安全​​:


[Fact]
public void ConcurrentAccess_ThreadSafe()
{
    // Arrange
    var collection = new ConcurrentDictionary<int, string>();
    int threadCount = 10;
    int iterations = 1000;
    
    // Act
    Parallel.For(0, threadCount, i =>
    {
        for (int j = 0; j < iterations; j++)
        {
            collection.TryAdd(j, $"Thread{i}");
        }
    });
    
    // Assert
    Assert.Equal(iterations, collection.Count);
}

六、持续集成中的测试

1. 测试自动化流程

​典型CI流程​​:

  1. 代码提交 → 触发构建
  2. 编译 → 运行单元测试
  3. 测试通过 → 部署到测试环境
  4. 运行集成/性能测试
  5. 结果报告

​GitHub Actions示例​​:


name: CI

on: [push, pull_request]

jobs:
  build:
    runs-on: windows-latest
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Setup .NET
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: '5.0.x'
    
    - name: Build
      run: dotnet build --configuration Release
      
    - name: Unit Tests
      run: dotnet test --configuration Release --no-build --verbosity normal
      
    - name: Code Coverage
      run: dotnet test --configuration Release /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura

2. 测试结果分析

​SonarQube集成​​:

  1. 安装SonarQube服务器
  2. 配置.NET项目分析
  3. 查看代码质量报告

​示例SonarQube配置​​:


- name: SonarQube Analysis
  uses: sonarsource/sonarcloud-github-action@master
  with:
    args: >
      -Dsonar.projectKey=my_project
      -Dsonar.organization=my_org
      -Dsonar.host.url=https://sonarcloud.io
      -Dsonar.login=$SONAR_TOKEN
      -Dsonar.cs.opencover.reportsPaths=coverage.opencover.xml

七、测试性能优化

1. 测试并行执行

​xUnit并行设置​​:


<!-- 在xunit.runner.json中 -->
{
  "parallelizeAssembly": true,
  "parallelizeTestCollections": true,
  "maxParallelThreads": 4
}

2. 测试选择性执行

​按标记运行测试​​:


[Fact]
[Trait("Category", "Fast")]
public void FastTest() { /* ... */ }

[Fact]
[Trait("Category", "Slow")]
public void SlowTest() { /* ... */ }

// 命令行运行特定类别
dotnet test --filter Category=Fast

3. 测试数据隔离

​使用内存数据库​​:


// 使用SQLite内存数据库进行测试
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();

var options = new DbContextOptionsBuilder<AppDbContext>()
    .UseSqlite(connection)
    .Options;

using (var context = new AppDbContext(options))
{
    // 测试代码
}

八、常见测试问题解决

1. 测试不稳定(Flaky Tests)

​解决方案​​:

  1. 添加等待条件(非固定等待)
  2. 隔离测试环境
  3. 添加重试机制

​示例重试机制​​:


public static class RetryHelper
{
    public static T ExecuteWithRetry<T>(Func<T> action, int retryCount = 3)
    {
        var exceptions = new List<Exception>();
        
        for (int i = 0; i < retryCount; i++)
        {
            try
            {
                return action();
            }
            catch (Exception ex)
            {
                exceptions.Add(ex);
                Thread.Sleep(500 * (i + 1));
            }
        }
        
        throw new AggregateException(exceptions);
    }
}

// 使用
var result = RetryHelper.ExecuteWithRetry(() => 
{
    // 可能不稳定的操作
});

2. 测试速度慢

​优化策略​​:

  1. 减少不必要的测试数据
  2. 使用轻量级模拟对象
  3. 并行执行测试
  4. 缓存昂贵资源

​示例优化​​:


// 原始慢测试
[Fact]
public void SlowTest()
{
    var data = LoadLargeDatasetFromDisk(); // 耗时操作
    // ...
}

// 优化后
private static readonly List<Data> CachedData = LoadOnce();

private static List<Data> LoadOnce()
{
    // 使用Lazy或静态构造函数确保只加载一次
    return LoadLargeDatasetFromDisk();
}

[Fact]
public void OptimizedTest()
{
    var data = CachedData; // 快速访问
    // ...
}

九、测试工具推荐

1. 单元测试框架

  • ​xUnit​​:现代、轻量、并行支持好
  • ​NUnit​​:功能丰富、社区支持好
  • ​MSTest​​:微软官方框架,与VS集成好

2. Mock框架

  • ​Moq​​:最流行的Mock框架
  • ​NSubstitute​​:语法简洁
  • ​FakeItEasy​​:简单易用

3. 性能测试工具

  • ​Apache JMeter​​:功能全面
  • ​k6​​:开发者友好
  • ​Gatling​​:高性能

4. 代码覆盖率

  • ​Coverlet​​:与xUnit/NUnit集成
  • ​OpenCover​​:功能全面
  • ​dotCover​​:JetBrains出品

十、测试策略制定

1. 测试金字塔调整

                ┌─────────────┐
                │  手动探索测试 │
                └─────────────┘
                       ▲
                       │
                       ▼
            ┌───────────────────┐
            │     端到端测试      │
            └───────────────────┘
                       ▲
                       │
                       ▼
        ┌───────────────────────┐
        │     集成测试          │
        └───────────────────────┘
                       ▲
                       │
                       ▼
    ┌─────────────────────────┐
    │       单元测试          │
    └─────────────────────────┘

​调整建议​​:

  • 基础设施代码:增加集成测试比例
  • UI代码:适当增加端到端测试
  • 核心业务逻辑:保持高单元测试覆盖率

2. 测试环境配置

​多环境测试策略​​:

  1. 开发环境:快速反馈的单元测试
  2. 集成环境:API契约测试
  3. 预发布环境:端到端测试
  4. 生产环境:监控+金丝雀测试

3. 测试数据管理

​策略选择​​:

  • ​生成式​​:运行时生成(适合简单场景)
  • ​复制式​​:从生产环境脱敏(适合复杂场景)
  • ​混合式​​:关键数据真实+辅助数据生成

​工具推荐​​:

  • ​Bogus​​:C#数据生成库
  • ​Faker.Net​​:类似Faker.js的实现
  • ​SQL Data Generator​​:Redgate工具(商业)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

code_shenbing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值