.NET 测试、发布与 Docker 部署入门
很多学习 .NET 的同学在前半段会把主要精力放在语言、Web API 和数据库上,但如果你的目标是真正交付一个项目,那么测试和部署一定要补上。
因为一个项目不是“本地能跑起来”就结束了,你还需要回答这些问题:
- 代码改了之后怎么验证没有把旧功能带坏?
- 接口项目如何生成发布产物?
- 怎样把项目放进 Docker 容器里运行?
- 线上配置和本地配置怎么区分?
这篇文章的目标,就是帮你把 .NET 项目从“开发态”推进到“可交付态”。
第一章 为什么测试和部署必须单独学习
只会写功能代码,但不会验证和交付,项目就很难真正落地。
测试解决的是:
- 功能是否正确
- 改动有没有回归风险
部署解决的是:
- 项目怎么运行到目标环境
- 配置如何切换
- 产物如何组织
这两块补齐后,你才算对一个后端项目有完整认知。
第二章 .NET 项目里常见的测试类型
2.1 单元测试
关注单个类、单个方法的行为是否符合预期。
特点:
- 运行快
- 定位问题明确
- 通常不依赖数据库和外部服务
2.2 集成测试
关注多个组件协作是否正常。
例如:
- 控制器 + 服务 + 数据访问一起工作是否正确
- HTTP 接口返回是否符合预期
2.3 端到端验证
更贴近真实运行路径,但成本也更高。
入门阶段不用一开始就把自动化测试铺得特别满,但至少要建立:
- 单元测试意识
- 接口集成验证意识
第三章 使用 xUnit 创建测试项目
3.1 创建测试项目
dotnet new xunit -n Demo.Tests
3.2 把测试项目加入解决方案
dotnet sln add .\Demo.Tests\Demo.Tests.csproj
3.3 添加业务项目引用
dotnet add .\Demo.Tests\Demo.Tests.csproj reference .\src\Demo.Application\Demo.Application.csproj
第四章 第一个单元测试
先有一个简单类:
public class PriceCalculator
{
public decimal Calculate(decimal price, int count)
{
return price * count;
}
}
测试代码:
using Xunit;
public class PriceCalculatorTests
{
[Fact]
public void Calculate_Should_Return_TotalPrice()
{
var calculator = new PriceCalculator();
var result = calculator.Calculate(10m, 3);
Assert.Equal(30m, result);
}
}
4.1 [Fact] 是什么
[Fact] 表示一个普通测试用例。
如果要做参数化测试,可以使用 [Theory]。
第五章 参数化测试
5.1 示例
using Xunit;
public class MathService
{
public int Add(int a, int b) => a + b;
}
public class MathServiceTests
{
[Theory]
[InlineData(1, 2, 3)]
[InlineData(5, 6, 11)]
[InlineData(-1, 1, 0)]
public void Add_Should_Return_Expected_Result(int a, int b, int expected)
{
var service = new MathService();
var result = service.Add(a, b);
Assert.Equal(expected, result);
}
}
5.2 什么时候用 [Theory]
适合:
- 同一逻辑需要多组输入验证
- 参数组合明确
第六章 什么样的代码值得写测试
不是所有代码都值得同样密度的测试。
更值得优先测试的通常是:
- 业务规则
- 金额计算
- 状态流转
- 权限判断
- 参数转换
相对不那么值得写大量样板测试的通常是:
- 纯数据搬运
- 很薄的简单封装
- 主要依赖框架行为的样板代码
测试的目标是降低回归风险,而不是为了“测试数量好看”。
第七章 测试时为什么要重视边界条件
很多 bug 不是出在正常路径,而是边界场景。
例如:
null- 空字符串
- 零值
- 重复数据
- 超大值
- 非法状态
一个业务方法即使正常输入没问题,也不代表边界场景没问题。
第八章 运行测试
命令行执行:
dotnet test
如果只运行指定项目:
dotnet test .\Demo.Tests\Demo.Tests.csproj
这一步最好形成习惯:
- 改完代码先跑测试
- 提交前至少跑一轮关键测试
第九章 Web API 项目如何做基础集成验证
即使不一开始就铺完整集成测试,也建议至少建立接口验证意识。
你可以通过这些方式做验证:
- Swagger 手工验证
- Postman / Apifox 验证
- 自动化 HTTP 集成测试
入门阶段先至少做到:
- 关键接口有可重复验证方式
第十章 .NET 项目如何发布
本地开发通常用:
dotnet run
但部署前通常要先生成发布产物:
dotnet publish -c Release -o .\publish
10.1 publish 和 build 的区别
build:主要是编译publish:用于生成可部署产物
发布目录里通常会包含:
- 应用程序集
- 依赖项
- 配置文件
- 运行所需资源
第十一章 发布前应该检查什么
发布不只是命令跑通,还要关注下面这些问题:
11.1 配置是否准备完整
例如:
- 数据库连接串
- JWT 配置
- Redis 配置
- 上传目录
- 日志目录
11.2 环境变量是否正确
很多线上问题其实是环境没配对,不是代码问题。
11.3 数据库迁移是否已执行
如果项目依赖数据库结构变化,部署前要确认:
- 迁移是否已准备
- 目标环境是否已更新
第十二章 Docker 为什么适合部署 .NET 项目
Docker 的价值不是“更潮”,而是:
- 运行环境更一致
- 部署更标准化
- 更适合服务器和容器环境
你可以把它理解成把应用及其运行环境一起打包。
第十三章 一个基础的 .NET Dockerfile
下面是一个典型的多阶段构建示例:
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish ./src/Demo.Api/Demo.Api.csproj -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
COPY --from=build /app/publish .
EXPOSE 8080
ENTRYPOINT ["dotnet", "Demo.Api.dll"]
13.1 这份 Dockerfile 的思路
第一阶段:
- 使用 SDK 镜像
- 恢复依赖
- 编译并发布
第二阶段:
- 使用更轻量的运行时镜像
- 拷贝发布结果
- 启动应用
这就是典型的多阶段构建思路。
第十四章 本地构建和运行 Docker 镜像
14.1 构建镜像
docker build -t demo-api:1.0 .
14.2 启动容器
docker run -d -p 8080:8080 --name demo-api demo-api:1.0
如果你的应用监听的是容器内 8080 端口,那么外部访问:
http://localhost:8080
第十五章 Docker 环境下配置怎么处理
容器部署时,通常不要把所有环境差异硬写进镜像。
更常见的方式是通过:
- 环境变量
- 挂载配置文件
- 编排工具注入配置
例如设置环境:
docker run -d -p 8080:8080 `
-e ASPNETCORE_ENVIRONMENT=Production `
--name demo-api demo-api:1.0
这能让应用按生产环境配置运行。
第十六章 一个比较实用的发布流程认知
入门阶段可以先把发布流程理解成下面这条线:
写代码
-> 跑测试
-> dotnet publish
-> 生成部署产物
-> Docker 打包
-> 目标环境启动
-> 验证接口
先把这条线跑通,比一开始追求复杂 CI/CD 更重要。
第十七章 初学者常见坑
17.1 不写测试,只靠手点
项目越大,这种方式越不稳定。
17.2 发布前不检查环境配置
最常见的部署事故来源之一。
17.3 Dockerfile 能构建,但应用启动失败
通常要检查:
- 入口 DLL 名字
- 端口暴露
- 环境变量
- 配置文件
17.4 本地能跑,容器里不能跑
常见原因:
- 路径依赖本地环境
- 配置文件缺失
- 端口映射错误
- 外部依赖地址不可达
第十八章 入门阶段建议掌握到什么程度
建议至少做到:
- 会创建并运行
xUnit测试项目 - 能写基础单元测试和参数化测试
- 知道哪些代码更值得优先测试
- 会使用
dotnet publish生成发布产物 - 能看懂基础 Dockerfile
- 能完成一次最基本的容器化运行
延伸阅读
- 如果你还没掌握
.NET工程基础,先阅读.NET CLI 与项目结构 - 如果你想把日志和配置与部署串起来,继续阅读
.NET 配置、选项绑定与日志实践 - 如果你想继续补齐接口层治理,继续阅读
ASP.NET Core 统一异常处理与统一返回
本文小结
测试和部署的意义,在于把项目从“开发环境里的代码”变成“可验证、可交付、可运行的系统”。对 .NET 开发者来说,哪怕不一开始就把自动化和容器体系做得很复杂,也应该先掌握这条最基础的交付主线:
- 写完能测
- 改动能验证
- 程序能发布
- 项目能容器化运行
这一步补齐后,你对整个 .NET 项目的理解才算真正完整。