Contents

Attachments

Attach values to tests to help diagnose issues and gather feedback.

Overview

Attach values such as strings and files to tests. Implement the Attachable protocol to create your own attachable types.

Attach data or strings

If your test produces encoded data that you want to save as an attachment, you can call record(_:named:sourceLocation:).

struct SalesReport { ... }

@Test func `sales report adds up`() async throws {
  let salesReport = await generateSalesReport()
  try salesReport.validate()
  let bytes: [UInt8] = try salesReport.convertToCSV()
  Attachment.record(bytes, named: "sales report.csv")
}

You can attach an instance of Array<UInt8>, ContiguousArray<UInt8>, ArraySlice<UInt8>, or Data because these types automatically conform to Attachable.

You can also attach an instance of String or Substring. The testing library treats attached strings as UTF-8 text files. If you want to save a string as an attachment using a different encoding, convert it to Data using data(using:allowLossyConversion:) and attach the resulting data instead of the original string.

Attach encodable values

If you have a value you want to save as an attachment that conforms to either Encodable or NSSecureCoding, you can extend it to add conformance to Attachable. When you import the Foundation module, the testing library automatically provides a default implementation of Attachable to types that also conform to Encodable or NSSecureCoding.

import Testing
import Foundation

struct SalesReport { ... }
extension SalesReport: Encodable, Attachable {}

@Test func `sales report adds up`() async throws {
  let salesReport = await generateSalesReport()
  try salesReport.validate()
  Attachment.record(salesReport, named: "sales report.json")
}

Attach files and directories

If you have a file you want to save as an attachment, you can attach it using its file URL. The testing library needs to read or map the file before attaching it to your test, and those operations can fail, so you need to explicitly create an instance of Attachment before you record it.

import Foundation

@Test func `sales report adds up`() async throws {
  let salesReport = await generateSalesReport()
  try salesReport.validate()
  let salesReportURL = try salesReport.save()
  let attachment = try await Attachment(contentsOf: salesReportURL)
  Attachment.record(attachment)
}

You can also attach a directory to a test using its file URL. When you attach a directory to a test, the testing library creates a ZIP file containing the directory’s contents, then attaches that ZIP file in place of the directory.

Attach images

You can attach instances of the following system-provided image types to a test:

Platform

Supported types

macOS

Cgimage, Ciimage, Nsimage

iOS, tvOS, and visionOS

Cgimage, Ciimage, Uiimage

watchOS

Cgimage, Uiimage

Windows

Bitmaps, Icons, Nn Wincodec Iwicbitmapsource (including its subclasses declared by Windows Imaging Component)

When you attach an image to a test, you can specify the image format to use in addition to a preferred name.

struct SalesReport { ... }

@Test func `sales report adds up`() async throws {
  let salesReport = await generateSalesReport()
  let image = try salesReport.renderTrendsGraph()
  Attachment.record(image, named: "sales report", as: .png)
}

If you don’t specify an image format when attaching an image to a test, the testing library selects the format to use based on the preferred name you pass.

Attach other values

If you have a value that needs a custom encoded representation when you save it as an attachment, implement withUnsafeBytes(for:_:). The implementation of this function calls its body argument and passes the encoded representation of self or, if a failure occurs, throws an error representing that failure.

struct SalesReport { ... }

extension SalesReport: Attachable {
  borrowing func withUnsafeBytes<R>(
    for attachment: borrowing Attachment<Self>,
    _ body: (UnsafeRawBufferPointer) throws -> R
  ) throws -> R {
    let bytes = try salesReport.convertToCSV() // might fail to convert to CSV
    try bytes.withUnsafeBytes { buffer in // rethrows any error from `body`
      try body(buffer)
    }
  }
}

If your type conforms to Sendable, the testing library avoids calling this function until it needs to save the attachment. If your type doesn’t conform to Sendable, the testing library calls this function as soon as you record the attachment.

Customize attachment behavior

If you can reliably estimate in advance how large the encoded representation will be, implement estimatedAttachmentByteCount. The testing library uses the value of this property as a hint to optimize memory and disk usage.

extension SalesReport: Attachable {
  ...

  var estimatedAttachmentByteCount: Int? {
    return self.entries.count * 123
  }
}

You can also implement preferredName(for:basedOn:) if you want to customize the name of the attachment when saving it.

extension SalesReport: Attachable {
  ...

  borrowing func preferredName(
    for attachment: borrowing Attachment<Self>,
    basedOn suggestedName: String
  ) -> String {
    if suggestedName.contains(".") {
      // The name already contains a path extension, so don't append another.
      return suggestedName
    }

    // Append ".csv" to the name so the resulting file opens as a spreadsheet.
    return "\(suggestedName).csv"
  }
}

Inspect attachments after a test run ends

By default, the testing library saves your attachments as soon as you call record(_:sourceLocation:) or record(_:named:sourceLocation:). You can access saved attachments after your tests finish running:

  • When using Xcode, you can access attachments from the test report.

  • When using Visual Studio Code, the testing library saves attachments to .build/attachments by default. Visual Studio Code reports the paths to individual attachments in its Tests Results panel.

  • When using Swift Package Manager’s swift test command, you can pass the --attachments-path option. The testing library saves attachments to the specified directory.

    If you do not pass the --attachments-path option, the testing library does not save any attachments you record.

Topics

Attaching values to tests

Attaching images to tests