Skip to content
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

Add Have/NotHaveSameCount for Dictionaries #1178

Merged
merged 3 commits into from
Nov 11, 2019
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
82 changes: 82 additions & 0 deletions Src/FluentAssertions/Collections/GenericDictionaryAssertions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
Expand Down Expand Up @@ -242,6 +243,87 @@ public AndConstraint<GenericDictionaryAssertions<TKey, TValue>> HaveCount(Expres
return new AndConstraint<GenericDictionaryAssertions<TKey, TValue>>(this);
}

/// <summary>
/// Assert that the current dictionary has the same number of elements as <paramref name="otherCollection" />.
/// </summary>
/// <param name="otherCollection">The other collection with the same expected number of elements</param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <see cref="because" />.
/// </param>
public AndConstraint<GenericDictionaryAssertions<TKey, TValue>> HaveSameCount(IEnumerable otherCollection, string because = "",
dennisdoomen marked this conversation as resolved.
Show resolved Hide resolved
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(otherCollection, nameof(otherCollection), "Cannot compare dictionary count against a <null> collection.");

if (Subject is null)
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:dictionary} to have the same count as {0}{reason}, but found {1}.",
otherCollection,
Subject);
}

int actualCount = Subject.Count;
int expectedCount = otherCollection.Cast<object>().Count();

Execute.Assertion
.ForCondition(actualCount == expectedCount)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:dictionary} to have {0} item(s){reason}, but count is {1}.", expectedCount, actualCount);

return new AndConstraint<GenericDictionaryAssertions<TKey, TValue>>(this);
}

/// <summary>
/// Assert that the current collection does not have the same number of elements as <paramref name="otherCollection" />.
/// </summary>
/// <param name="otherCollection">The other collection with the unexpected number of elements</param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <see cref="because" />.
/// </param>
public AndConstraint<GenericDictionaryAssertions<TKey, TValue>> NotHaveSameCount(IEnumerable otherCollection, string because = "",
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(otherCollection, nameof(otherCollection), "Cannot compare dictionary count against a <null> collection.");

if (Subject is null)
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:dictionary} to not have the same count as {0}{reason}, but found {1}.",
otherCollection,
Subject);
}

if (ReferenceEquals(Subject, otherCollection))
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:dictionary} {0} to not have the same count as {1}{reason}, but they both reference the same object.",
Subject,
otherCollection);
}

int actualCount = Subject.Count;
int expectedCount = otherCollection.Cast<object>().Count();

Execute.Assertion
.ForCondition(actualCount != expectedCount)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:dictionary} to not have {0} item(s){reason}, but count is {1}.", expectedCount, actualCount);

return new AndConstraint<GenericDictionaryAssertions<TKey, TValue>>(this);
}

#endregion

#region BeEmpty
Expand Down
213 changes: 213 additions & 0 deletions Tests/Shared.Specs/GenericDictionaryAssertionSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,219 @@ public void When_dictionary_count_is_less_or_equal_to_and_dictionary_is_null_it_

#endregion

#region Have Same Count

[Fact]
public void When_dictionary_and_collection_have_the_same_number_elements_it_should_succeed()
{
// Arrange
var dictionary = new Dictionary<int, string>
{
[1] = "One",
[2] = "Two",
[3] = "Three"
};
IEnumerable collection = new[] { 4, 5, 6 };

// Act / Assert
dictionary.Should().HaveSameCount(collection);
}

[Fact]
public void When_dictionary_and_collection_do_not_have_the_same_number_of_elements_it_should_fail()
{
// Arrange
var dictionary = new Dictionary<int, string>
{
[1] = "One",
[2] = "Two",
[3] = "Three"
};
IEnumerable collection = new[] { 4, 6 };

// Act
Action act = () => dictionary.Should().HaveSameCount(collection);

// Assert
act.Should().Throw<XunitException>().WithMessage(
"Expected dictionary to have 2 item(s), but count is 3.");
}

[Fact]
public void When_comparing_item_counts_and_a_reason_is_specified_it_should_it_in_the_exception()
{
// Arrange
var dictionary = new Dictionary<int, string>
{
[1] = "One",
[2] = "Two",
[3] = "Three"
};
IEnumerable collection = new[] { 4, 6 };

// Act
Action act = () => dictionary.Should().HaveSameCount(collection, "we want to test the {0}", "reason");

// Assert
act.Should().Throw<XunitException>().WithMessage(
"Expected dictionary to have 2 item(s) because we want to test the reason, but count is 3.");
}

[Fact]
public void When_asserting_dictionary_and_collection_have_same_count_against_null_dictionary_it_should_throw()
{
// Arrange
Dictionary<string, int> dictionary = null;
IEnumerable collection = new[] { 1, 2, 3 };

// Act
Action act = () => dictionary.Should().HaveSameCount(collection,
"because we want to test the behaviour with a null subject");

// Assert
act.Should().Throw<XunitException>().WithMessage(
"Expected dictionary to have the same count as {1, 2, 3} because we want to test the behaviour with a null subject, but found <null>.");
}

