Skip to content

Allow an argument to match a type formatter by its interface/base class/generic definition #644

Description

@delatrie

The type formatters API allows users to provide their own serialization algorithms to format arguments of certain types. To do so, two things are required:

  1. Define a class that derives from Allure.Net.Commons.TypeFormatter<T>.
  2. Call Allure.Net.Commons.AllureLifecycle.AddTypeFormatter<T>(Allure.Net.Commons.TypeFormatter<T> formatter) early enough for the formatter to be picked up by Allure.

If everything is done right, Allure will use the formatter to convert arguments of the matched type to strings in two contexts:

  • when converting test method arguments to strings (usually, at test end)
  • when convertins arguments of attribute-based steps at step end

Limitations

If we'd like to format a value of type T, then:

  1. T must be known at compile time
  2. T must be accessible
  3. T must be the exact type of the value (i.e., value.GetType() == typeof(T) must be true)

This is cumbersome in the following situations:

  1. T is a private or internal type of a 3rd party library
  2. There are a lot of types we'd like to format similarly but have to call AddTypeFormater for each of them anyway.

Solution

A solution is to make the matching algorithm less restrictive. We can try to find a match in the following order:

  1. TypeFormatter<T>.
  2. TypeFormatter<G>, where G is a generic type definition of a (bound) generic type T.
  3. TypeFormatter<I>, where I is a type of an interface implemented by T.
  4. TypeFormatter<IG>, where IG is a generic type definition of an interface implemented by T.
  5. TypeFormatter<B>, where B is a base class of T.
  6. TypeFormatter<GB>, where GB is a generic type definition of the base class of T.

Options 2, 4, and 6 require a new AddTypeFormatter overload since these formatters can't be added with the existing one.

Options 5 and 6 apply repeatedly until we reach System.Object.

If the match result is found, we can cache it by adding the [value.GetType(), formatter] entry to the dictionary of formatters.

Activity

  1. added theissue type on Dec 29, 2025
  2. Riverlution commented on Feb 23, 2026

    @Riverlution

    This would be very useful for us too. I was about to raise a separate enhancement before finding this issue.

    Minimal Repro

    using System;
    using Allure.Net.Commons;
    using Allure.NUnit.Attributes;
    using NUnit.Framework;
    
    public class Repro
    {
        [AllureStep("Run noisy step")]
        public void Run(Action callback)
        {
            callback();
        }
    
        [Test]
        public void ShowsHugeParameterPayload()
        {
            Run(() => { });
        }
    }
    

    Observed:

    • callback parameter is extremely large (delegate internals / runtime object graph)

    Expected:

    • Ability to define a global/fallback formatter for unmatched types, e.g. "" or truncated text

    Why this matters:

    • Without assignable/fallback matching, consumers need reflection-heavy custom code to discover concrete runtime types and register formatters one-by-one.
  3. added 2 commits that reference this issue on Jul 27, 2026
    ddff637
    c3118ab
  4. NoTh0ughts commented on Jul 28, 2026

    @NoTh0ughts

    As a workaround, you can override JsonConvert.DefaultSettings locally in OneTimeSetup with useful fields such as MaxDepth and Converters.

    JsonConvert.DefaultSettings = () => new JsonSerializerSettings
    {
           MaxDepth = 3,
           Converters = { /* ... */ },
    };
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions