June 30, 2013

Code Contract


Normally we use Debug.Assert() for code contracts. To stop the Debug.Assert() statements from activating during unit tests, add the following to te unit tests application config file ("App.Config"):
  <?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.diagnostics>
        <assert assertuienabled="false"/> <!-- Disable Debug.Assert() when running unit tests in debug mode. -->
    </system.diagnostics>
</configuration>
Following is a code contract type class that can make code assertions without throwing an Assert dialog during an NUnit test. Also the code contracts will be logged in release. A potential product failure in the field could be traced back to a code contract failure.
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace Common.CodeContracts
{
    /// <summary>
    /// A simple code contract class to replace Debug.Assert(). It can be used in release 
    /// and will log code contract failures to the Trace output window.
    /// </summary>
    public static class RuntimeCodeContract
    {
        // Switch on the Debugger.Break when required
        public static bool DebuggerBreakOnFailure { get; set; } = false;

        public static void Requires(bool condition,
                string message,
                [CallerFilePath] string file = "",
                [CallerMemberName] string member = "",
                [CallerLineNumber] int line = 0)
        {
            if (!condition)
            {
                var msg = $"Code contract failure detected: \"{message}\" in member \"{member}\" on line {line} in file \"{file}\"";
                Trace.TraceError(msg);
                if (DebuggerBreakOnFailure)
                {
                    Debugger.Break(); // Use the call stack window to find the invoker location
                }

                //throw new InvalidOperationException(msg);
            }
        }

        public static void RequiresArgument(bool condition,
                string paramName,
                string message = "",
                [CallerFilePath] string file = "",
                [CallerMemberName] string member = "",
                [CallerLineNumber] int line = 0)
        {
            if (!condition)
            {
                var msg = $"Argument requirement failure detected: {message} \"{paramName}\" in member \"{member}\" on line {line} in file \"{file}\"";
                Trace.TraceError(msg);
                if (DebuggerBreakOnFailure)
                {
                    Debugger.Break(); // Use the call stack window to find the invoker location
                }

                //throw new ArgumentException(message, paramName);
            }
        }

        public static void RequiresArgumentNotNull(object parameter,
                string paramName,
                [CallerFilePath] string file = "",
                [CallerMemberName] string member = "",
                [CallerLineNumber] int line = 0)
        {
            if (parameter == null)
            {
                var msg = $"Argument {paramName} was null in member {member} on line {line} in file {file}";
                Trace.TraceError(msg);
                if (DebuggerBreakOnFailure)
                {
                    Debugger.Break(); // Use the call stack window to find the invoker location
                }

                //throw new ArgumentNullException(msg, paramName);
            }
        }

    }
}
It is also a good example of using the compiler attributes "CallerFilePath", "CallerMemberName", and "CallerLineNumber" for diagnostics.

No comments: