diff --git a/docs/mdsource/doc-index.include.md b/docs/mdsource/doc-index.include.md
index 50e487c51..34428ced8 100644
--- a/docs/mdsource/doc-index.include.md
+++ b/docs/mdsource/doc-index.include.md
@@ -41,6 +41,7 @@
* [Recording](/docs/recording.md)
* [Explicit Targets](/docs/explicit-targets.md)
* [TempDirectory](/docs/temp-directory.md)
+ * [TempFile](/docs/temp-file.md)
* [FSharp Usage](/docs/fsharp.md)
* [Compared to ApprovalTests](/docs/compared-to-approvaltests.md)
* [Plugins](/docs/plugins.md)
\ No newline at end of file
diff --git a/docs/mdsource/temp-file.source.md b/docs/mdsource/temp-file.source.md
index ff5e75292..7a2c4bfcf 100644
--- a/docs/mdsource/temp-file.source.md
+++ b/docs/mdsource/temp-file.source.md
@@ -98,6 +98,58 @@ Result:
snippet: TempFileTests.Scrubbing.verified.txt
+### Create Method
+
+Creates a new temporary file with optional extension and encoding.
+
+snippet: TempFileCreate
+
+
+#### With Extension
+
+Create a temporary file with a specific extension:
+
+snippet: TempFileCreateWithExtension
+
+
+#### With Encoding
+
+Create a temporary file with a specific text encoding and BOM:
+
+snippet: TempFileCreateWithEncoding
+
+
+### CreateText Method
+
+Creates a new temporary file with text content asynchronously.
+
+snippet: TempFileCreateText
+
+
+#### With Extension
+
+snippet: TempFileCreateTextWithExtension
+
+
+#### With Encoding
+
+Create a text file with specific encoding:
+
+snippet: TempFileCreateTextWithEncoding
+
+
+### CreateBinary Method
+
+Creates a new temporary file with binary content asynchronously.
+
+snippet: TempFileCreateBinary
+
+
+#### With Extension
+
+snippet: TempFileCreateBinaryWithExtension
+
+
### Debugging
Given `TempFile` deletes the file on test completion (even failure), it can be difficult to debug what caused the failure.
diff --git a/docs/readme.md b/docs/readme.md
index c41978779..dbf211f4c 100644
--- a/docs/readme.md
+++ b/docs/readme.md
@@ -50,6 +50,7 @@ To change this file edit the source file and then run MarkdownSnippets.
* [Recording](/docs/recording.md)
* [Explicit Targets](/docs/explicit-targets.md)
* [TempDirectory](/docs/temp-directory.md)
+ * [TempFile](/docs/temp-file.md)
* [FSharp Usage](/docs/fsharp.md)
* [Compared to ApprovalTests](/docs/compared-to-approvaltests.md)
* [Plugins](/docs/plugins.md)
diff --git a/docs/temp-file.md b/docs/temp-file.md
index e60b8ba65..a5bad7648 100644
--- a/docs/temp-file.md
+++ b/docs/temp-file.md
@@ -91,7 +91,7 @@ public void StringConversion()
Trace.WriteLine(content);
}
```
-snippet source | anchor
+snippet source | anchor
@@ -114,7 +114,7 @@ public void FileInfoConversion()
Trace.WriteLine(directoryName);
}
```
-snippet source | anchor
+snippet source | anchor
@@ -135,7 +135,7 @@ public void InfoProperty()
Trace.WriteLine(directoryName);
}
```
-snippet source | anchor
+snippet source | anchor
@@ -224,6 +224,139 @@ Result:
+### Create Method
+
+Creates a new temporary file with optional extension and encoding.
+
+
+
+```cs
+using var temp = TempFile.Create();
+
+File.WriteAllText(temp, "content");
+
+// file automatically deleted here
+```
+snippet source | anchor
+
+
+
+#### With Extension
+
+Create a temporary file with a specific extension:
+
+
+
+```cs
+using var temp = TempFile.Create(".txt");
+
+File.WriteAllText(temp, "content");
+```
+snippet source | anchor
+
+
+
+#### With Encoding
+
+Create a temporary file with a specific text encoding and BOM:
+
+
+
+```cs
+using var temp = TempFile.Create(".txt", Encoding.UTF8);
+
+File.Exists(temp.Path);
+```
+snippet source | anchor
+
+
+
+### CreateText Method
+
+Creates a new temporary file with text content asynchronously.
+
+
+
+```cs
+using var temp = await TempFile.CreateText("Hello, World!");
+
+var content = await File.ReadAllTextAsync(temp);
+Assert.Equal("Hello, World!", content);
+```
+snippet source | anchor
+
+
+
+#### With Extension
+
+
+
+```cs
+var json = """
+ {
+ "name": "test",
+ "value": 123
+ }
+ """;
+
+using var temp = await TempFile.CreateText(json, ".json");
+
+var content = await File.ReadAllTextAsync(temp);
+Assert.Equal(json, content);
+```
+snippet source | anchor
+
+
+
+#### With Encoding
+
+Create a text file with specific encoding:
+
+
+
+```cs
+using var temp = await TempFile.CreateText(
+ "Content with special chars: äöü",
+ ".txt",
+ Encoding.UTF8);
+
+var content = await File.ReadAllTextAsync(temp, Encoding.UTF8);
+Assert.Equal("Content with special chars: äöü", content);
+```
+snippet source | anchor
+
+
+
+### CreateBinary Method
+
+Creates a new temporary file with binary content asynchronously.
+
+
+
+```cs
+byte[] data = [0x01, 0x02, 0x03, 0x04];
+
+using var temp = await TempFile.CreateBinary(data);
+
+var readData = await File.ReadAllBytesAsync(temp);
+Assert.Equal(data, readData);
+```
+snippet source | anchor
+
+
+
+#### With Extension
+
+
+
+```cs
+byte[] data = [0x01, 0x02, 0x03, 0x04];
+using var temp = await TempFile.CreateBinary(data, ".bin");
+```
+snippet source | anchor
+
+
+
### Debugging
Given `TempFile` deletes the file on test completion (even failure), it can be difficult to debug what caused the failure.
diff --git a/readme.md b/readme.md
index 7cd6a70ca..250e35b08 100644
--- a/readme.md
+++ b/readme.md
@@ -1112,6 +1112,7 @@ To opt out of this feature, include the following in the project file:
* [Recording](/docs/recording.md)
* [Explicit Targets](/docs/explicit-targets.md)
* [TempDirectory](/docs/temp-directory.md)
+ * [TempFile](/docs/temp-file.md)
* [FSharp Usage](/docs/fsharp.md)
* [Compared to ApprovalTests](/docs/compared-to-approvaltests.md)
* [Plugins](/docs/plugins.md)
diff --git a/src/Verify.Tests/TempFileTests.CreateBinary_CanBeVerified.verified.bin b/src/Verify.Tests/TempFileTests.CreateBinary_CanBeVerified.verified.bin
new file mode 100644
index 000000000..177e962b3
--- /dev/null
+++ b/src/Verify.Tests/TempFileTests.CreateBinary_CanBeVerified.verified.bin
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/Verify.Tests/TempFileTests.CreateText_CanBeVerified.verified.txt b/src/Verify.Tests/TempFileTests.CreateText_CanBeVerified.verified.txt
new file mode 100644
index 000000000..2f28b19bc
--- /dev/null
+++ b/src/Verify.Tests/TempFileTests.CreateText_CanBeVerified.verified.txt
@@ -0,0 +1 @@
+test content
\ No newline at end of file
diff --git a/src/Verify.Tests/TempFileTests.cs b/src/Verify.Tests/TempFileTests.cs
index 50708e6d6..0003f07ec 100644
--- a/src/Verify.Tests/TempFileTests.cs
+++ b/src/Verify.Tests/TempFileTests.cs
@@ -160,6 +160,17 @@ public void Create_WithEncoding_CreatesFileWithBom()
Assert.True(bytes.Length >= 3);
}
+ [Fact]
+ public void CreateText_WithEncoding_CreatesFileWithBom()
+ {
+ using var temp = TempFile.Create(".txt", Encoding.UTF8);
+
+ Assert.True(File.Exists(temp.Path));
+ var bytes = File.ReadAllBytes(temp.Path);
+ // UTF8 BOM should be present
+ Assert.True(bytes.Length >= 3);
+ }
+
[Fact]
public void Dispose_DeletesFile()
{
@@ -380,4 +391,344 @@ public void InfoProperty()
[Fact]
public void Constructor_WithEmptyExtension_ThrowsArgumentException() =>
Assert.Throws(() => new TempFile(string.Empty));
+
+ [Fact]
+ public async Task CreateText_WithContent_CreatesFileWithContent()
+ {
+ var content = "Hello, World!";
+
+ using var temp = await TempFile.CreateText(content);
+
+ Assert.True(File.Exists(temp.Path));
+ var readContent = await File.ReadAllTextAsync(temp.Path);
+ Assert.Equal(content, readContent);
+ }
+
+ [Fact]
+ public async Task CreateText_WithExtension_CreatesFileWithExtensionAndContent()
+ {
+ var content = "Test content";
+
+ using var temp = await TempFile.CreateText(content, ".txt");
+
+ Assert.EndsWith(".txt", temp.Path);
+ Assert.True(File.Exists(temp.Path));
+ var readContent = await File.ReadAllTextAsync(temp.Path);
+ Assert.Equal(content, readContent);
+ }
+
+ [Fact]
+ public async Task CreateText_WithEncoding_CreatesFileWithSpecifiedEncoding()
+ {
+ var content = "Test with special characters: äöü";
+
+ using var temp = await TempFile.CreateText(content, ".txt", Encoding.UTF8);
+
+ Assert.True(File.Exists(temp.Path));
+ var readContent = await File.ReadAllTextAsync(temp.Path, Encoding.UTF8);
+ Assert.Equal(content, readContent);
+ }
+
+ [Fact]
+ public async Task CreateText_WithAsciiEncoding_CreatesFileWithAsciiEncoding()
+ {
+ var content = "ASCII content only";
+
+ using var temp = await TempFile.CreateText(content, ".txt", Encoding.ASCII);
+
+ Assert.True(File.Exists(temp.Path));
+ var readContent = await File.ReadAllTextAsync(temp.Path, Encoding.ASCII);
+ Assert.Equal(content, readContent);
+ }
+
+ [Fact]
+ public async Task CreateText_WithEmptyContent_CreatesEmptyFile()
+ {
+ using var temp = await TempFile.CreateText(string.Empty, ".txt");
+
+ Assert.True(File.Exists(temp.Path));
+ var readContent = await File.ReadAllTextAsync(temp.Path);
+ Assert.Empty(readContent);
+ }
+
+ [Fact]
+ public async Task CreateText_WithMultilineContent_PreservesLineBreaks()
+ {
+ var content = """
+ Line 1
+ Line 2
+ Line 3
+ """;
+
+ using var temp = await TempFile.CreateText(content, ".txt");
+
+ var readContent = await File.ReadAllTextAsync(temp.Path);
+ Assert.Equal(content, readContent);
+ }
+
+ [Fact]
+ public async Task CreateText_DisposesCorrectly()
+ {
+ string path;
+ {
+ using var temp = await TempFile.CreateText("content", ".txt");
+ path = temp.Path;
+ Assert.True(File.Exists(path));
+ }
+
+ Assert.False(File.Exists(path));
+ }
+
+ [Fact]
+ public async Task CreateBinary_WithContent_CreatesFileWithBinaryContent()
+ {
+ var data = "Hello"u8.ToArray(); // "Hello" in ASCII
+
+ using var temp = await TempFile.CreateBinary(data);
+
+ Assert.True(File.Exists(temp.Path));
+ var readData = await File.ReadAllBytesAsync(temp.Path);
+ Assert.Equal(data, readData);
+ }
+
+ [Fact]
+ public async Task CreateBinary_WithExtension_CreatesFileWithExtensionAndContent()
+ {
+ byte[] data = [0x01, 0x02, 0x03, 0x04];
+
+ using var temp = await TempFile.CreateBinary(data, ".bin");
+
+ Assert.EndsWith(".bin", temp.Path);
+ Assert.True(File.Exists(temp.Path));
+ var readData = await File.ReadAllBytesAsync(temp.Path);
+ Assert.Equal(data, readData);
+ }
+
+ [Fact]
+ public async Task CreateBinary_WithEmptyContent_CreatesEmptyFile()
+ {
+ using var temp = await TempFile.CreateBinary(ReadOnlyMemory.Empty, ".bin");
+
+ Assert.True(File.Exists(temp.Path));
+ var readData = await File.ReadAllBytesAsync(temp.Path);
+ Assert.Empty(readData);
+ }
+
+ [Fact]
+ public async Task CreateBinary_WithLargeBinaryData_WritesCorrectly()
+ {
+ var data = new byte[1024];
+ Random.Shared.NextBytes(data);
+
+ using var temp = await TempFile.CreateBinary(data, ".dat");
+
+ Assert.True(File.Exists(temp.Path));
+ var readData = await File.ReadAllBytesAsync(temp.Path);
+ Assert.Equal(data, readData);
+ Assert.Equal(1024, readData.Length);
+ }
+
+ [Fact]
+ public async Task CreateBinary_DisposesCorrectly()
+ {
+ string path;
+ byte[] data = [0xFF, 0xFE, 0xFD];
+
+ {
+ using var temp = await TempFile.CreateBinary(data, ".bin");
+ path = temp.Path;
+ Assert.True(File.Exists(path));
+ }
+
+ Assert.False(File.Exists(path));
+ }
+
+ [Fact]
+ public async Task CreateBinary_WithImageData_CreatesValidFile()
+ {
+ // Simulate a simple 1x1 PNG (minimal valid PNG)
+ byte[] pngData =
+ [
+ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // PNG signature
+ 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52 // IHDR chunk start
+ ];
+
+ using var temp = await TempFile.CreateBinary(pngData, ".png");
+
+ Assert.EndsWith(".png", temp.Path);
+ var readData = await File.ReadAllBytesAsync(temp.Path);
+ Assert.Equal(pngData, readData);
+ }
+
+ [Fact]
+ public async Task CreateText_CanBeVerified()
+ {
+ using var temp = await TempFile.CreateText("test content", ".txt");
+
+ var content = await File.ReadAllTextAsync(temp.Path);
+ await Verify(content);
+ }
+
+ [Fact]
+ public async Task CreateBinary_CanBeVerified()
+ {
+ byte[] data = [0x01, 0x02, 0x03, 0x04, 0x05];
+
+ using var temp = await TempFile.CreateBinary(data, ".bin");
+
+ var readData = await File.ReadAllBytesAsync(temp.Path);
+ await Verify(readData);
+ }
+
+ [Fact]
+ public async Task CreateText_WithJsonContent_CreatesValidJsonFile()
+ {
+ var jsonContent = """
+ {
+ "name": "test",
+ "value": 123
+ }
+ """;
+
+ using var temp = await TempFile.CreateText(jsonContent, ".json");
+
+ Assert.EndsWith(".json", temp.Path);
+ var readContent = await File.ReadAllTextAsync(temp.Path);
+ Assert.Equal(jsonContent, readContent);
+ }
+
+ [Fact]
+ public async Task CreateText_WithXmlContent_CreatesValidXmlFile()
+ {
+ var xmlContent = """
+
+
+ value
+
+ """;
+
+ using var temp = await TempFile.CreateText(xmlContent, ".xml");
+
+ Assert.EndsWith(".xml", temp.Path);
+ var readContent = await File.ReadAllTextAsync(temp.Path);
+ Assert.Equal(xmlContent, readContent);
+ }
+
+ [Fact]
+ public void Create()
+ {
+ #region TempFileCreate
+
+ using var temp = TempFile.Create();
+
+ File.WriteAllText(temp, "content");
+
+ // file automatically deleted here
+
+ #endregion
+ }
+
+
+ [Fact]
+ public void CreateWithExtension()
+ {
+ #region TempFileCreateWithExtension
+
+ using var temp = TempFile.Create(".txt");
+
+ File.WriteAllText(temp, "content");
+
+ #endregion
+ }
+
+
+
+ [Fact]
+ public void CreateWithEncoding()
+ {
+ #region TempFileCreateWithEncoding
+
+ using var temp = TempFile.Create(".txt", Encoding.UTF8);
+
+ File.Exists(temp.Path);
+
+ #endregion
+ }
+
+ [Fact]
+ public async Task CreateText()
+ {
+ #region TempFileCreateText
+
+ using var temp = await TempFile.CreateText("Hello, World!");
+
+ var content = await File.ReadAllTextAsync(temp);
+ Assert.Equal("Hello, World!", content);
+
+ #endregion
+ }
+
+ [Fact]
+ public async Task CreateTextWithExtension()
+ {
+ #region TempFileCreateTextWithExtension
+
+ var json = """
+ {
+ "name": "test",
+ "value": 123
+ }
+ """;
+
+ using var temp = await TempFile.CreateText(json, ".json");
+
+ var content = await File.ReadAllTextAsync(temp);
+ Assert.Equal(json, content);
+
+ #endregion
+ }
+
+ [Fact]
+ public async Task CreateTextWithEncoding()
+ {
+ #region TempFileCreateTextWithEncoding
+
+ using var temp = await TempFile.CreateText(
+ "Content with special chars: äöü",
+ ".txt",
+ Encoding.UTF8);
+
+ var content = await File.ReadAllTextAsync(temp, Encoding.UTF8);
+ Assert.Equal("Content with special chars: äöü", content);
+
+ #endregion
+ }
+
+
+
+ [Fact]
+ public async Task CreateBinary()
+ {
+ #region TempFileCreateBinary
+
+ byte[] data = [0x01, 0x02, 0x03, 0x04];
+
+ using var temp = await TempFile.CreateBinary(data);
+
+ var readData = await File.ReadAllBytesAsync(temp);
+ Assert.Equal(data, readData);
+
+ #endregion
+ }
+
+ [Fact]
+ public async Task CreateBinaryWithExtension()
+ {
+ #region TempFileCreateBinaryWithExtension
+
+ byte[] data = [0x01, 0x02, 0x03, 0x04];
+ using var temp = await TempFile.CreateBinary(data, ".bin");
+
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/src/Verify/TempFile.cs b/src/Verify/TempFile.cs
index 33d9f5644..c6dc7305e 100644
--- a/src/Verify/TempFile.cs
+++ b/src/Verify/TempFile.cs
@@ -146,6 +146,43 @@ public TempFile(string? extension = null)
}
}
+ ///
+ /// Creates a new temporary file with optional extension and encoding.
+ ///
+ ///
+ /// Optional file extension (e.g., ".txt", ".json").
+ /// If not provided, no extension is added.
+ /// The extension should include the leading dot.
+ ///
+ ///
+ /// Optional text encoding to use when creating the file.
+ /// If provided and the extension is recognized as a text format, the file will be created with the appropriate BOM.
+ /// If null, the file is created as an empty binary file.
+ ///
+ ///
+ /// A new instance representing the created file.
+ /// The caller is responsible for disposing the instance to ensure cleanup.
+ ///
+ ///
+ /// The file is created immediately upon calling this method. If encoding is specified and the extension
+ /// is recognized as a text format, the file will be initialized with the appropriate byte order mark (BOM).
+ /// Otherwise, an empty file is created.
+ ///
+ ///
+ /// Thrown if the file cannot be created (e.g., due to permissions or disk space).
+ ///
+ ///
+ ///
+ /// // Create empty temp file
+ /// using var temp = TempFile.Create();
+ ///
+ /// // Create temp file with extension
+ /// using var txtFile = TempFile.Create(".txt");
+ ///
+ /// // Create temp file with UTF-8 encoding and BOM
+ /// using var utf8File = TempFile.Create(".txt", Encoding.UTF8);
+ ///
+ ///
public static TempFile Create(string? extension = null, Encoding? encoding = null)
{
var file = new TempFile(extension);
@@ -159,6 +196,127 @@ public static TempFile Create(string? extension = null, Encoding? encoding = nul
return file;
}
+ ///
+ /// Creates a new temporary file with the specified text content.
+ ///
+ ///
+ /// The text content to write to the file.
+ ///
+ ///
+ /// Optional file extension (e.g., ".txt", ".json", ".xml").
+ /// If not provided, no extension is added.
+ /// The extension should include the leading dot.
+ ///
+ ///
+ /// Optional text encoding to use when writing the content.
+ /// If null, the default UTF-8 encoding without BOM is used.
+ ///
+ ///
+ /// A task that represents the asynchronous operation.
+ /// The task result contains a new instance with the written content.
+ /// The caller is responsible for disposing the instance to ensure cleanup.
+ ///
+ ///
+ ///
+ /// The file is created and written asynchronously. The content is written using the specified
+ /// encoding, or UTF-8 without BOM if no encoding is specified.
+ ///
+ ///
+ /// This method is ideal for creating temporary test data files, configuration files, or
+ /// any text-based content that needs to be written to disk for testing purposes.
+ ///
+ ///
+ ///
+ /// Thrown if the file cannot be created or written to (e.g., due to permissions or disk space).
+ ///
+ ///
+ ///
+ /// // Create temp file with simple text
+ /// using var temp = await TempFile.CreateText("Hello, World!");
+ ///
+ /// // Create JSON file
+ /// using var json = await TempFile.CreateText(
+ /// "{\"name\": \"test\"}",
+ /// ".json");
+ ///
+ /// // Create file with specific encoding
+ /// using var utf8 = await TempFile.CreateText(
+ /// "Content with special chars: äöü",
+ /// ".txt",
+ /// Encoding.UTF8);
+ ///
+ ///
+ public static async Task CreateText(string content, string? extension = null, Encoding? encoding = null)
+ {
+ var file = new TempFile(extension);
+
+ if (encoding == null)
+ {
+ await File.WriteAllTextAsync(file, content);
+ }
+ else
+ {
+ await File.WriteAllTextAsync(file, content, encoding);
+ }
+
+ return file;
+ }
+
+ ///
+ /// Creates a new temporary file with the specified binary content.
+ ///
+ ///
+ /// The binary content to write to the file.
+ /// Can be empty to create an empty file.
+ ///
+ ///
+ /// Optional file extension (e.g., ".bin", ".dat", ".png").
+ /// If not provided, no extension is added.
+ /// The extension should include the leading dot.
+ ///
+ ///
+ /// A task that represents the asynchronous operation.
+ /// The task result contains a new instance with the written content.
+ /// The caller is responsible for disposing the instance to ensure cleanup.
+ ///
+ ///
+ ///
+ /// The file is created and written asynchronously with the exact binary content provided.
+ /// This method is suitable for creating temporary files containing binary data such as
+ /// images, serialized objects, or any non-text data.
+ ///
+ ///
+ /// The content is written as-is without any encoding or transformation.
+ ///
+ ///
+ ///
+ /// Thrown if the file cannot be created or written to (e.g., due to permissions or disk space).
+ ///
+ ///
+ ///
+ /// // Create temp file with binary data
+ /// byte[] data = [0x01, 0x02, 0x03, 0x04];
+ /// using var temp = await TempFile.CreateBinary(data);
+ ///
+ /// // Create image file
+ /// byte[] imageData = await File.ReadAllBytesAsync("source.png");
+ /// using var image = await TempFile.CreateBinary(imageData, ".png");
+ ///
+ /// // Create empty binary file
+ /// using var empty = await TempFile.CreateBinary(
+ /// ReadOnlyMemory<byte>.Empty,
+ /// ".bin");
+ ///
+ ///
+ public static async Task CreateBinary(ReadOnlyMemory content, string? extension = null)
+ {
+ var file = new TempFile(extension);
+
+ await File.WriteAllBytesAsync(file, content);
+
+ return file;
+ }
+
public void Dispose()
{
if (File.Exists(Path))