我有一个使用
dotnet test
运行 .NET xUnit 测试的 GitHub 操作:
jobs:
build-net:
name: .NET build & test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.100
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build
测试步骤为每个测试输出类似这样的内容:
Failed Library.Tests.UnitTest1.DeliberateFail2 [< 1 ms]
Error Message:
System.Exception : Error message
Stack Trace:
at Library.Tests.UnitTest1.DeliberateFail2() in /home/runner/work/Library/Library.Tests/UnitTest1.cs:line 30
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
然后是总结:
Failed! - Failed: 2, Passed: 133, Skipped: 0, Total: 135, Duration: 639 ms - Library.Tests.dll (net7.0)
Error: Process completed with exit code 1.
这会导致操作中出现单步失败消息:
正如您所看到的,来自
dotnet build
的空引用警告指向一行代码(并将出现在 PR 中该行的旁边),而 dotnet test
只给我 Process completed with exit code 1
我不只是想要一个失败的构建步骤,我想要每个失败的测试都有一个注释。
现有方法可以解决此问题:
.trx
结果解析为 .md
格式 https://github.com/dorny/test-reporter但它们都涉及对我的操作管道的更改或新的依赖项,我都不想要。
根据 GitHub 的文档,我应该能够使用问题匹配器来修复此问题,这是一个将
dotnet test
输出转换为注释格式的正则表达式(类似于 VSCode 中的相同模式)。
解析
dotnet test
输出的正则表达式看起来像 ^\s*Failed\s(.*)([\s\S]*?)(Stack\sTrace:\s*at\s)(.*)\s+in\s+(.*):line\s+(\d+)\s*$
,我将其添加为 .github/xunit.json
:
{
"problemMatcher": [
{
"owner": "xunit",
"pattern": [
{
"regexp": "^\\s*Failed\\s(.*)([\\s\\S]*?)(Stack\\sTrace:\\s*at\\s)(.*)\\s+in\\s+(.*):line\\s+(\\d+)\\s*$",
"file": 5,
"fromPath": 5,
"line": 6,
"message": 2
}
]
}
]
}
然后我使用
add-matcher
来包含此内容:
- name: Build
run: dotnet build --no-restore
- name: set matcher
run: echo "::add-matcher::${{ github.workspace }}/.github/xunit.json"
- name: Test
run: dotnet test --no-build --logger "trx;LogFileName=test-results.trx"
这不起作用 - 我仍然没有得到注释。
我也尝试过多行问题匹配:
{
"problemMatcher": [
{
"owner": "xunit",
"pattern": [
{
"regexp": "^\\s*Failed\\s(.*)$",
"message": 1
},
{
"regexp": "^\\s*at\\s(.*)\\s+in\\s+(.*):line\\s+(\\d+)\\s*$",
"file": 2,
"fromPath": 2,
"line": 3,
}
]
}
]
}
有人已经为
dotnet test
输出构建了 GitHub 问题匹配器吗?看起来很疯狂,没有其他人这样做过吗?
任何人都可以帮助修复这些正则表达式(或指出我在配置操作时做错了什么)?
测试了您的场景,它确实生成了注释。
这是一个测试工作流程及其输出:
name: problem_matcher_test
on: workflow_dispatch
jobs:
ci:
runs-on: ubuntu-latest
steps:
- name: Add problem matcher
env:
PROBLEM_MATCHER_CONFIG: |
{
"problemMatcher": [
{
"owner": "xunit",
"pattern": [
{
"regexp": "^\\s*Failed\\s(.*)$",
"message": 1
},
{
"regexp": "^\\s*at\\s(.*)\\s+in\\s+(.*):line\\s+(\\d+)\\s*$",
"file": 2,
"fromPath": 2,
"line": 3
}
]
}
]
}
run: |
echo "$PROBLEM_MATCHER_CONFIG" > xunit.json
echo "$PWD/xunit.json"
cat -n "$PWD/xunit.json"
echo "::add-matcher::$PWD/xunit.json"
- name: Dumping Logs
env:
LOGS: |
Failed Library.Tests.UnitTest1.DeliberateFail1 [< 1 ms]
at Library.Tests.UnitTest1.DeliberateFail1() in /home/runner/work/Library/Library.Tests/UnitTests.cs:line 1
Failed Library.Tests.UnitTest1.DeliberateFail2 [< 1 ms]
at Library.Tests.UnitTest1.DeliberateFail2() in /home/runner/work/Library/Library.Tests/UnitTests.cs:line 2
run: |
echo "$LOGS"
- name: Remove problem matcher
run: |
echo "::remove-matcher::$PWD/xunit.json"
输出:
有了有效的文件路径,它应该生成完整的注释。在此处观察有关路径的这些调试日志:
但是,例外情况是多行,并且可能并不总是包含相同数量的行。对于上述测试,我仅将日志剥离到所需的行。
显然,由于其限制以及缺乏配置选项,问题匹配器解决方案无法针对多个多行错误进行扩展。
IMO,如果您不想使用现有的解决方案,将测试日志转储到文件然后扫描这些日志以获取所需的输出更有意义。
我一直在尝试解决多行匹配的限制,所以这是我的问题匹配器:
PROBLEM_MATCHER_CONFIG: |
{
"problemMatcher": [
{
"owner": "xunit",
"pattern": [
{
"regexp": "^\\s*Failed\\s(.*)$"
},
{
"regexp": "^\\s*Error Message:\\s*$"
},
{
"regexp": "^\\s*(.*?)\\s*$",
"message": 1
},
{
"regexp": "^\\s*Expected:"
},
{
"regexp": "^\\s*Actual:"
},
{
"regexp": "^\\s*Stack Trace:\\s*$"
},
{
"regexp": "^\\s*at\\s(.*)\\s+in\\s+(.*):line\\s+(\\d+)\\s*$",
"file": 2,
"fromPath": 2,
"line": 3
}
]
},
{
"owner": "xunit.exc",
"pattern": [
{
"regexp": "^\\s*Failed\\s(.*)$"
},
{
"regexp": "^\\s*Error Message:\\s*$"
},
{
"regexp": "^\\s*(.*Exception.*)\\s*$",
"message": 1
},
{
"regexp": "^\\s*Stack Trace:\\s*$"
},
{
"regexp": "^\\s*at\\s(.*)\\s+in\\s+(.*):line\\s+(\\d+)\\s*$",
"file": 2,
"fromPath": 2,
"line": 3
}
]
}
]
}
必须定义 2 个所有者才能匹配不同的输出格式。第一个与常规 dotnet 测试用例输出匹配,但预期结果有所不同。例如。就像这里:
Failed Prime.UnitTests.Services.PrimeService_IsPrimeShould.IsPrime_InputIs2_ReturnTrue [2 ms]
Error Message:
2 should be prime
Expected: True
Actual: False
Stack Trace:
at Prime.Services.PrimeService.IsPrime(Int32 candidate) in C:\Users\Jens\Documents\Visual Studio 2022\Projects\unit-testing-using-dotnet-test\PrimeService\PrimeService.cs:line 9
at Prime.UnitTests.Services.PrimeService_IsPrimeShould.IsPrime_InputIs1_ReturnFalse() in C:\Users\Jens\Documents\Visual Studio 2022\Projects\unit-testing-using-dotnet-test\PrimeService.Tests\PrimeService_IsPrimeShould.cs:line 12
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
第二个匹配抛出异常的测试用例的输出,如下所示:
Failed Prime.UnitTests.Services.PrimeService_IsPrimeShould.IsPrime_InputIs1_ReturnFalse [2 ms]
Error Message:
System.NotImplementedException : Not implemented.
Stack Trace:
at Prime.Services.PrimeService.IsPrime(Int32 candidate) in C:\Users\Jens\Documents\Visual Studio 2022\Projects\unit-testing-using-dotnet-test\PrimeService\PrimeService.cs:line 10
at Prime.UnitTests.Services.PrimeService_IsPrimeShould.IsPrime_InputIs1_ReturnFalse() in C:\Users\Jens\Documents\Visual Studio 2022\Projects\unit-testing-using-dotnet-test\PrimeService.Tests\PrimeService_IsPrimeShould.cs:line 12
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
这将为两种 dotnet 测试结果提供有效的注释,至少对于实际/预期适合单行的标准测试用例而言。