HaveElementWithValue?
Hi,
I wish there was a way to assert a JObject contains an element with a value as specified.
// arrange
...
var expectedTokenKey = "token";
var expectedTokenValue = "1234";
// act
JObject json = await ...GetJsonAsync();
// assert
json.Should().HaveElementWithValue(expectedTokenKey, expectedTokenValue);
Is there another better way for that I'm unaware of?
No, but we do accept contributions ;-)
In the FluentAssertions core library I can only think of
GenericDictionaryAssertions.Contain(TKey key, TValue value) that does a similar combined assertion.
dictionary.Should().Contain(1, "One");
which could also be written as
dictionary.Should().ContainKey(1)
.WhichValue.Should().Be("One");
In FluentAssertions.Json the proposed HaveElementAndValue(string expectedElement, string expectedValue)
var subject = JToken.Parse("{ 'id': 42 }");
subject.Should().HaveElementAndValue("id", "42");
can also be written as either
var subject = JToken.Parse("{ 'id': 42 }");
subject.Should().HaveElement("id")
.Which.Should().HaveValue("42");
or
var subject = JToken.Parse("{ 'id': 42 }");
subject.Should().HaveElement("id")
.Which.Value<string>().Should().Be("42");
Using the existing Which pattern has two benefits:
- has no implicit assumptions that the
Valueof theidelement is convertible tostring - the same code pattern can be used for other expectations than
string.
var subject = JToken.Parse("{ 'id': [42] }");
subject.Should().HaveElement("id")
.Which.Values<int>().Should().ContainSingle()
.Which.Should().Be(42);
Not only do I like it, but I wasn't even aware of the Which operator, which I was so anxiously looking for 😳
Thank you 💐
Related: https://github.com/fluentassertions/fluentassertions/issues/1355.
But does that mean we still see value in #39?
I haven't really used FA.Json that much, but I'm not seeing cases for HaveElementAndValue that aren't already covered by the existing API.
The use case I encountered is just like you described with the dictionary value. I get json from HTTP API server. I want to verify the json contains a specified key and value.
But would subject.Should().HaveElement("id").Which.Value<string>().Should().Be("42"); work for you?
It actually would. A syntax sugar would be nice tho. Like in dictionary, as Jonas mentioned.
I haven't dug into the history of Contain(TKey, TValue), so I'm possibly missing some historical context, but I'm not that big fan of it, as it:
- tries to assert multiple things at once, and
- can be accomplished using the
WhichValue
To be clear, the existence of Contain(TKey, TValue) (in my opinion) is not the right argument to use to justify the addition of HaveElementAndValue.
But I don't mind it either. Having a useful shortcut is fine by me.
But I don't mind it either. Having a useful shortcut is fine by me.
Then we're left with the naming to be decided. Let's continue this conversation in #40.
The naming is fine by me.
I'm a bit confused whether we're agreeing on HaveElementWithValue or HaveElementAndValue?
I think there's two discussions:
- Whether or not to add this API at all.
- What the name should be.
I know that we can accomplish the same with a combination of HaveElement and Which, but I don't mind adding this new member. I would prefer to name it HaveElementWithValue.
After a nights sleep, I'm fine adding it.
I agree that HaveElementWithValue is preferable to HaveElementAndValue.
Regarding the method signature, I'm looking at https://en.wikipedia.org/wiki/JSON#Data_types_and_syntax to get a picture of possible scenarios.
- Should it be generic
HaveElementWithValue<T>(string expectedElement, T expectedValue)?- That won't work for Array type, as those should be extracted using
Values<T>instead ofValue<T>
- That won't work for Array type, as those should be extracted using
- Should we have
bool,double,stringoverloads - Should it be able to handle Object
Json is text only, so why don't we stick to a string and a Jtoken overload?
- Should it be generic
HaveElementWithValue<T>(string expectedElement, T expectedValue)?
I like this.
Json is text only, so why don't we stick to a string and a Jtoken overload?
Eventho JSON is text only, 5 isn't recognized as "5". Similarly null != "null", in the JSON.