What's the idiomatic way to verify collection size in xUnit?
Asked Answered
C

8

171

I have in my test suite a test that goes something like this:

[Fact]
public void VerifySomeStuff()
{
    var stuffCollection = GetSomeStuff();

    Assert.Equal(1, stuffCollection.Count());
}

This test works as I expect, but when I run it xUnit prints a warning:

warning xUnit2013: Do not use Assert.Equal() to check for collection size.

However, no alternative is suggested in the warning, and a google search takes me to the source code in xUnit for the test that verifies this warning is printed.

If Assert.Equal() isn't the correct way to verify the length of a collection, what is?


To clarify: I realize that I could "trick" xUnit into not emitting this warning by e.g. extracting a variable or using Assert.True(stuff.Count() == 1) instead. The latter is just hacky, and the former feels like if xUnit is e.g. trying to avoid multiple iterations of an IEnumerable<T>, then this is the wrong way to go (because I'll get compiler hints about that separately if it's an issue), and xUnit itself should never have to evaluate the input more than once (in fact it probably will get the same input regardless of variable extraction, because of how C# function calling works).

So, I'm not just interested in removing that warning from my output. An answer to my question also explains why that warning is included in the library in the first place and why whatever approach I should use instead is better.

Cheroot answered 9/10, 2017 at 19:19 Comment(2)
if you store stuffCollection.Count() in a separate variable and pass it to the assert does it give you the same error?Carpentaria
Maybe this one?Brey
A
187

Xunit offers quick fixes for most of its warnings, so you should be able to see what it thinks is "right".

xunit

In your case, it wants you to use Assert.Single since you are expecting exactly one item. If you were asserting an arbitrary number, like 412, then it would not give you a warning about using Count. It will only suggest using Single if you are expecting one item, or Empty if you are expecting no items.

Arraignment answered 9/10, 2017 at 20:15 Comment(9)
Thanks, that makes sense. FWIW, I was seeing this when building in VS Code, where the quick action did not show up, so actually including the fix suggestion in the warning message would have been much more helpful.Cheroot
@TomasLycken - ah. Yes there is an issue for that here: github.com/xunit/xunit/issues/1423Arraignment
I'm not a fan of that behavior; sometimes the 1 count is just incidental, and it seems less expressive to enforce the call to .Single(). The test may change to expect a different count, and it seems annoying to have to make the change to call a completely different method rather than just changing a number.Bobbysoxer
Single is cool for single Item, I have 3 items, and I don't want to write full Assert.Collection, does xUnit have Assert.Triple? hahaLacee
@PawelCioch according to xunit.net/xunit.analyzers/rules/xUnit2013.html they have Empty, Single and NotEmpty - if you expect a dynamic value xUnit2013 shouldn't trigger.Springhouse
@Springhouse thus it should not show warning. I can do Assert.True(3 == list.Count) to avoid warning, but this is worse than Assert.Equal(3, list.Count) in testing the error for first will be "expected true found false" not very informative, while equal "expected 3, found 1" so Equal for collection count is definitely better than "hacking" to avoid warning. I get the intention of xUnit creator but sometimes it's really better to check count than to write complex Assert.CollectionLacee
@PawelCioch xUnit only warns for 0 and 1, I don't get a warning for var list = new List<Int32> { 1, 2, 3 };Assert.Equal(3, list.Count);Springhouse
@Springhouse I'll try some day, maybe new version does it better now... Thanks!Lacee
But why would it give an error about checking for size? The error suggests you should never do it, not that there's a better way in this specific instance.Finochio
D
23

The rule only applies when testing for 0 or 1 items in collection.

Assert.Equal(0, result.Length) // rule warning, use .Empty
Assert.Equal(1, result.Length) // rule warning, use .Single
Assert.Equal(2, result.Length) // ok

To satisy rule:

Assert.Empty(result); // for 0 items
Assert.Single(result); // for 1 item
Assert.NotEmpty(result); // for 1 or more items

When using Assert.NotEmpty we may as well be precise with a count

Assert.Equal(2, result.Length) // Does not violate rule xUnit2013

https://xunit.net/xunit.analyzers/rules/xUnit2013

Divine answered 8/7, 2021 at 10:34 Comment(2)
Assert.NotEmpty(result) is not a good equivalent for Assert.Equal(2, result.Length) nor for 2 or more items it tests a different thing.Highhanded
Agree, it was an "Off by 1"-error in comment. Edited comment for Assert.NotEmpty(result) from 2 to 1.Divine
D
7

If you have more than one item, you can't use Assert.Single.

The expectation seems to be that you should use Assert.Collection:

var stuffCollection = GetSomeStuff();

Assert.Collection(stuffCollection, 
    item => Assert.True(true), // this lambda verifies the first item
    item => Assert.True(true), // second item
);

The assertion above verifies that there are exactly two items in the collection. You can provide stricter lambdas (such as item => Assert.Equals(7, item.property1) for each item if you want.

Personally, I'm not a fan; this seems like a very verbose way of saying how long you want the collection to be.

Detain answered 29/9, 2020 at 7:30 Comment(1)
Assert.Count has been proposed but Assert.Collection seems to be the idiomatic way to verify collections, github.com/xunit/xunit/discussions/2145.Teleutospore
R
3

I found this give me the same error:

Assert.Equal(2, vm.Errors.Count());

And casting it stopped the error from appearing.

Assert.Equal(2, (int)vm.Errors.Count());
Racine answered 28/11, 2018 at 16:33 Comment(2)
I'm quite sure, this is not the ideomatic way.Springhouse
You could also do Assert.Equal(2, vm.Errors.Count() + 1) =)Holly
S
3

Look I won't tell anyone if you just ignore the warning. Personally I think its more hassle than its worth. If you have an .editorconfig:

[*.cs]
dotnet_diagnostic.xUnit2013.severity = none # warning xUnit2013: Do not use Assert.Equal() to check for collection size.
Skell answered 1/11, 2022 at 23:20 Comment(2)
I was looking for this. I find that warning too opinionated. If my tests changes from 1 to more items then its not just changing the number, but the whole line just because someone wanted to get rid of the warning. This should be the default. Thanks FrankGilmore
Yep that's exactly how I ran into it.Skell
P
2

I had same issue when I used Count property as below in xUnit.

enter image description here

After, I use Count() function on collection, it fixed my issue.

Pilkington answered 12/4, 2018 at 2:59 Comment(3)
Fixed the issue, but you still don't use XUnit like you should!Lifesize
@DanielEisenreich what is the correct way to assert count for a specific number if it's greater than 1?Flitting
@Flitting and the other 4 upvotes. Forget what I said, I was too cheeky. If it's greater one you have no other choice.Lifesize
K
2

For single element in a list, it's best to use this instead: Assert.Single(resultList);

Kif answered 10/8, 2020 at 16:24 Comment(0)
K
-1

To check the length of a collection with one element, you can use:

Assert.Single(yourCollection)

Kiosk answered 18/11, 2022 at 5:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.