Scrubbers
April 3, 2026 ยท View on GitHub
Scrubbers run on the final string before doing the verification action.
Multiple scrubbers can be defined at multiple levels.
By default scrubber are executed in reverse order. So the most recent added method scrubber through to earliest added global scrubber. All scrubber APIs support a ScrubberLocation location. To execute a scrubber last use ScrubberLocation.Last.
Scrubbers can be added multiple times to have them execute multiple times. This can be helpful when compounding multiple scrubbers together.
Available Scrubbers
Scrubbers can be added to an instance of VerifySettings or globally on VerifierSettings.
Directory Scrubbers
- The current solution directory will be replaced with
{SolutionDirectory}. To disable useVerifierSettings.DontScrubSolutionDirectory()in a module initializer. See solution Discovery - The current project directory will be replaced with
{ProjectDirectory}. To disable useVerifierSettings.DontScrubProjectDirectory()in a module initializer. - On Windows, the current user profile will be replaced with
{UserProfile}. To disable useVerifierSettings.DontScrubUserProfile()in a module initializer. - The
AppDomain.CurrentDomain.BaseDirectorywill be replaced with{CurrentDirectory}. - The
Assembly.CodeBasewill be replaced with{CurrentDirectory}. - The
Path.GetTempPath()will be replaced with{TempPath}.
Attribute data
The solution and project directory replacement functionality is achieved by adding attributes to the target assembly at compile time. For any project that references Verify, the following attributes will be added:
[assembly: AssemblyMetadata("Verify.ProjectDirectory", "C:\Code\TheSolution\Project\")]
[assembly: AssemblyMetadata("Verify.SolutionDirectory", "C:\Code\TheSolution\")]
This information can be useful to consumers when writing tests, so it is exposed via AttributeReader:
- Project directory for an assembly:
AttributeReader.GetProjectDirectory(assembly) - Project directory for the current executing assembly:
AttributeReader.GetProjectDirectory() - Solution directory for an assembly:
AttributeReader.GetSolutionDirectory(assembly) - Solution directory for the current executing assembly:
AttributeReader.GetSolutionDirectory()
ScrubLines
Allows lines to be selectively removed using a Func.
For example remove lines containing text:
verifySettings.ScrubLines(line => line.Contains("text"));
ScrubLinesContaining
Remove all lines containing any of the defined strings.
For example remove lines containing text1 or text2
verifySettings.ScrubLinesContaining("text1", "text2");
Case insensitive by default (StringComparison.OrdinalIgnoreCase).
StringComparison can be overridden:
verifySettings.ScrubLinesContaining(StringComparison.Ordinal, "text1", "text2");
ScrubLinesWithReplace
Allows lines to be selectively replaced using a Func.
For example converts lines to upper case:
verifySettings.ScrubLinesWithReplace(line => line.ToUpper());
ScrubMachineName
Replaces Environment.MachineName with TheMachineName.
verifySettings.ScrubMachineName();
ScrubUserName
Replaces Environment.UserName with TheUserName.
verifySettings.ScrubUserName();
AddScrubber
Adds a scrubber with full control over the text via a Func
DisableScrubbers
Given the following target
static object BuildTarget() =>
new Target(
"C:/Code/TheSolution",
"C:/Code/TheSolution/TheProject",
new Date(2020, 1, 1),
new DateTime(2020, 1, 1),
new DateTimeOffset(2020, 1, 1, 1, 1, 1, TimeSpan.FromHours(10)),
new Guid("ae8529a6-30a0-46e2-b7d6-9fcb7b23463c"));
When scrubbers are disabled the result will be:
{
TheSolutionDir: C:/Code/TheSolution,
TheProjectDir: C:/Code/TheSolution/TheProject,
Date: 2020-01-01,
DateTime: 2020-01-01,
DateTimeOffset: 2020-01-01 01:01:01 +10,
Guid: ae8529a6-30a0-46e2-b7d6-9fcb7b23463c
}
Instance
[Fact]
public Task Instance()
{
var settings = new VerifySettings();
settings.DisableScrubbers();
return Verify(BuildTarget(), settings);
}
Fluent
[Fact]
public Task Fluent() =>
Verify(BuildTarget())
.DisableScrubbers();
More complete example
NUnit
[TestFixture]
public class ScrubbersSample
{
[Test]
public Task Lines()
{
var settings = new VerifySettings();
settings.ScrubLinesWithReplace(
replaceLine: _ =>
{
if (_.Contains("LineE"))
{
return "NoMoreLineE";
}
return _;
});
settings.ScrubLines(removeLine: _ => _.Contains('J'));
settings.ScrubLinesContaining("b", "D");
settings.ScrubLinesContaining(StringComparison.Ordinal, "H");
return Verify(
settings: settings,
target: """
LineA
LineB
LineC
LineD
LineE
LineH
LineI
LineJ
""");
}
[Test]
public Task LinesFluent() =>
Verify("""
LineA
LineB
LineC
LineD
LineE
LineH
LineI
LineJ
""")
.ScrubLinesWithReplace(
replaceLine: _ =>
{
if (_.Contains("LineE"))
{
return "NoMoreLineE";
}
return _;
})
.ScrubLines(removeLine: _ => _.Contains('J'))
.ScrubLinesContaining("b", "D")
.ScrubLinesContaining(StringComparison.Ordinal, "H");
[Test]
public Task RemoveOrReplace() =>
Verify("""
LineA
LineB
LineC
""")
.ScrubLinesWithReplace(
replaceLine: line =>
{
if (line.Contains("LineB"))
{
return null;
}
return line.ToLower();
});
[Test]
public Task EmptyLines() =>
Verify("""
LineA
LineC
""")
.ScrubEmptyLines();
}
xUnit
public class ScrubbersSample
{
[Fact]
public Task Lines()
{
var settings = new VerifySettings();
settings.ScrubLinesWithReplace(
replaceLine: line =>
{
if (line.Contains("LineE"))
{
return "NoMoreLineE";
}
return line;
});
settings.ScrubLines(removeLine: _ => _.Contains('J'));
settings.ScrubLinesContaining("b", "D");
settings.ScrubLinesContaining(StringComparison.Ordinal, "H");
return Verify(
settings: settings,
target: """
LineA
LineB
LineC
LineD
LineE
LineH
LineI
LineJ
""");
}
[Fact]
public Task EmptyLine()
{
var settings = new VerifySettings();
settings.ScrubLinesWithReplace(
replaceLine: _ => "");
return Verify(
settings: settings,
target: "");
}
[Fact]
public Task LinesFluent() =>
Verify("""
LineA
LineB
LineC
LineD
LineE
LineH
LineI
LineJ
""")
.ScrubLinesWithReplace(
replaceLine: _ =>
{
if (_.Contains("LineE"))
{
return "NoMoreLineE";
}
return _;
})
.ScrubLines(removeLine: _ => _.Contains('J'))
.ScrubLinesContaining("b", "D")
.ScrubLinesContaining(StringComparison.Ordinal, "H");
[Fact]
public Task RemoveOrReplace() =>
Verify("""
LineA
LineB
LineC
""")
.ScrubLinesWithReplace(
replaceLine: line =>
{
if (line.Contains("LineB"))
{
return null;
}
return line.ToLower();
});
[Fact]
public Task EmptyLines() =>
Verify("""
LineA
LineC
""")
.ScrubEmptyLines();
}
Fixie
public class ScrubbersSample
{
public Task Lines()
{
var settings = new VerifySettings();
settings.ScrubLinesWithReplace(
replaceLine: _ =>
{
if (_.Contains("LineE"))
{
return "NoMoreLineE";
}
return _;
});
settings.ScrubLines(removeLine: _ => _.Contains('J'));
settings.ScrubLinesContaining("b", "D");
settings.ScrubLinesContaining(StringComparison.Ordinal, "H");
return Verify(
settings: settings,
target: """
LineA
LineB
LineC
LineD
LineE
LineH
LineI
LineJ
""");
}
public Task LinesFluent() =>
Verify("""
LineA
LineB
LineC
LineD
LineE
LineH
LineI
LineJ
""")
.ScrubLinesWithReplace(
replaceLine: _ =>
{
if (_.Contains("LineE"))
{
return "NoMoreLineE";
}
return _;
})
.ScrubLines(removeLine: _ => _.Contains('J'))
.ScrubLinesContaining("b", "D")
.ScrubLinesContaining(StringComparison.Ordinal, "H");
public Task RemoveOrReplace() =>
Verify("""
LineA
LineB
LineC
""")
.ScrubLinesWithReplace(
replaceLine: line =>
{
if (line.Contains("LineB"))
{
return null;
}
return line.ToLower();
});
public Task EmptyLines() =>
Verify("""
LineA
LineC
""")
.ScrubEmptyLines();
}
MSTest
[TestClass]
public partial class ScrubbersSample
{
[TestMethod]
public Task Lines()
{
var settings = new VerifySettings();
settings.ScrubLinesWithReplace(
replaceLine: _ =>
{
if (_.Contains("LineE"))
{
return "NoMoreLineE";
}
return _;
});
settings.ScrubLines(removeLine: _ => _.Contains('J'));
settings.ScrubLinesContaining("b", "D");
settings.ScrubLinesContaining(StringComparison.Ordinal, "H");
return Verify(
settings: settings,
target: """
LineA
LineB
LineC
LineD
LineE
LineH
LineI
LineJ
""");
}
[TestMethod]
public Task LinesFluent() =>
Verify("""
LineA
LineB
LineC
LineD
LineE
LineH
LineI
LineJ
""")
.ScrubLinesWithReplace(
replaceLine: _ =>
{
if (_.Contains("LineE"))
{
return "NoMoreLineE";
}
return _;
})
.ScrubLines(removeLine: _ => _.Contains('J'))
.ScrubLinesContaining("b", "D")
.ScrubLinesContaining(StringComparison.Ordinal, "H");
[TestMethod]
public Task RemoveOrReplace() =>
Verify("""
LineA
LineB
LineC
""")
.ScrubLinesWithReplace(
replaceLine: line =>
{
if (line.Contains("LineB"))
{
return null;
}
return line.ToLower();
});
[TestMethod]
public Task EmptyLines() =>
Verify("""
LineA
LineC
""")
.ScrubEmptyLines();
}
TUnit
public class ScrubbersSample
{
[Test]
public Task Lines()
{
var settings = new VerifySettings();
settings.ScrubLinesWithReplace(
replaceLine: _ =>
{
if (_.Contains("LineE"))
{
return "NoMoreLineE";
}
return _;
});
settings.ScrubLines(removeLine: _ => _.Contains('J'));
settings.ScrubLinesContaining("b", "D");
settings.ScrubLinesContaining(StringComparison.Ordinal, "H");
return Verify(
settings: settings,
target: """
LineA
LineB
LineC
LineD
LineE
LineH
LineI
LineJ
""");
}
[Test]
public Task LinesFluent() =>
Verify("""
LineA
LineB
LineC
LineD
LineE
LineH
LineI
LineJ
""")
.ScrubLinesWithReplace(
replaceLine: _ =>
{
if (_.Contains("LineE"))
{
return "NoMoreLineE";
}
return _;
})
.ScrubLines(removeLine: _ => _.Contains('J'))
.ScrubLinesContaining("b", "D")
.ScrubLinesContaining(StringComparison.Ordinal, "H");
[Test]
public Task RemoveOrReplace() =>
Verify("""
LineA
LineB
LineC
""")
.ScrubLinesWithReplace(
replaceLine: line =>
{
if (line.Contains("LineB"))
{
return null;
}
return line.ToLower();
});
[Test]
public Task EmptyLines() =>
Verify("""
LineA
LineC
""")
.ScrubEmptyLines();
}
Results
LineA
LineC
NoMoreLineE
LineI
Scrubber levels
Scrubbers can be defined at three levels:
- Method: Will run the verification in the current test method.
- Class: As a class level 'VerifySettings' field then re-used at the method level.
- Global: Will run for test methods on all tests.
NUnit
[TestFixture]
public class ScrubberLevelsSample
{
VerifySettings classLevelSettings;
public ScrubberLevelsSample()
{
classLevelSettings = new();
classLevelSettings.AddScrubber(_ => _.Replace("Three", "C"));
}
[Test]
public Task Simple()
{
var settings = new VerifySettings(classLevelSettings);
settings.AddScrubber(_ => _.Replace("Two", "B"));
return Verify("One Two Three", settings);
}
[Test]
public Task SimpleFluent() =>
Verify("One Two Three", classLevelSettings)
.AddScrubber(_ => _.Replace("Two", "B"));
[ModuleInitializer]
public static void Setup() =>
VerifierSettings.AddScrubber(_ => _.Replace("One", "A"));
}
xUnit
public class ScrubberLevelsSample
{
VerifySettings classLevelSettings;
public ScrubberLevelsSample()
{
classLevelSettings = new();
classLevelSettings.AddScrubber(_ => _.Replace("Three", "C"));
}
[Fact]
public Task Usage()
{
var settings = new VerifySettings(classLevelSettings);
settings.AddScrubber(_ => _.Replace("Two", "B"));
return Verify("One Two Three", settings);
}
[Fact]
public Task UsageFluent() =>
Verify("One Two Three", classLevelSettings)
.AddScrubber(_ => _.Replace("Two", "B"));
[ModuleInitializer]
public static void Initialize() =>
VerifierSettings.AddScrubber(_ => _.Replace("One", "A"));
}
Fixie
public class ScrubberLevelsSample
{
VerifySettings classLevelSettings;
public ScrubberLevelsSample()
{
classLevelSettings = new();
classLevelSettings.AddScrubber(_ => _.Replace("Three", "C"));
}
public Task Simple()
{
var settings = new VerifySettings(classLevelSettings);
settings.AddScrubber(_ => _.Replace("Two", "B"));
return Verify("One Two Three", settings);
}
public Task SimpleFluent() =>
Verify("One Two Three", classLevelSettings)
.AddScrubber(_ => _.Replace("Two", "B"));
[ModuleInitializer]
public static void Setup() =>
VerifierSettings.AddScrubber(_ => _.Replace("One", "A"));
}
MSTest
[TestClass]
public partial class ScrubberLevelsSample
{
VerifySettings classLevelSettings;
public ScrubberLevelsSample()
{
classLevelSettings = new();
classLevelSettings.AddScrubber(_ => _.Replace("Three", "C"));
}
[TestMethod]
public Task Simple()
{
var settings = new VerifySettings(classLevelSettings);
settings.AddScrubber(_ => _.Replace("Two", "B"));
return Verify("One Two Three", settings);
}
[TestMethod]
public Task SimpleFluent() =>
Verify("One Two Three", classLevelSettings)
.AddScrubber(_ => _.Replace("Two", "B"));
[AssemblyInitialize]
public static void Setup(TestContext testContext) =>
VerifierSettings.AddScrubber(_ => _.Replace("One", "A"));
}
TUnit
public class ScrubberLevelsSample
{
VerifySettings classLevelSettings;
public ScrubberLevelsSample()
{
classLevelSettings = new();
classLevelSettings.AddScrubber(_ => _.Replace("Three", "C"));
}
[Test]
public Task Simple()
{
var settings = new VerifySettings(classLevelSettings);
settings.AddScrubber(_ => _.Replace("Two", "B"));
return Verify("One Two Three", settings);
}
[Test]
public Task SimpleFluent() =>
Verify("One Two Three", classLevelSettings)
.AddScrubber(_ => _.Replace("Two", "B"));
[ModuleInitializer]
public static void Setup() =>
VerifierSettings.AddScrubber(_ => _.Replace("One", "A"));
}
Result
A B C