Skip to content

DateTimes Formatting / Serializing Issue #1441

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
JoshHariz opened this issue Apr 17, 2025 · 13 comments
Closed

DateTimes Formatting / Serializing Issue #1441

JoshHariz opened this issue Apr 17, 2025 · 13 comments

Comments

@JoshHariz
Copy link

JoshHariz commented Apr 17, 2025

Describe the bug

When datetimes are serialized, they are using the below format which is not ISO 8601 as the docs say.

Output:

    "DateCreated": "2025-04-16 23:50:34.0967133 Utc",
    "MessageDate": "2025-03-27 16:04:50.129 Utc",

Verify Settings:

[ModuleInitializer]
public static void Initialize()
{
    // Formats test failure messages
    VerifyDiffPlex.Initialize(VerifyTests.DiffPlex.OutputType.Compact); 

    // Saves all Verify result files in a results directory
    Verifier.UseSourceFileRelativeDirectory("Results");
}

Version:

		<PackageReference Include="Verify" Version="29.2.0" />
		<PackageReference Include="Verify.DiffPlex" Version="3.1.2" />
		<PackageReference Include="Verify.XunitV3" Version="29.2.0" />

Minimum Repro:

Input

return Verify(DateTime.Now)
    .DontScrubDateTimes()
    .UseStrictJson();

Expected Output:
2025-04-17T00:41:27.7349436Z

Actual Output
2025-04-17 00:41:27.7349436 Utc

I dont think I'm doing anything outside of the defaults so should be trivial to replicate. If I'm missing something please let me know.

Thanks!

@SimonCropp
Copy link
Member

where do the docs say ISO 8601?

@JoshHariz
Copy link
Author

I've been looking deep into the docs trying to understand the datetime serialization and I think I assumed it was all from Verify. It appears that the exact statement I was thinking of is from Argon.

Argon is referenced often in spots like
https://github.com/VerifyTests/Verify/blob/main/docs/serializer-settings.md#default-settings
https://github.com/VerifyTests/Verify/blob/main/docs/serializer-settings.md#serializer-settings

And the Argon docs say that it uses ISO-8601
https://github.com/SimonCropp/Argon/blob/main/docs/DatesInJSON.md

So by inference I figured it would be the same, but I guess that was my mistake. Either way I've figured out the TreatAsString method and got that working to format the datetimes in ISO-8601 :)

Here's the code I got working if anyone faces the same issue as me in the future

VerifierSettings.TreatAsString<DateTime>((date, _) => JsonSerializer.Serialize(date));
VerifierSettings.TreatAsString<DateTimeOffset>((date, _) => JsonSerializer.Serialize(date));

return Verify(DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(9.5)))
    .DontScrubDateTimes()
    .UseStrictJson();

@JoshHariz
Copy link
Author

JoshHariz commented Apr 17, 2025

Wait I take it back it isn't resolved, the TreatAsString works fine when I pass it just a DateTime but when I have an object which contains a datetime it doesnt work.

Setup

VerifierSettings.TreatAsString<DateTime>((date, _) => JsonSerializer.Serialize(date));
VerifierSettings.TreatAsString<DateTimeOffset>((date, _) => JsonSerializer.Serialize(date));

// actualSentMessage is an object which contains datetimes with path actualSentMessage.Metadata.DateCreated
await Verify(actualSentMessage)
    .UseFileName($"{Path.GetFileNameWithoutExtension(source)}_Canonical")
    .DontScrubDateTimes()
    .DontScrubGuids()
    .UseStrictJson();

Expected Output

    "DateCreated": "2025-04-17T03:52:20.83734Z",
    "MessageDate": "2025-03-27T16:04:50.129Z",

Actual Output

    "DateCreated": "2025-04-17 03:52:20.83734 Utc",
    "MessageDate": "2025-03-27 16:04:50.129 Utc",

@SimonCropp
Copy link
Member

@SimonCropp
Copy link
Member

And the Argon docs say that it uses ISO-8601

Verify has custom serialization for dates

@SimonCropp
Copy link
Member

out of curiosity why do u not want to scrub those datetimes? dont they change every time the test runs

if they are consistent, perhaps the Named Date and Times feature would make more sense https://github.com/VerifyTests/Verify/blob/main/docs/dates.md#named-date-and-times

@JoshHariz
Copy link
Author

JoshHariz commented Apr 17, 2025

I see so TreatAsString only works for when you directly pass a datetime in, it doesnt work for properties.

So is there no way to set the ISO format for the output?

The reason I'd like to do this is so that we can take the outputs of some tests and use it to test/debug other parts of our system. Currently we have to manually fixup the dates to valid json before they can be used. I have Explicit tests which are only run to retrieve the output, we scrub the datetimes for our standard tests.

@SimonCropp
Copy link
Member

can u share the full code of one of your explicit tests?

@SimonCropp
Copy link
Member

bump

@JoshHariz
Copy link
Author

Apologies, been away over the Easter weekend. Here is a sample, nothing too fancy.

/// <summary>
/// This test will generate a working canonical that can be used for testing the target-side system. The date formats will need to be slightly modified to work.
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
[Theory(DisplayName = "", Explicit = true)]
//[MemberData(nameof(ValidPayloads))]
[InlineData(TestInputs.CompGradeChange)]
public async Task CreateWorkingCanonical(string source)
{
    // Arrange
    EnvelopeCanonical? actualSentMessage = null;
    // Custom class
    Mock<IMessageManager> messageManagerMock = new();
    messageManagerMock.Setup(
        x => x.SendToTopic(It.IsAny<EnvelopeCanonical>())
        .Callback<EnvelopeCanonical>(
            message => actualSentMessage = message);

    var inputRequest = TestDataFactory.CreateHttpRequest(TestInputs.ReadFile(source));

    // Act
    await payrollFunction.Run(inputRequest);

    // Assert
    await Verify(actualSentMessage)
        .UseFileName($"{Path.GetFileNameWithoutExtension(source)}_Canonical")
        .DontScrubDateTimes()
        .DontScrubGuids()
        .UseStrictJson();
}

@SimonCropp
Copy link
Member

IMO you should use this approach

[Theory(DisplayName = "", Explicit = true)]
//[MemberData(nameof(ValidPayloads))]
[InlineData(TestInputs.CompGradeChange)]
public async Task CreateWorkingCanonical(string source)
{
    // Arrange
    EnvelopeCanonical? actualSentMessage = null;
    // Custom class
    Mock<IMessageManager> messageManagerMock = new();
    messageManagerMock.Setup(
        x => x.SendToTopic(It.IsAny<EnvelopeCanonical>())
        .Callback<EnvelopeCanonical>(
            message => actualSentMessage = message);

    var inputRequest = TestDataFactory.CreateHttpRequest(TestInputs.ReadFile(source));

    // Act
    await payrollFunction.Run(inputRequest);

    string json = System.Text.Json.JsonSerializer.Serialize(actualSentMessage);

    // Assert
    await Verify(json)
        .UseFileName($"{Path.GetFileNameWithoutExtension(source)}_Canonical");
}

@JoshHariz
Copy link
Author

Thanks very much that seems to meet my requirements. Appreciate your help and you shining some light in this area :)

@SimonCropp
Copy link
Member

glad u found a way forward

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants