Skip to content

Commit 5900172

Browse files
committed
Add more complicated Enumerable w/ Enumerable tests
Add a test which has a List which contains elements which also have a List of string. From what I can tell, there's no way for a ValidationAttribute validator to report back on all of the elements that failed validation within a simple IEnumerable<string> list. At best, you might be able to write a ValidationAttribute validator which reports back on the index of the first failure within the list.
1 parent 55a21ce commit 5900172

File tree

4 files changed

+111
-2
lines changed

4 files changed

+111
-2
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel.DataAnnotations;
4+
using System.Linq;
5+
6+
namespace RecursiveDataAnnotationsValidation.Tests.Attributes;
7+
8+
/// <summary>Examine each element in an IEnumerable of string and return false (not valid) if
9+
/// any of the string values are null/whitespace. It makes no attempt to return the
10+
/// index number of the invalid entry; it only reports back a generic error for
11+
/// the IEnumerable of string property.
12+
/// </summary>
13+
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property)]
14+
public class EnumerableStringNotNullOrWhitespaceAttribute : ValidationAttribute
15+
{
16+
public EnumerableStringNotNullOrWhitespaceAttribute()
17+
{
18+
ErrorMessage = "Found elements that are null or whitespace.";
19+
}
20+
21+
public override bool IsValid(object value)
22+
{
23+
if (value is not IEnumerable<string> enumerableStrings) return true;
24+
25+
return !enumerableStrings.Any(x => string.IsNullOrWhiteSpace(x));
26+
}
27+
}

test/RecursiveDataAnnotationsValidation.Tests/EnumerableExampleTests.cs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public void Passes_all_validation_no_children()
2020
Items = new List<ItemExample>(),
2121
ItemsList = new List<ItemExample>(),
2222
ItemsCollection = new List<ItemExample>(),
23+
ItemWithListExamples = new List<ItemWithListExample>(),
2324
};
2425

2526
var validationResults = new List<ValidationResult>();
@@ -110,7 +111,19 @@ public void Passes_all_validation_with_children()
110111
ExampleEnumD = ExampleEnum.ValueA
111112
}
112113
}
113-
}
114+
},
115+
ItemWithListExamples = new List<ItemWithListExample>
116+
{
117+
new ItemWithListExample
118+
{
119+
ItemWithListName = "ItemList 0L",
120+
Claims = new List<string>
121+
{
122+
"Claim 0L-1",
123+
"Claim 0L-2",
124+
},
125+
},
126+
},
114127
};
115128

116129
var validationResults = new List<ValidationResult>();
@@ -284,7 +297,46 @@ public void Fails_on_Items_Child2_SimpleA_BoolC()
284297
ExampleEnumD = ExampleEnum.ValueA
285298
}
286299
},
287-
}
300+
},
301+
ItemWithListExamples = new List<ItemWithListExample>
302+
{
303+
new ItemWithListExample
304+
{
305+
ItemWithListName = null,
306+
Claims = new List<string>
307+
{
308+
null,
309+
"ItemList Claim 0L-1L",
310+
},
311+
},
312+
new ItemWithListExample
313+
{
314+
ItemWithListName = "ItemList 1L",
315+
Claims = new List<string>
316+
{
317+
"ItemList Claim 1L-0L",
318+
" ", // whitespace
319+
},
320+
},
321+
new ItemWithListExample
322+
{
323+
ItemWithListName = "ItemList 2L",
324+
Claims = null
325+
},
326+
new ItemWithListExample
327+
{
328+
ItemWithListName = "ItemList 3L",
329+
Claims = new List<string>
330+
{
331+
"ItemList Claim 3L-0L",
332+
"ItemList Claim 3L-1L",
333+
"ItemList Claim 3L-2L",
334+
"ItemList Claim 3L-3L",
335+
"ItemList Claim 3L-4L",
336+
"ItemList Claim 3L-5L",
337+
},
338+
},
339+
},
288340
};
289341

290342
[Theory]
@@ -298,6 +350,17 @@ public void Fails_on_Items_Child2_SimpleA_BoolC()
298350
[InlineData("ItemsCollection[3].SimpleA.IntegerA")]
299351
[InlineData("ItemsCollection[3].SimpleA.StringB")]
300352
[InlineData("ItemsList[1].SimpleA.ExampleEnumD")]
353+
[InlineData("ItemWithListExamples[0].ItemWithListName")]
354+
// It would be nice if these would indicate which element in the IEnumerable was not valid.
355+
// That's not easy / almost impossible to do with simple IEnumerable<T> like "string".
356+
// There might be an ValidationAttribute derived class that does it?
357+
// It may be out of scope for the RecursiveDataAnnotationsValidation project.
358+
// It might be possible to report on the FIRST failure in the IEnumerable<T> with the index,
359+
// but that would be the responsibility of whatever inherits from ValidationAttribute.
360+
[InlineData("ItemWithListExamples[0].Claims")]
361+
[InlineData("ItemWithListExamples[1].Claims")]
362+
// ^---- future would output "ItemWithListExamples[x].Claims[y]" for the specific element
363+
[InlineData("ItemWithListExamples[2].Claims")]
301364
public void Multiple_failures_contains_expected_memberName(string expectedMemberName)
302365
{
303366
var validationResults = new List<ValidationResult>();

test/RecursiveDataAnnotationsValidation.Tests/TestModels/EnumerableExample.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,8 @@ public class EnumerableExample
2020

2121
[Required]
2222
public List<ItemExample> ItemsCollection { get; set; }
23+
24+
[Required]
25+
public List<ItemWithListExample> ItemWithListExamples { get; set; }
2326
}
2427
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Collections.Generic;
2+
using System.ComponentModel.DataAnnotations;
3+
using RecursiveDataAnnotationsValidation.Tests.Attributes;
4+
5+
namespace RecursiveDataAnnotationsValidation.Tests.TestModels
6+
{
7+
public class ItemWithListExample
8+
{
9+
[Required]
10+
public string ItemWithListName { get; set; }
11+
12+
[Required]
13+
[EnumerableStringNotNullOrWhitespace]
14+
public List<string> Claims { get; set; }
15+
}
16+
}

0 commit comments

Comments
 (0)