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));
        }
예제 #3
0
        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);
        }
예제 #6
0
        /// <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));
        }