Exemple #1
0
        /// <summary>
        /// Makes an assertion about an exception raised by the previous action.
        /// </summary>
        /// <param name="description">A string describing the assertion being made. This should be a short sentence
        /// that starts with 'it ...', but eliding the 'it'. For example, "puts the lotion in the bucket" would be an
        /// acceptable value.</param>
        /// <param name="assertion">The assertion to be made. It should throw an <see cref="AssertionFailedException"/>
        /// if it fails.</param>
        /// <returns>A spec under which additional actions and assertions can be made.</returns>
        /// <remarks>This assertion has the side effect of making the preceeding action no longer fail when an
        /// exception is thrown.</remarks>
        public XSpec TheException(string description, Func <Exception, bool> assertion)
        {
            if (description == null)
            {
                throw new ArgumentNullException("description");
            }
            if (assertion == null)
            {
                throw new ArgumentNullException("assertion");
            }

            XSpec parent  = null;
            XSpec newNode = AddNode(
                description,
                () =>
            {
                if (parent.exception == null)
                {
                    throw new AssertFailedException("No exception was thrown.");
                }
                Assert.IsTrue(assertion(parent.exception));
            },
                NodeType.TheException, NodeType.It);

            parent = newNode.parent;
            parent.swallowExceptions = true;

            return(newNode);
        }
Exemple #2
0
        static void Run(XSpec root)
        {
            var            stack = new List <XSpec>();
            List <XSpec[]> tests = root.GatherTests(stack).ToList();

            for (int i = 0; i < tests.Count; i++)
            {
                XSpec[] test = tests[i];
                for (int j = 0; j < test.Length; j++)
                {
                    if (!test[j].Exec())
                    {
                        break;
                    }
                }
            }

            var  builder = new StringBuilder();
            bool passed  = root.Report(builder, 0);

            if (!passed)
            {
                throw new AssertFailedException("\r\n" + builder.ToString());
            }
        }
Exemple #3
0
 XSpec(string description, Action action, XSpec parent, NodeType type)
 {
     this.action      = action;
     this.description = description;
     this.parent      = parent;
     this.type        = type;
 }
Exemple #4
0
        /// <summary>
        /// Asserts that the previous action should throw an exception of the specified type.
        /// </summary>
        /// <typeparam name="TException">The type of exception that should have been thrown.</typeparam>
        /// <returns>A spec under which additional actions and assertions can be made.</returns>
        /// <remarks>This assertion has the side effect of making the preceeding action no longer fail when an
        /// exception is thrown; instead, the exception will be recorded and this assertion will make sure it was of
        /// the specified type.</remarks>
        /// <example>
        /// <code>
        ///[TestMethod]
        ///public void WhenIncrementingAnInteger()
        ///{
        ///    int x = 0;
        ///    XSpec.Given( "an integer, set to zero", () => x = 0 )
        ///        .When( "the integer is incremented", () => x++ )
        ///            .It( "can be incremented again to two", () => { x++; Assert.AreEqual( x, 2 ); } )
        ///            .It( "should be 1", () => x == 1 )
        ///        .When( "the integer is incremented again", () => x++ )
        ///            .It( "should be 2", () => x == 2 )
        ///        .When( "the integer is divided by zero", () => x = x / ( x - 2 ) )
        ///            .ItShouldThrow&lt;DivideByZeroException>()
        ///    .Go();
        ///}
        /// </code>
        /// </example>
        public XSpec ItShouldThrow <TException>()
        {
            XSpec parent = null;

            XSpec newNode = AddNode(
                "should throw " + typeof(TException).Name,
                () =>
            {
                if (parent.exception == null)
                {
                    throw new AssertFailedException("No exception was thrown.");
                }
                Exception exception          = parent.exception;
                AggregateException aggregate = parent.exception as AggregateException;
                if (aggregate != null)
                {
                    exception = aggregate.Flatten().InnerException;
                }
                Assert.IsInstanceOfType(exception, typeof(TException));
            },
                NodeType.It);

            parent = newNode.parent;
            parent.swallowExceptions = true;

            return(newNode);
        }
