tpierrain/NFluent

Is there a possibility to control the checked values?

dnperfors opened this issue · 3 comments

Expected Behavior

I am writing some custom checks where I would like to have more control over the checked values. This might be benifitial for the ContainsKey checks as well, so I use that as an example.

When I write the following check:

var dictionary = new Dictionary<string, string>
{
    ["Key1"] = "Value1",
    ["Key2"] = "Value2",
};

Check.That(dictionary).ContainsKey("MyKey");

I want to have the following message:

The checked dictionary does not contain the expected key.
    The checked dictionary keys:
    	{Key1, Key2} (2 items)
    Expected key:
    	["MyKey"]

Current Behavior

Currently I get the following error message:

The checked dictionary does not contain the expected key.
    The checked dictionary:
    	{[Key1, Value1], [Key2, Value2]} (2 items)
    Expected key:
    	["MyKey"]

Possible Solution

For the expected value I can already use the method DefineExpectedResult.
Would it be possible to get a DefineCheckedValues, or a way to add a formatter for the checked value?

Context

As said, I am writing some custom checks. These checks are around the HttpResponseMessage where I want to check for specific header names, but I don't want to show the value of the header. However, in other tests I do want to show the header value, but now I only see "{[Header, System.String[]]} (1 item)"

Your Environment

  • Version used: 2.7.0
  • Framework version: Net Standard 2.0

Hello
have you tried using CheckSutAttribute [see wiki]?(https://github.com/tpierrain/NFluent/wiki/advanced-checks#checksutattribute).
This could help you get closer to what you are trying to achieve.

That is indeed what I ended up doing, however it forced me to re-implement the checks and I couldn't use some extension methods I have to check that specific the specific key is actually in there.

So instead of saying:

.FailWhen(sut => !sut.ContainsKey(expectedKey), "Key not found.")
.DefineCheckedValues(sut => sut.Select(x => x.Key))

I had to do:

.CheckSutAttributes(sut => sut.Select(x => x.Key).ToArray(), "keys")
.FailWhen(sut => !sut.Any(x => x == expectedKey), "Key not found.")

The advantage of the first version is that you can expand the check to also check the values, which is not as easy as the second version.

Thanks for your input. I am still considering how to properly address this. I must say I have yet to come with a more convenient solution.
The current behaviour is intentional: it ensures a consistent reporting of the sut disregarding of the used check.
Rationale: a failing check is the signal of some expectations on production code not being met, but one can rarely identify why the expectation was not met. So NFluent aims at providing as much information as possible regarding the sut.
But when we can provide some focus or extra information which could help, the best way is add it to the initial error message.

Based on your example, you can achieve this with Analyze (see wiki) which gives you the maximum freedom:

 .Analyze((sut, context) =>
  {
    if (!sut.ContainsKey(x))
    {
      context.Fails($"Key {x} not found. Existing keys:{string.Join(',', sut.Select( t => t.Key))}.");
    }
    if (sut[x] != xValue)
    {
       context.Fails("The entry does not have the expected value: {sut[x]} instead of {xValue}."):
    } 
  }).EndCheck();

What do you think?
Note: I have not tested this code, so there may be bugs, but this is the gist of it.