Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 5 additions & 106 deletions 111 Src/FluentAssertions/ObjectAssertionsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,71 +12,6 @@ namespace FluentAssertions;

public static class ObjectAssertionsExtensions
{
/// <summary>
/// Asserts that an object can be serialized and deserialized using the binary serializer and that it stills retains
/// the values of all members.
/// </summary>
/// <param name="assertions"></param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public static AndConstraint<ObjectAssertions> BeBinarySerializable(this ObjectAssertions assertions, string because = "",
Comment thread
lg2de marked this conversation as resolved.
params object[] becauseArgs)
{
return BeBinarySerializable<object>(assertions, options => options, because, becauseArgs);
}

/// <summary>
/// Asserts that an object can be serialized and deserialized using the binary serializer and that it stills retains
/// the values of all members.
/// </summary>
/// <param name="assertions"></param>
/// <param name="options">
/// A reference to the <see cref="EquivalencyAssertionOptions{TExpectation}"/> configuration object that can be used
/// to influence the way the object graphs are compared. You can also provide an alternative instance of the
/// <see cref="EquivalencyAssertionOptions{TExpectation}"/> class. The global defaults are determined by the
/// <see cref="AssertionOptions"/> class.
/// </param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
/// <exception cref="ArgumentNullException"><paramref name="options"/> is <see langword="null"/>.</exception>
public static AndConstraint<ObjectAssertions> BeBinarySerializable<T>(this ObjectAssertions assertions,
Func<EquivalencyAssertionOptions<T>, EquivalencyAssertionOptions<T>> options, string because = "",
params object[] becauseArgs)
{
Guard.ThrowIfArgumentIsNull(options);

try
{
object deserializedObject = CreateCloneUsingBinarySerializer(assertions.Subject);

EquivalencyAssertionOptions<T> defaultOptions = AssertionOptions.CloneDefaults<T>()
.RespectingRuntimeTypes().IncludingFields().IncludingProperties();

((T)deserializedObject).Should().BeEquivalentTo((T)assertions.Subject, _ => options(defaultOptions));
}
catch (Exception exc)
{
Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Expected {0} to be serializable{reason}, but serialization failed with:"
+ Environment.NewLine + Environment.NewLine + "{1}.",
assertions.Subject,
exc.Message);
}

return new AndConstraint<ObjectAssertions>(assertions);
}

/// <summary>
/// Asserts that an object can be serialized and deserialized using the data contract serializer and that it stills retains
/// the values of all members.
Expand Down Expand Up @@ -134,50 +69,14 @@ public static AndConstraint<ObjectAssertions> BeDataContractSerializable<T>(this
Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Expected {0} to be serializable{reason}, but serialization failed with:"
+ Environment.NewLine + Environment.NewLine + "{1}.",
+ Environment.NewLine + Environment.NewLine + "{1}.",
assertions.Subject,
exc.Message);
}

return new AndConstraint<ObjectAssertions>(assertions);
}

private static object CreateCloneUsingBinarySerializer(object subject)
{
using var stream = new MemoryStream();

var binaryFormatter = new BinaryFormatter
{
Binder = new SimpleBinder(subject.GetType())
};

#pragma warning disable SYSLIB0011, CA2300 // BinaryFormatter is obsoleted, GH-issue 1779 tracks the upcoming removal in .NET 8.0
binaryFormatter.Serialize(stream, subject);
stream.Position = 0;
return binaryFormatter.Deserialize(stream);
#pragma warning restore SYSLIB0011, CA2300
}

private sealed class SimpleBinder : SerializationBinder
{
private readonly Type type;

public SimpleBinder(Type type)
{
this.type = type;
}

public override Type BindToType(string assemblyName, string typeName)
{
if (type.FullName == typeName && type.Assembly.FullName == assemblyName)
{
return type;
}

return null;
}
}

