Skip to content

Commit acc7c7b

Browse files
committed
Name the guarded API in ThrowIfVerifyHasBeenRun
new StackTrace(1, false) already skips the frame for the guard itself, so frame 0 is the API that called it and frame 1 is that API's caller. Reading frame 1 meant the message named the calling test method as "The API", pointing at the wrong place to move to a module initializer.
1 parent fd7d700 commit acc7c7b

3 files changed

Lines changed: 27 additions & 2 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
public class ThrowIfVerifyHasBeenRunTests
2+
{
3+
// The message points at the API that must move to a module initializer, so it has to
4+
// name that API and not the code that called it
5+
[Fact]
6+
public void NamesTheApi()
7+
{
8+
var original = InnerVerifier.verifyHasBeenRun;
9+
InnerVerifier.verifyHasBeenRun = true;
10+
try
11+
{
12+
var exception = Assert.Throws<Exception>(
13+
() => VerifierSettings.IgnoreMembers<string>("TheMember"));
14+
15+
Assert.Contains("The API 'VerifyTests.VerifierSettings.IgnoreMembers'", exception.Message);
16+
Assert.DoesNotContain(nameof(NamesTheApi), exception.Message);
17+
}
18+
finally
19+
{
20+
InnerVerifier.verifyHasBeenRun = original;
21+
}
22+
}
23+
}

src/Verify/Verifier/InnerVerifier.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ public static void ThrowIfVerifyHasBeenRun()
3232
return;
3333
}
3434

35+
// The frame for this method is already skipped, so frame 0 is the guarded API.
36+
// Frame 1 would be the caller of that API, which is the code being told off.
3537
var stackTrace = new StackTrace(1, false);
36-
var method = stackTrace.GetFrame(1)!.GetMethod()!;
38+
var method = stackTrace.GetFrame(0)!.GetMethod()!;
3739
var type = method.DeclaringType;
3840
throw new($"The API '{type}.{method.Name}' must be called prior to any Verify has run. Usually this is done in a [ModuleInitializer]. Verify run by: {verifyHasBeenRunBy}");
3941
}

src/todo.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ All six resolved 2026-08-16 (five fixed here; the inline item resolved as by-des
5656
- [ ] **`Delete:` section drops subdirectories, breaking the parse round-trip.**
5757
`Verify/Verifier/VerifyExceptionMessageBuilder.cs:62` emits `Path.GetFileName(file)` while the other sections emit directory-relative paths, and `Verify.ExceptionParsing/Parser.cs:109` reconstructs with `Path.Combine(directory, name)`. For `UseUniqueDirectory()`/`VerifyDirectory` tests, a stale `{Directory}\Type.Method\old.verified.txt` parses back as the nonexistent `{Directory}\old.verified.txt`; same-named files in different subdirectories collapse.
5858

59-
- [ ] **`ThrowIfVerifyHasBeenRun` blames the caller instead of the API.**
59+
- [x] **`ThrowIfVerifyHasBeenRun` blames the caller instead of the API.**
6060
`Verify/Verifier/InnerVerifier.cs:35-38``new StackTrace(1, false)` already skips the guard, so frame 0 is the guarded API; `GetFrame(1)` fetches the API's caller. The message names the user's own method as "The API". Runtime-reproduced (Debug and Release). Fix: `GetFrame(0)`.
6161

6262
## Minor / edge cases

0 commit comments

Comments
 (0)