public static AndConstraint <ObjectAssertions> BeJsonSerializable <T>(this ObjectAssertions assertions, Func <EquivalencyAssertionOptions <T>, EquivalencyAssertionOptions <T> > options, string because = "", params object[] becauseArgs) { Execute.Assertion.ForCondition(assertions.Subject != null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} to be JSON serializable{reason}, but the value is null. Please provide a value for the assertion."); Execute.Assertion.ForCondition(assertions.Subject is T) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} to be JSON serializable{reason}, but {context:object} is not assignable to {0}", typeof(T)); try { var deserializedObject = CreateCloneUsingJsonSerializer(assertions.Subject); var defaultOptions = AssertionOptions.CloneDefaults <T>() .RespectingRuntimeTypes() .IncludingFields() .IncludingProperties(); var typedSubject = (T)assertions.Subject; ((T)deserializedObject).Should().BeEquivalentTo(typedSubject, _ => options(defaultOptions)); } catch (Exception exc) { Execute.Assertion .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} to be JSON serializable{reason}, but serializing {0} failed with {1}", assertions.Subject, exc); } return(new AndConstraint <ObjectAssertions>(assertions)); }
/// <summary> /// Asserts that a collection of objects is equivalent to another collection of objects. /// </summary> /// <remarks> /// The two collections are equivalent when they both contain the same strings in any order. /// </remarks> /// <param name="config"> /// A reference to the <see cref="EquivalencyAssertionOptions{String}"/> 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{String}"/> class. The global defaults are determined by the /// <see cref="AssertionOptions"/> class. /// </param> /// <param name="because"> /// An optional 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 <see cref="because" />. /// </param> public AndConstraint <StringCollectionAssertions> BeEquivalentTo(IEnumerable <string> expectation, Func <EquivalencyAssertionOptions <string>, EquivalencyAssertionOptions <string> > config, string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(config, nameof(config)); EquivalencyAssertionOptions <IEnumerable <string> > options = config(AssertionOptions.CloneDefaults <string>()).AsCollection(); var context = new EquivalencyValidationContext { Subject = Subject, Expectation = expectation, RootIsCollection = true, CompileTimeType = typeof(IEnumerable <string>), Because = because, BecauseArgs = becauseArgs, Tracer = options.TraceWriter }; var equivalencyValidator = new EquivalencyValidator(options); equivalencyValidator.AssertEquality(context); return(new AndConstraint <StringCollectionAssertions>(this)); }
public static AndConstraint <GenericCollectionAssertions <T> > NotContainEquivalentOf <T>( this GenericCollectionAssertions <T> assert, T expectation, Func <EquivalencyAssertionOptions <T>, EquivalencyAssertionOptions <T> > config, string because = "", params object[] becauseArgs) { if (ReferenceEquals(assert.Subject, null)) { return(new AndConstraint <GenericCollectionAssertions <T> >(assert)); } IEquivalencyAssertionOptions options = config(AssertionOptions.CloneDefaults <T>()); IEnumerable <object> actualItems = assert.Subject.Cast <object>(); using (var scope = new AssertionScope()) { scope.AddReportable("configuration", options.ToString()); foreach (var actualItem in actualItems) { var context = new EquivalencyValidationContext { Subject = actualItem, Expectation = expectation, CompileTimeType = typeof(T), Because = because, BecauseArgs = becauseArgs, Tracer = options.TraceWriter, }; var equivalencyValidator = new EquivalencyValidator(options); equivalencyValidator.AssertEquality(context); var failures = scope.Discard(); if (!failures.Any()) { Execute.Assertion .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} {0} to not contain equivalent of {1}.", assert.Subject, expectation); break; } } } return(new AndConstraint <GenericCollectionAssertions <T> >(assert)); }
internal static void BeEquivalentTo <TExpectation, TSubject>(TSubject subject, TExpectation expectation, Func <EquivalencyAssertionOptions <TExpectation>, EquivalencyAssertionOptions <TExpectation> > config, string because = "", params object[] becauseArgs) { var options = config(AssertionOptions.CloneDefaults <TExpectation>()); var context = new EquivalencyValidationContext { Subject = subject, Expectation = expectation, CompileTimeType = typeof(TExpectation), Because = because, BecauseArgs = becauseArgs, Tracer = options.TraceWriter }; new EquivalencyValidator(options).AssertEquality(context); }
/// <summary> /// Asserts that a collection of objects is equivalent to another collection of objects. /// </summary> /// <remarks> /// Objects within the collections are equivalent when both object graphs have equally named properties with the same /// value, irrespective of the type of those objects. Two properties are also equal if one type can be converted to another /// and the result is equal. /// The type of a collection property is ignored as long as the collection implements <see cref="IEnumerable"/> and all /// items in the collection are structurally equal. /// </remarks> /// <param name="config"> /// A reference to the <see cref="EquivalencyAssertionOptions{TSubject}"/> 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{TSubject}"/> class. The global defaults are determined by the /// <see cref="AssertionOptions"/> class. /// </param> /// <param name="because"> /// An optional 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 <see cref="because" />. /// </param> public void BeEquivalentTo(IEnumerable expectation, Func <EquivalencyAssertionOptions <IEnumerable>, EquivalencyAssertionOptions <IEnumerable> > config, string because = "", params object[] becauseArgs) { EquivalencyAssertionOptions <IEnumerable> options = config(AssertionOptions.CloneDefaults <IEnumerable>()); var context = new EquivalencyValidationContext { Subject = Subject, Expectation = expectation, RootIsCollection = true, CompileTimeType = typeof(IEnumerable), Because = because, BecauseArgs = becauseArgs, Tracer = options.TraceWriter }; new EquivalencyValidator(options).AssertEquality(context); }
/// <summary> /// Asserts that an object is equivalent to another object. /// </summary> /// <remarks> /// Objects are equivalent when both object graphs have equally named properties with the same value, /// irrespective of the type of those objects. Two properties are also equal if one type can be converted to another and the result is equal. /// The type of a collection property is ignored as long as the collection implements <see cref="IEnumerable{T}"/> and all /// items in the collection are structurally equal. /// </remarks> /// <param name="config"> /// A reference to the <see cref="EquivalencyAssertionOptions{TSubject}"/> 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{TSubject}"/> class. The global defaults are determined by the /// <see cref="AssertionOptions"/> class. /// </param> /// <param name="because"> /// An optional 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 <see cref="because" />. /// </param> public void BeEquivalentTo <TExpectation>(TExpectation expectation, Func <EquivalencyAssertionOptions <TExpectation>, EquivalencyAssertionOptions <TExpectation> > config, string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(config, nameof(config)); EquivalencyAssertionOptions <TExpectation> options = config(AssertionOptions.CloneDefaults <TExpectation>()); var context = new EquivalencyValidationContext { Subject = Subject, Expectation = expectation, CompileTimeType = typeof(TExpectation), Because = because, BecauseArgs = becauseArgs, Tracer = options.TraceWriter }; var equivalencyValidator = new EquivalencyValidator(options); equivalencyValidator.AssertEquality(context); }
/// <summary> /// Asserts that an object can be serialized and deserialized using the JSON serializer and that it stills retains /// the values of all members. /// </summary> /// <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 <see cref="because" />. /// </param> public static AndConstraint <ObjectAssertions> BeJsonSerializable <T>(this ObjectAssertions assertions, Func <EquivalencyAssertionOptions <T>, EquivalencyAssertionOptions <T> > options, string because = "", params object[] becauseArgs) { try { object deserializedObject = CreateCloneUsingJsonSerializer <T>(assertions.Subject); EquivalencyAssertionOptions <T> defaultOptions = AssertionOptions.CloneDefaults <T>() .RespectingRuntimeTypes().IncludingFields().IncludingProperties(); ((T)deserializedObject).ShouldBeEquivalentTo(assertions.Subject, _ => options(defaultOptions)); } catch (Exception exc) { Execute.Assertion .BecauseOf(because, becauseArgs) .FailWith("Expected {0} to be JSON serializable{reason}, but serialization failed with:\r\n\r\n{1}", assertions.Subject, exc.Message); } return(new AndConstraint <ObjectAssertions>(assertions)); }