Exemple #5
0
        /// <summary>
        /// Executes the specification, in a less-isolated fashion.
        /// </summary>
        /// <exception cref="AssertionFailedException">The specification validation has failed.</exception>
        /// <remarks>A log of the execution is written to stdout.
        /// <para>This is a much faster way to run tests than <see cref="GoIsolated"/>; instead of isolating each
        /// assertion, this execution mode runs each step until the first failure; it then backs up and runs everything
        /// up to the first failure, skipping any assertions along the way. The upshot is that if an assertion fails,
        /// we re-run the <see cref="Given"/>s and <see cref="Where"/>s to re-establish context, and then continue on.
        /// </para>
        /// <para>This has the quadratic run time when all the assertions fail, but a linear run time when they all
        /// pass, which is most of the time.</para>
        /// </remarks>
        public void GoQuick()
        {
            XSpec root = this;

            while (root.parent != null)
            {
                root = root.parent;
            }
            RunQuick(root);
        }
Exemple #6
0
        /// <summary>
        /// Executes the specification.
        /// </summary>
        /// <exception cref="AssertionFailedException">The specification validation has failed.</exception>
        /// <remarks>A log of the execution is written to stdout.</remarks>
        /// <example>
        /// <code>
        ///[TestMethod]
        ///public void WhenIncrementingAnInteger()
        ///{
        ///    int x = 0;
        ///    XSpec.Given( "an integer, set to zero", () => x = 0 )
        ///        .When( "the integer is incremented", () => x++ )
        ///            .It( "can be incremented again to two", () => { x++; Assert.AreEqual( x, 2 ); } )
        ///            .It( "should be 1", () => x == 1 )
        ///        .When( "the integer is incremented again", () => x++ )
        ///            .It( "should be 2", () => x == 2 )
        ///        .When( "the integer is divided by zero", () => x = x / ( x - 2 ) )
        ///            .ItShouldThrow&lt;DivideByZeroException>()
        ///    .Go();
        ///}
        /// </code>
        /// </example>
        public void GoIsolated()
        {
            XSpec root = this;

            while (root.parent != null)
            {
                root = root.parent;
            }
            Run(root);
        }
Exemple #7
0
        /// <summary>
        /// Adds a new node to the this spec, with an explicit precedence override. The new node may or may not be a
        /// child of this one.
        /// </summary>
        /// <param name="description">The description associated with the node.</param>
        /// <param name="action">The action associated with the node.</param>
        /// <param name="type">The type of the node.</param>
        /// <param name="precedence">The type to use as the precedence of this node. This parameter exists specifically
        /// so that 'TheException' and 'It' nodes can be peers in the tree, rather than one being the child of the
        /// other.</param>
        /// <returns>The new node.</returns>
        XSpec AddNode(string description, Action action, NodeType type, NodeType precedence)
        {
            XSpec parent = this;

            while (parent.type >= precedence)
            {
                parent = parent.parent;
            }

            var node = new XSpec(description, action, parent, type);

            parent.children.Add(node);
            return(node);
        }
Exemple #8
0
        static void RunQuick(XSpec root)
        {
            XSpec[] tests = root.GatherTestsQuick().ToArray();
            int     end   = 0;

            while (end < tests.Length)
            {
                // First, fast-forward, resetting state until we get up to just past our last failure.
                bool failedPrereqs = false;
                for (int i = 0; i < end; i++)
                {
                    if (tests[i].type > NodeType.When)
                    {
                        continue;
                    }
                    if (!tests[i].Exec())
                    {
                        failedPrereqs = true;
                        break;
                    }
                }

                if (failedPrereqs)
                {
                    break;
                }
                for ( ; end < tests.Length; end++)
                {
                    if (!tests[end].Exec())
                    {
                        break;
                    }
                }
                end++; // Skip forward, so we can make progress.
            }

            var  builder = new StringBuilder();
            bool passed  = root.Report(builder, 0);

            if (!passed)
            {
                throw new AssertFailedException("\r\n" + builder.ToString());
            }
        }