-
Notifications
You must be signed in to change notification settings - Fork 0
Object Extensions
#ObjectExtensions Class
The ObjectExtensions class provides various useful extension methods for unit testing. For example, an IsEqual method is provided to perform a deep comparison of property values of two objects of any complexity. Note that these extension methods should be confined to unit testing projects to prevent unnecessary bloat in production code. Also, these extension methods are purposely placed in their own ..Extensions namespace to prevent possible conflicts with other classes in this package.
The package can be added to your .NET Core 2 project by adding the following element to the .csproj file of a unit test class:
<ItemGroup>
<PackageReference Include="EDennis.NetCoreTestingUtilities" Version="3.4.0" />
...
</ItemGroup>
Afterward, the package can be imported into a test class as such:
using EDennis.NetCoreTestingUtilities.Extensions;
This method returns true if the provided object variable and the current object variable (this) refer to the same object in memory. The method compares hashes and property values. Example:
[Test]
public void TestSameObjectInMemory() {
Person p1 = new Person("Bob", "Jones");
Person p2 = p1; //assign p2 to point to p1 (same object in memory)
Assert.True(p1.IsSame(p2));
}
This method returns true if the provided object variable and the current object variable (this) have the same property values. NOTE: this is a deep comparison. Example:
[Test]
public void TestEqualObjects() {
Person p1 = new Person("Bob", "Jones");
Person p2 = new Person("Bob", "Jones");
Assert.False(p1.IsSame(p2));
Assert.True(p1.IsEqual(p2));
}
This method returns true if the provided object variable and the current object variable (this) have the same property values, ignoring all properties at the provided JSON paths. Example:
[Test]
public void TestEqualObjectsIgnoringSelectedProperties() {
Person p1 = new Person(1, "Bob", "Jones");
Person p2 = new Person(2, "Bob", "Jones");
Assert.False(p1.IsEqual(p2));
Assert.True(p1.IsEqual(p2, new string[] { "ID" })); //ignore ID
}
This method serializes an object to a JSON string. Example:
[Test]
public void TestToJsonString() {
string expectedJson =
@"{
""ID"": 0,
""FirstName"": ""David"",
""LastName"": ""Parks""
}";
expectedJson = JToken.Parse(expectedJson).ToString(Formatting.Indented);
Person p1 = new Person("David", "Parks");
string actualJson = p1.ToJsonString();
Assert.AreEqual(expectedJson, actualJson);
}
This method creates a new object of type T by deserializing the provided JSON string. When called from an object constructor, this method provides a convenient way to load an object with properties. Example:
[Test]
public void TestFromJsonString() {
string json =
@"{
""FirstName"": ""David"",
""LastName"": ""Parks""
}";
var p1 = new Person().FromJsonString(json);
Person p2 = new Person("David", "Parks");
Assert.True(p1.IsEqual(p2));
}
This method creates a new object of type T by deserializing a JSON object or array in the provided JSON file at the provided path. When called from an object constructor, this method provides a convenient way to load an object with properties. NOTE: you can use "" or "/" instead of "." in the objectPath. Example:
[Test]
public void TestFromJsonPath2() {
var p1 = new Person().FromJsonPath(@"DavidParks.json", "Contact");
Person p2 = new Person("Jill", "Parks");
Assert.True(p1.IsEqual(p2));
}
This method is equivalent to the above method, but it combines the filePath and objectPath into a single expression. Note that you can use a backslash or forward slash instead of period in the objectPath. Example:
[Test]
public void TestFromJsonPath3() {
var p1 = new Person().FromJsonPath(@"DavidParks.json\Contact");
Person p2 = new Person("Jill", "Parks");
Assert.True(p1.IsEqual(p2));
}
This method creates a new object of type T by deserializing a JSON object or array at the provided path in the provided JToken object. When called from an object constructor, this method provides a convenient way to load an object with properties. Example:
[Test]
public void TestFromJsonPath1() {
string json =
@"{
""FirstName"": ""David"",
""LastName"": ""Parks"",
""Contact"": {
""FirstName"": ""Jill"",
""LastName"": ""Parks""
}
}";
var jtoken = JToken.Parse(json);
var p1 = new Person().FromJsonPath(jtoken, "Contact");
Person p2 = new Person("Jill", "Parks");
Assert.True(p1.IsEqual(p2));
}
This method creates a new object of type T by deserializing a JSON object or array from a SQL Server FOR JSON query. When called from an object constructor, this method provides a convenient way to load an object with properties. Example:
[Test]
public void TestFromSql1() {
using (var context = new JsonResultContext()) {
var expectedJson = new List<Person>().FromJsonPath(@"01.json\persons");
var actualJson = new List<Person>().FromSql(@"01.sql", context);
Assert.True(expectedJson.IsEqual(actualJson));
}
}
{
"_comment": "01.json"
"persons": [
{
"ID": 1,
"FirstName": "Bob",
"LastName": "Jones",
"DateOfBirth": "1980-01-23T00:00:00",
"Skills": [
{
"Category": "Application Development",
"Score": 3
},
{
"Category": "Project Management",
"Score": 3
}
]
},
{
"ID": 2,
"FirstName": "Jill",
"LastName": "Jones",
"DateOfBirth": "1981-01-24T00:00:00",
"Skills": [
{
"Category": "Application Development",
"Score": 2
},
{
"Category": "Project Management",
"Score": 1
}
]
}
]
}
-- 01.sql
declare @Person table (
ID int,
FirstName varchar(30),
LastName varchar(30),
DateOfBirth datetime
);
declare @Skill table (
PersonID int,
Category varchar(30),
Score int
);
insert into @Person(ID,FirstName,LastName,DateOfBirth)
values
(1,'Bob','Jones','1980-01-23'),
(2,'Jill','Jones','1981-01-24');
insert into @Skill(PersonID,Category,Score)
values
(1,'Application Development',3),
(1,'Project Management',3),
(2,'Application Development',2),
(2,'Project Management',1);
declare @j varchar(max);
set @j =
(
select ID,FirstName,LastName,DateOfBirth,
(select
Category,Score
from @Skill s
where s.PersonID = p.ID
for json path) Skills
from @Person p
for json path
);
select @j [json];
This method creates a new object of type T by deserializing a JSON object or array from a SQL Server FOR JSON query. When called from an object constructor, this method provides a convenient way to load an object with properties. Example:
[Test]
public void TestFromSql1() {
using (var context = new JsonResultContext()) {
var expectedJson = new List<Person>().FromJsonPath(@"01.json\persons");
var actualJson = new List<Person>().FromSql(@"01.sql",
"Server=(localdb)\\mssqllocaldb;Database=tempdb;Trusted_Connection=True;");
Assert.True(expectedJson.IsEqual(actualJson));
}
}
IsEqualOrWrite -- compares actual and expected objects to each other and, when they are unequal, writes out the two objects side-by-side, highlighting the discrepant properties.
Name | Required? | Description |
---|---|---|
T obj2 | Yes | The object to compare (typically the expected value) |
int maxDepth | No | The maximum depth of the object graph to compare† |
string[] propertiesToIgnore | No | a string array of property names to ignore during the comparison |
ITestOutputHelper output | Yes | A reference to Xunit's injected ITestOutputHelper |
bool | No | Whether to ignore the order of elements in arrays during the comparison |
†Special note: for arrays, count the array itself as the first level |
//compare actual object to expected object, and
//write out the objects side-by-side when they are not equal
Assert.True(actual.IsEqualOrWrite(expected, 3, Output));
One of the most powerful features of .Net MVC is the ability to test controller actions (methods) directly without launching a web server and making an HTTP request. Unfortunately, if the return type of the controller action is an IActionResult or ActionResult, comparing the actual return type to an expected return type requires a little extra code.
The extension methods provided by the current library make the testing process a little easier by providing extension methods that extract the object value and status code of the ActionResult or IActionResult. There isn't any magical code here, but the extension methods can reduce the testing code a little.
The ActionResultExtensions class and the IActtionResultExtensions class provide extension methods that perform the same functionality:
- GetObject Method -- returns the object contained in the response body
- GetStatusCode Method -- returns the status code from an action result or 0 if there is no status code