private static object CreateCloneUsingDataContractSerializer(object subject)
{
using var stream = new MemoryStream();
Expand Down Expand Up @@ -214,7 +113,7 @@ public static AndConstraint<ObjectAssertions> BeXmlSerializable(this ObjectAsser
Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Expected {0} to be serializable{reason}, but serialization failed with:"
+ Environment.NewLine + Environment.NewLine + "{1}.",
+ Environment.NewLine + Environment.NewLine + "{1}.",
assertions.Subject,
exc.Message);
}
Expand All @@ -225,10 +124,10 @@ public static AndConstraint<ObjectAssertions> BeXmlSerializable(this ObjectAsser
private static object CreateCloneUsingXmlSerializer(object subject)
{
using var stream = new MemoryStream();
var binaryFormatter = new XmlSerializer(subject.GetType());
binaryFormatter.Serialize(stream, subject);
var serializer = new XmlSerializer(subject.GetType());
serializer.Serialize(stream, subject);

stream.Position = 0;
return binaryFormatter.Deserialize(stream);
return serializer.Deserialize(stream);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,6 @@ namespace FluentAssertions
}
public static class ObjectAssertionsExtensions
{
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeBinarySerializable(this FluentAssertions.Primitives.ObjectAssertions assertions, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeBinarySerializable<T>(this FluentAssertions.Primitives.ObjectAssertions assertions, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>> options, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeDataContractSerializable(this FluentAssertions.Primitives.ObjectAssertions assertions, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeDataContractSerializable<T>(this FluentAssertions.Primitives.ObjectAssertions assertions, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>> options, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeXmlSerializable(this FluentAssertions.Primitives.ObjectAssertions assertions, string because = "", params object[] becauseArgs) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,6 @@ namespace FluentAssertions
}
public static class ObjectAssertionsExtensions
{
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeBinarySerializable(this FluentAssertions.Primitives.ObjectAssertions assertions, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeBinarySerializable<T>(this FluentAssertions.Primitives.ObjectAssertions assertions, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>> options, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeDataContractSerializable(this FluentAssertions.Primitives.ObjectAssertions assertions, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeDataContractSerializable<T>(this FluentAssertions.Primitives.ObjectAssertions assertions, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>> options, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeXmlSerializable(this FluentAssertions.Primitives.ObjectAssertions assertions, string because = "", params object[] becauseArgs) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,6 @@ namespace FluentAssertions
}
public static class ObjectAssertionsExtensions
{
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeBinarySerializable(this FluentAssertions.Primitives.ObjectAssertions assertions, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeBinarySerializable<T>(this FluentAssertions.Primitives.ObjectAssertions assertions, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>> options, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeDataContractSerializable(this FluentAssertions.Primitives.ObjectAssertions assertions, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeDataContractSerializable<T>(this FluentAssertions.Primitives.ObjectAssertions assertions, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>> options, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeXmlSerializable(this FluentAssertions.Primitives.ObjectAssertions assertions, string because = "", params object[] becauseArgs) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,6 @@ namespace FluentAssertions
}
public static class ObjectAssertionsExtensions
{
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeBinarySerializable(this FluentAssertions.Primitives.ObjectAssertions assertions, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeBinarySerializable<T>(this FluentAssertions.Primitives.ObjectAssertions assertions, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>> options, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeDataContractSerializable(this FluentAssertions.Primitives.ObjectAssertions assertions, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeDataContractSerializable<T>(this FluentAssertions.Primitives.ObjectAssertions assertions, System.Func<FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>, FluentAssertions.Equivalency.EquivalencyAssertionOptions<T>> options, string because = "", params object[] becauseArgs) { }
public static FluentAssertions.AndConstraint<FluentAssertions.Primitives.ObjectAssertions> BeXmlSerializable(this FluentAssertions.Primitives.ObjectAssertions assertions, string because = "", params object[] becauseArgs) { }
Expand Down
182 changes: 0 additions & 182 deletions 182 Tests/FluentAssertions.Specs/Primitives/ObjectAssertionSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1251,188 +1251,6 @@ public void Should_throw_a_helpful_error_when_accidentally_using_equals()
}
}

public class BeBinarySerializable
{
[Fact]
public void When_an_object_is_binary_serializable_it_should_succeed()
{
// Arrange
var subject = new SerializableClass
{
Name = "John",
Id = 2
};

// Act
Action act = () => subject.Should().BeBinarySerializable();

// Assert
act.Should().NotThrow();
}

[Fact]
public void When_an_object_is_binary_serializable_with_non_serializable_members_it_should_succeed()
{
// Arrange
var subject = new SerializableClassWithNonSerializableMember
{
Name = "John",
NonSerializable = "Nonserializable value"
};

// Act
Action act = () => subject.Should().BeBinarySerializable<SerializableClassWithNonSerializableMember>(options =>
options.Excluding(s => s.NonSerializable));

// Assert
act.Should().NotThrow();
}

[Fact]
public void When_injecting_null_options_it_should_throw()
{
// Arrange
var subject = new SerializableClassWithNonSerializableMember();

// Act
Action act = () => subject.Should().BeBinarySerializable<SerializableClassWithNonSerializableMember>(options: null);

// Assert
act.Should().ThrowExactly<ArgumentNullException>()
.WithParameterName("options");
}

[Fact]
public void When_an_object_is_not_binary_serializable_it_should_fail()
{
// Arrange
var subject = new UnserializableClass
{
Name = "John"
};

// Act
Action act = () => subject.Should().BeBinarySerializable("we need to store it on {0}", "disk");

// Assert
act.Should().Throw<XunitException>()
.WithMessage(
"*to be serializable because we need to store it on disk, but serialization failed with:*UnserializableClass*");
}

[Fact]
public void When_an_object_is_binary_serializable_but_not_deserializable_it_should_fail()
{
// Arrange
var subject = new BinarySerializableClassMissingDeserializationConstructor
{
Name = "John",
BirthDay = 20.September(1973)
};

// Act
Action act = () => subject.Should().BeBinarySerializable();

// Assert
act.Should().Throw<XunitException>()
.WithMessage(
"*to be serializable, but serialization failed with:*BinarySerializableClassMissingDeserializationConstructor*");
}

[Fact]
public void When_an_object_is_binary_serializable_but_doesnt_restore_all_properties_it_should_fail()
{
// Arrange
var subject = new BinarySerializableClassNotRestoringAllProperties
{
Name = "John",
BirthDay = 20.September(1973)
};

// Act
Action act = () => subject.Should().BeBinarySerializable();

// Assert
act.Should().Throw<XunitException>()
.WithMessage("*to be serializable, but serialization failed with:*subject.Name*to be*");
}

[Fact]
public void When_a_system_exception_is_asserted_to_be_serializable_it_should_compare_its_fields_and_properties()
{
// Arrange
var subject = new Exception("some error");

// Act
Action act = () => subject.Should().BeBinarySerializable();

// Assert
act.Should().NotThrow();
}
}

internal class UnserializableClass
{
public string Name { get; set; }
}

[Serializable]
public class SerializableClass
{
public string Name { get; set; }

public int Id;
}

[Serializable]
public class SerializableClassWithNonSerializableMember
{
[NonSerialized]
private string nonSerializable;

public string Name { get; set; }

public string NonSerializable
{
get => nonSerializable;
set => nonSerializable = value;
}
}

[Serializable]
internal class BinarySerializableClassMissingDeserializationConstructor : ISerializable
{
public string Name { get; set; }

public DateTime BirthDay { get; set; }

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
}
}

[Serializable]
internal class BinarySerializableClassNotRestoringAllProperties : ISerializable
{
public string Name { get; set; }

public DateTime BirthDay { get; set; }

public BinarySerializableClassNotRestoringAllProperties()
{
}

public BinarySerializableClassNotRestoringAllProperties(SerializationInfo info, StreamingContext context)
{
BirthDay = info.GetDateTime("BirthDay");
}

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("BirthDay", BirthDay);
}
}

public class BeXmlSerializable
{
[Fact]
Expand Down
10 changes: 1 addition & 9 deletions 10 docs/_pages/basicassertions.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,9 @@ Some users requested the ability to easily downcast an object to one of its deri
customer.Animals.First().As<Human>().Height.Should().Be(178);
```

We’ve also added the possibility to assert that an object can be serialized and deserialized using the XML, binary or data contract formatters.
We’ve also added the possibility to assert that an object can be serialized and deserialized using the XML or data contract formatters.

```csharp
theObject.Should().BeXmlSerializable();
theObject.Should().BeBinarySerializable();
theObject.Should().BeDataContractSerializable();
```

Internally, `BeBinarySerializable` uses the [Object graph comparison](objectgraphs.md) API, so if you are in need of excluding certain properties from the comparison (for instance, because its backing field is `[NonSerializable]`, you can do this:

```csharp
theObject.Should().BeBinarySerializable<MyClass>(
options => options.Excluding(s => s.SomeNonSerializableProperty));
```
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.