GitHub Actions中使用serilog.json配置文件时出现错误
本地执行dotnet test时所有测试正常,但推送代码后GitHub Actions中有2个测试失败,报错找不到Serilog配置文件。
错误信息
Error Message:
System.IO.FileNotFoundException : The configuration file 'AnimeViewedDiary.Infrastructure\serilog.json' was not found and is not optional. The expected physical path was '/home/runner/work/animeViewedDiary/animeViewedDiary/src/AnimeViewedDiary.Infrastructure\serilog.json'.
Stack Trace:
at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load(Boolean reload)
at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
项目结构与相关代码
- serilog.json路径:
src/AnimeViewDiary.Infrastucture/serilog.json - 测试文件路径:
tests/AnimeViewDiary.Data.UnitTests/ - UnitOfWork文件路径:
src/AnimeViewDiary.Data/Repositories/Based/UnitOfWork.cs
失败的测试代码
[Fact] public void SaveChanges_SuccessSaveChanges_SaveChanges() { // Arrange Mock<DiaryApplicationDbContext> mockApplicationDbContext = UnitOfWorkConfiguration.CreateMockApplicationDbContext(); Mock<DiaryIdentityDbContext> mockIdentityDbContext = UnitOfWorkConfiguration.CreateMockIdentityDbContext(); IUnitOfWork unitOfWork = new UnitOfWork(mockIdentityDbContext.Object, mockApplicationDbContext.Object); // Act unitOfWork.SaveChanges(); // Assert mockApplicationDbContext.Verify(options => options.SaveChanges(), Times.Once); mockIdentityDbContext.Verify(options => options.SaveChanges(), Times.Once); }
UnitOfWork构造函数代码
public UnitOfWork(DiaryIdentityDbContext identityDbContext, DiaryApplicationDbContext applicationDbContext) { string currentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory()).Name; const string projectName = "AnimeViewedDiary.Infrastructure"; const string projectApiName = "AnimeViewedDiary.WebAPI"; const string serilogFileName = "serilog.json"; const string srcDirectoryName = "src"; string srcFolderPath; if (currentDirectory == projectApiName) srcFolderPath = Directory.GetParent(currentDirectory)?.Parent?.ToString()!; else { string rootDirectory = new DirectoryInfo(currentDirectory).Parent?.Parent?.Parent?.Parent?.Parent?.Parent ?.ToString()!; srcFolderPath = Path.Combine(rootDirectory, srcDirectoryName); } string fullPath = Path.Combine(srcFolderPath, projectName) + "\\"; string pathToFile = fullPath + serilogFileName; IConfigurationRoot configuration = new ConfigurationBuilder() .AddJsonFile(pathToFile, optional: false, reloadOnChange: true) .Build(); ILogger logger = new LoggerConfiguration() .ReadFrom.Configuration(configuration) .CreateLogger(); _identityDbContext = identityDbContext; _applicationDbContext = applicationDbContext; UserRepository = new UserRepository(_identityDbContext, logger); }
GitHub Actions工作流配置
name: Build and Testing .NET AnimeViewedDiary Project on: push: branches: - "*" - "*/*" - "**" pull_request: branches: - "*" - "*/*" - "**" jobs: build: runs-on: ubuntu-latest strategy: matrix: dotnet-version: ["7.0.x"] steps: - uses: actions/checkout@v3 - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} uses: actions/setup-dotnet@v3 with: dotnet-version: ${{ matrix.dotnet-version }} - name: Restore/Install dependencies run: dotnet restore - name: Build project run: dotnet build --configuration Release --no-restore - name: Unit tests run: dotnet test
问题原因与解决方法
问题原因
- 路径分隔符不兼容:代码硬编码了Windows风格的
\\分隔符,而GitHub Actions使用Ubuntu系统(Linux),默认用/分隔符,导致生成的路径混合两种格式,系统无法识别。 - 目录层级依赖不可靠:通过多次调用
Parent获取根目录,这种方式依赖当前执行目录的层级,本地与GitHub Actions的执行目录结构不一致,导致路径计算错误。 - 测试耦合外部依赖:单元测试中直接读取真实配置文件,既耦合环境,又可能因为配置文件未复制到测试输出目录导致找不到。
解决方法
方法一:修复路径处理逻辑(临时方案)
- 避免硬编码路径分隔符,改用
Path.Combine自动适配系统:
将原代码中:
修改为:string fullPath = Path.Combine(srcFolderPath, projectName) + "\\"; string pathToFile = fullPath + serilogFileName;string pathToFile = Path.Combine(srcFolderPath, projectName, serilogFileName); - 优化目录获取逻辑,避免多层
Parent调用,比如通过查找解决方案文件定位根目录,或使用相对路径。
方法二:重构代码解耦日志依赖(推荐方案)
单元测试应隔离外部依赖,通过依赖注入传入Mock日志对象:
- 修改
UnitOfWork构造函数,直接接收ILogger参数:public UnitOfWork(DiaryIdentityDbContext identityDbContext, DiaryApplicationDbContext applicationDbContext, ILogger<UserRepository> logger) { _identityDbContext = identityDbContext; _applicationDbContext = applicationDbContext; UserRepository = new UserRepository(_identityDbContext, logger); } - 在WebAPI项目的DI容器中配置Serilog,运行时注入真实日志:
// Program.cs中 builder.Host.UseSerilog((context, config) => config.ReadFrom.Configuration(context.Configuration)); - 单元测试中创建Mock日志对象传入:
[Fact] public void SaveChanges_SuccessSaveChanges_SaveChanges() { // Arrange Mock<DiaryApplicationDbContext> mockApplicationDbContext = UnitOfWorkConfiguration.CreateMockApplicationDbContext(); Mock<DiaryIdentityDbContext> mockIdentityDbContext = UnitOfWorkConfiguration.CreateMockIdentityDbContext(); var mockLogger = new Mock<ILogger<UserRepository>>(); IUnitOfWork unitOfWork = new UnitOfWork(mockIdentityDbContext.Object, mockApplicationDbContext.Object, mockLogger.Object); // Act unitOfWork.SaveChanges(); // Assert mockApplicationDbContext.Verify(options => options.SaveChanges(), Times.Once); mockIdentityDbContext.Verify(options => options.SaveChanges(), Times.Once); }
方法三:确保配置文件复制到测试输出目录
如果必须使用真实配置文件,修改AnimeViewDiary.Infrastructure项目的serilog.json属性:
- 在Visual Studio中右键文件→属性,设置“复制到输出目录”为“如果较新则复制”;
- 或直接在项目文件(.csproj)中添加:
之后通过<ItemGroup> <None Update="serilog.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup>Directory.GetCurrentDirectory()结合相对路径读取文件。
内容的提问来源于stack exchange,提问作者saibel203