[Fact]
public void When_asserting_dictionary_and_collection_have_same_count_against_a_null_collection_it_should_throw()
{
// Arrange
var dictionary = new Dictionary<int, string>
{
[1] = "One",
[2] = "Two",
[3] = "Three"
};
IEnumerable collection = null;

// Act
Action act = () => dictionary.Should().HaveSameCount(collection);

// Assert
act.Should().Throw<ArgumentNullException>().WithMessage(
"Cannot compare dictionary count against a <null> collection.*");
}

#endregion

#region Not Have Same Count

[Fact]
public void When_asserting_not_same_count_and_collections_have_different_number_elements_it_should_succeed()
{
// Arrange
var dictionary = new Dictionary<int, string>
{
[1] = "One",
[2] = "Two",
[3] = "Three"
};
IEnumerable collection = new[] { 4, 6 };

// Act / Assert
dictionary.Should().NotHaveSameCount(collection);
}

[Fact]
public void When_asserting_not_same_count_and_both_collections_have_the_same_number_elements_it_should_fail()
{
// Arrange
var dictionary = new Dictionary<int, string>
{
[1] = "One",
[2] = "Two",
[3] = "Three"
};
IEnumerable collection = new[] { 4, 5, 6 };

// Act
Action act = () => dictionary.Should().NotHaveSameCount(collection);

// Assert
act.Should().Throw<XunitException>().WithMessage(
"Expected dictionary to not have 3 item(s), but count is 3.");
}

[Fact]
public void When_comparing_not_same_item_counts_and_a_reason_is_specified_it_should_it_in_the_exception()
{
// Arrange
var dictionary = new Dictionary<int, string>
{
[1] = "One",
[2] = "Two",
[3] = "Three"
};
IEnumerable collection = new[] { 4, 5, 6 };

// Act
Action act = () => dictionary.Should().NotHaveSameCount(collection, "we want to test the {0}", "reason");

// Assert
act.Should().Throw<XunitException>().WithMessage(
"Expected dictionary to not have 3 item(s) because we want to test the reason, but count is 3.");
}

[Fact]
public void When_asserting_dictionary_and_collection_to_not_have_same_count_against_null_dictionary_it_should_throw()
{
// Arrange
Dictionary<int, string> dictionary = null;
IEnumerable collection = new[] { 1, 2, 3 };

// Act
Action act = () => dictionary.Should().NotHaveSameCount(collection,
"because we want to test the behaviour with a null subject");

// Assert
act.Should().Throw<XunitException>().WithMessage(
"Expected dictionary to not have the same count as {1, 2, 3} because we want to test the behaviour with a null subject, but found <null>.");
}

[Fact]
public void When_asserting_dictionary_and_collection_to_not_have_same_count_against_a_null_collection_it_should_throw()
{
// Arrange
var dictionary = new Dictionary<int, string>
{
[1] = "One",
[2] = "Two",
[3] = "Three"
};
IEnumerable collection = null;

// Act
Action act = () => dictionary.Should().NotHaveSameCount(collection);

// Assert
act.Should().Throw<ArgumentNullException>().WithMessage(
"Cannot compare dictionary count against a <null> collection.*");
}

[Fact]
public void When_asserting_dictionary_and_collection_to_not_have_same_count_but_both_reference_the_same_object_it_should_throw()
{
// Arrange
var dictionary = new Dictionary<int, string>
{
[1] = "One",
[2] = "Two",
[3] = "Three"
};
IEnumerable collection = dictionary;

// Act
Action act = () => dictionary.Should().NotHaveSameCount(collection,
"because we want to test the behaviour with same objects");

// Assert
act.Should().Throw<XunitException>().WithMessage(
"*not have the same count*because we want to test the behaviour with same objects*but they both reference the same object.");
}

#endregion

#region Be Empty

[Fact]
Expand Down
8 changes: 7 additions & 1 deletion docs/_pages/dictionaries.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ You can also assert that the dictionary has a certain number of items:
```csharp
dictionary.Should().HaveCount(2);
dictionary.Should().NotHaveCount(3);

dictionary1.Should().HaveSameCount(dictionary2);
dictionary1.Should().NotHaveSameCount(dictionary3);

dictionary1.Should().HaveSameCount(dictionary2.Keys);
dictionary1.Should().NotHaveSameCount(dictionary3.Keys);
```

And finally you can assert that the dictionary contains a specific key/value pair or not:
Expand All @@ -81,7 +87,7 @@ dictionary.Should().NotContain(item1, item2);
dictionary.Should().NotContain(9, "Nine");
```

Chaining additional assertion is supported as well.
Chaining additional assertions is supported as well.

```csharp
dictionary.Should().ContainValue(myClass)
Expand Down