Skip to content

Commit 05667d4

Browse files
author
tznind
committed
Add tests for type extensions
1 parent c9cd64b commit 05667d4

2 files changed

Lines changed: 185 additions & 7 deletions

File tree

src/TypeExtensions.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,29 @@ public static class TypeExtensions
1818
public static Type? GetElementTypeEx(this Type type)
1919
{
2020
var elementType = type.GetElementType();
21-
2221
if (elementType != null)
23-
{
2422
return elementType;
25-
}
2623

2724
if (type.IsAssignableTo(typeof(IList)) && type.IsGenericType)
2825
{
29-
return type.GetGenericArguments().Single();
26+
var args = type.GetGenericArguments();
27+
if (args.Length != 1)
28+
throw new InvalidOperationException($"Expected exactly one generic argument for IList type {type}, but found {args.Length}.");
29+
return args[0];
3030
}
31-
31+
3232
if (type.IsGenericType(typeof(IEnumerable<>)))
3333
{
34-
return type.GetGenericArguments().Single();
34+
var args = type.GetGenericArguments();
35+
if (args.Length != 1)
36+
throw new InvalidOperationException($"Expected exactly one generic argument for IEnumerable type {type}, but found {args.Length}.");
37+
return args[0];
3538
}
3639

37-
3840
return null;
3941
}
4042

43+
4144
/// <summary>
4245
/// Returns true if <paramref name="type"/> is an implementation of a generic parent
4346
/// type <paramref name="genericParentHypothesis"/>.

tests/TypeExtensionTests.cs

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+

2+
using System.Collections.ObjectModel;
3+
4+
namespace UnitTests;
5+
6+
[TestFixture]
7+
public class TypeExtensionTests
8+
{
9+
[Test]
10+
public void IReadOnlyListOfString_IsDetected()
11+
{
12+
var type = typeof(IReadOnlyList<string>);
13+
14+
Assert.That(
15+
type.IsGenericType(typeof(IReadOnlyList<>)),
16+
Is.True);
17+
}
18+
19+
[Test]
20+
public void ListOfString_Implements_IReadOnlyList()
21+
{
22+
var type = typeof(List<string>);
23+
24+
Assert.That(
25+
type.IsGenericType(typeof(IReadOnlyList<>)),
26+
Is.True);
27+
}
28+
29+
[Test]
30+
public void ArrayOfString_Implements_IReadOnlyList()
31+
{
32+
var type = typeof(string[]);
33+
34+
Assert.That(
35+
type.IsGenericType(typeof(IReadOnlyList<>)),
36+
Is.True);
37+
}
38+
39+
[Test]
40+
public void CompilerGeneratedReadOnlyArray_IsDetected()
41+
{
42+
IReadOnlyList<int> list = new[] { 1, 2, 3 };
43+
var type = list.GetType(); // <>z__ReadOnlyArray`1
44+
45+
Assert.That(
46+
type.IsGenericType(typeof(IReadOnlyList<>)),
47+
Is.True);
48+
}
49+
50+
[Test]
51+
public void ReadOnlyCollection_IsDetected()
52+
{
53+
var type = typeof(ReadOnlyCollection<int>);
54+
55+
Assert.That(
56+
type.IsGenericType(typeof(IReadOnlyList<>)),
57+
Is.True);
58+
}
59+
60+
[Test]
61+
public void NonGenericType_DoesNotMatch()
62+
{
63+
var type = typeof(string);
64+
65+
Assert.That(
66+
type.IsGenericType(typeof(IReadOnlyList<>)),
67+
Is.False);
68+
}
69+
70+
[Test]
71+
public void UnrelatedGenericType_DoesNotMatch()
72+
{
73+
var type = typeof(Dictionary<int, string>);
74+
75+
Assert.That(
76+
type.IsGenericType(typeof(IReadOnlyList<>)),
77+
Is.False);
78+
}
79+
80+
[Test]
81+
public void OpenGeneric_IReadOnlyListDefinition_MatchesItself()
82+
{
83+
var type = typeof(IReadOnlyList<>);
84+
85+
Assert.That(
86+
type.IsGenericType(typeof(IReadOnlyList<>)),
87+
Is.True);
88+
}
89+
90+
[Test]
91+
public void Array_ReturnsElementType()
92+
{
93+
var type = typeof(string[]);
94+
95+
Assert.That(
96+
type.GetElementTypeEx(),
97+
Is.EqualTo(typeof(string)));
98+
}
99+
100+
[Test]
101+
public void GenericIList_ReturnsElementType()
102+
{
103+
var type = typeof(List<int>);
104+
105+
Assert.That(
106+
type.GetElementTypeEx(),
107+
Is.EqualTo(typeof(int)));
108+
}
109+
110+
[Test]
111+
public void IReadOnlyList_ReturnsElementType()
112+
{
113+
var type = typeof(IReadOnlyList<double>);
114+
115+
Assert.That(
116+
type.GetElementTypeEx(),
117+
Is.EqualTo(typeof(double)));
118+
}
119+
120+
[Test]
121+
public void IEnumerable_ReturnsElementType()
122+
{
123+
var type = typeof(IEnumerable<Guid>);
124+
125+
Assert.That(
126+
type.GetElementTypeEx(),
127+
Is.EqualTo(typeof(Guid)));
128+
}
129+
130+
[Test]
131+
public void CompilerGeneratedReadOnlyArray_ReturnsElementType()
132+
{
133+
IReadOnlyList<int> list = new[] { 1, 2, 3 };
134+
var type = list.GetType(); // <>z__ReadOnlyArray`1
135+
136+
Assert.That(
137+
type.GetElementTypeEx(),
138+
Is.EqualTo(typeof(int)));
139+
}
140+
141+
[Test]
142+
public void MultipleGenericArguments_ThrowsInvalidOperation()
143+
{
144+
var type = typeof(Dictionary<int, string>); // 2 generic args
145+
146+
Assert.That(
147+
() => type.GetElementTypeEx(),
148+
Throws.InvalidOperationException
149+
.With.Message.Contain("Expected exactly one generic argument"));
150+
}
151+
152+
153+
private class TestType
154+
{
155+
public int Foo { get; set; }
156+
}
157+
158+
[Test]
159+
public void ExistingProperty_IsReturned()
160+
{
161+
var prop = typeof(TestType).GetPropertyOrThrow("Foo");
162+
163+
Assert.That(prop, Is.Not.Null);
164+
Assert.That(prop.Name, Is.EqualTo("Foo"));
165+
}
166+
167+
[Test]
168+
public void MissingProperty_Throws()
169+
{
170+
Assert.That(
171+
() => typeof(TestType).GetPropertyOrThrow("Bar"),
172+
Throws.Exception.With.Message.Contains("Could not find expected property"));
173+
}
174+
175+
}

0 commit comments

Comments
 (0)