I have the following code
public class MyCollection<T>
{
public void Add(T obj) { ... }
public int Count { get; }
}
and the following unit test to check whether Add
increases the Count
property by one:
[Fact]
public void Add_ShouldIncreaseCount()
{
var collection = new MyCollection<int>();
var oldCount = collection.Count;
collection.Add(default);
Assert.Equal(collection.Count, oldCount + 1);
}
However, I feel like the test is not "good" as in it only shows that Add
works for MyCollection<int>
.
Do I have to add tests for more value types and also reference types (e.g. string
etc.) or is there a way (such as AutoFixture
's fixture.Create<T>()
to create arbitrary values) to create a "dummy" type?
Is there a way to indicate that I do not care about the actual generic type? What are the best practices in this case?
2 Answers 2
Testing the degree of freedom that a generic class affords you is like testing a function that can take any int. There's no reasonable way to test every possible input, and no construct that I know of to ensure that the same test would succeed no matter what T
is.
Therefore, you should do what you do with a normal function of int: you test a few different int values (in particular, boundary cases to test for off-by-one errors and such). Here this means that you test the Add()
with an int instantiation, with a string instantiationr and perhaps with some totally other type. For instance, if your language has function types you might use that to demonstrate that your container really just stores the function, as opposed e.g. to calling it. Other than that I don't see what else due diligence would require.
-
This question is C# specific, but if this were C++ I'd also add test that the container obeys move or copy semantics as appropriate. C# sidesteps the need for this test, luckily.Mooing Duck– Mooing Duck12/21/2020 20:49:25Commented Dec 21, 2020 at 20:49
-
1Yeah, but when testing something that can be any int, picking
0
,1
and something else(say42
) is often a good ideaFlydog57– Flydog5712/21/2020 22:13:49Commented Dec 21, 2020 at 22:13
I would test with object first, it's the highest level thing you can use. Also it's the responsibility of classes that inherit object to conform to object, so if MyType
encounters issues, it's more likely an issue with the type than this code. I would add tests for the most common anticipated implementations if you really want peace of mind, it's not that expensive to do. A general side note, I personally prefer magic numbers in unit tests, because it could potentially mask issues to use count to get the initial value.
Foo
that it couldn't do with an argument of typeint
as well.T
is a reference type (where T : class
) or a value type (where T : struct
) likeint
. Try testing it for another type that's a reference type and call it a day.