Sitelet https://github.com/fluentassertions/fluentassertions/pull/3295/files
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions Src/FluentAssertions/Specialized/JsonNodeAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ protected override string Identifier
public AndWhichConstraint<JsonNodeAssertions<T>, JsonNode> HaveProperty(string code,
[StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs)
{
bool hasProperty = Subject is JsonObject obj && obj.TryGetPropertyValue(code, out _);
JsonNode property = null;
bool hasProperty = Subject is JsonObject obj && obj.TryGetPropertyValue(code, out property);

CurrentAssertionChain
.BecauseOf(because, becauseArgs)
Expand All @@ -52,7 +53,7 @@ public AndWhichConstraint<JsonNodeAssertions<T>, JsonNode> HaveProperty(string c
.ForCondition(hasProperty)
.FailWith("Expected {context:JSON node} to have property {0}{reason}.", code);

return new AndWhichConstraint<JsonNodeAssertions<T>, JsonNode>(this, Subject?[code]);
return new AndWhichConstraint<JsonNodeAssertions<T>, JsonNode>(this, property);
}

/// <summary>
Expand Down Expand Up @@ -204,12 +205,13 @@ public AndConstraint<JsonNodeAssertions<T>> NotBeNumeric(
public AndWhichConstraint<JsonNodeAssertions<T>, DateTime> BeLocalDate(
[StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs)
{
DateTime actualValue = default;

CurrentAssertionChain
.ForCondition(Subject is JsonValue value && value.TryGetValue<DateTime>(out _) && !Subject.ToString().EndsWith('Z'))
.ForCondition(Subject is JsonValue value && value.TryGetValue(out actualValue) && !Subject.ToString().EndsWith('Z'))
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:JSON node} to be a local date{reason}, but {0} is not.", Subject);

var actualValue = Subject is JsonValue jsonValue && jsonValue.TryGetValue<DateTime>(out var result) ? result : default;
return new AndWhichConstraint<JsonNodeAssertions<T>, DateTime>(this, actualValue);
}

Expand Down Expand Up @@ -248,12 +250,13 @@ public AndConstraint<JsonNodeAssertions<T>> NotBeLocalDate(
public AndWhichConstraint<JsonNodeAssertions<T>, DateTime> BeUtcDate(
[StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs)
{
DateTime actualValue = default;

CurrentAssertionChain
.ForCondition(Subject is JsonValue value && value.TryGetValue<DateTime>(out _) && Subject.ToString().EndsWith('Z'))
.ForCondition(Subject is JsonValue value && value.TryGetValue(out actualValue) && Subject.ToString().EndsWith('Z'))
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} to be a UTC date{reason}, but {0} is not.", Subject);

var actualValue = Subject is JsonValue jsonValue && jsonValue.TryGetValue<DateTime>(out var result) ? result : default;
return new AndWhichConstraint<JsonNodeAssertions<T>, DateTime>(this, actualValue);
}

Expand Down Expand Up @@ -292,12 +295,13 @@ public AndConstraint<JsonNodeAssertions<T>> NotBeUtcDate(
public AndWhichConstraint<JsonNodeAssertions<T>, bool> BeBool(
[StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs)
{
bool actualValue = default;

CurrentAssertionChain
.ForCondition(Subject is JsonValue value && value.TryGetValue(out bool _))
.ForCondition(Subject is JsonValue value && value.TryGetValue(out actualValue))
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context} to be a boolean{reason}, but {0} is not.", Subject);

var actualValue = Subject is JsonValue jsonValue && jsonValue.TryGetValue<bool>(out var result) && result;
return new AndWhichConstraint<JsonNodeAssertions<T>, bool>(this, actualValue);
}

Expand Down Expand Up @@ -335,12 +339,13 @@ public AndConstraint<JsonNodeAssertions<T>> NotBeBool(
public AndWhichConstraint<JsonNodeAssertions<T>, string> BeString(
[StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs)
{
string actualValue = null;

CurrentAssertionChain
.ForCondition(Subject is JsonValue value && value.TryGetValue(out string _))
.ForCondition(Subject is JsonValue value && value.TryGetValue(out actualValue))
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:JSON node} to be a string{reason}, but {0} is not.", Subject);

var actualValue = Subject is JsonValue jsonValue && jsonValue.TryGetValue<string>(out var result) ? result : null;
return new AndWhichConstraint<JsonNodeAssertions<T>, string>(this, actualValue);
}

Expand Down
18 changes: 18 additions & 0 deletions Tests/FluentAssertions.Specs/Specialized/JsonNodeSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Text.Json.Nodes;
using FluentAssertions.Execution;
using FluentAssertions.Extensions;
using Xunit;
using Xunit.Sdk;
Expand Down Expand Up @@ -39,6 +40,23 @@ public void Can_continue_with_the_value_of_a_property()
subject.ToString().Should().Be("John");
}

[Fact]
public void Arrays_do_not_have_properties()
{
// Arrange
var jsonNode = JsonNode.Parse("[1, 2, 3]");

// Act
var act = () =>
{
using var _ = new AssertionScope();
return jsonNode.Should().HaveProperty("code", "that is what we expect");
};

// Assert
act.Should().Throw<XunitException>();
Comment thread
jnyrup marked this conversation as resolved.
}

[Fact]
public void Can_ensure_a_property_does_not_exist()
{
Expand Down
1 change: 1 addition & 0 deletions docs/_pages/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ sidebar:

### Fixes
* `JsonNodeAssertions.HaveProperty`/`NotHaveProperty` now correctly distinguish an absent property from one with an explicit `null` value - [#3282](https://github.com/fluentassertions/fluentassertions/pull/3282)
* `JsonNodeAssertions.HaveProperty` no longer throws an `InvalidOperationException` when wrapped in an `AssertionScope` and invoked on a non-`JsonObject` subject - [#3295](https://github.com/fluentassertions/fluentassertions/pull/3295)

## 8.9.0

Expand Down
Loading