Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it OK to use Assert as preconditions?

I was going through this post related to handling of nulls.

One of the recommendation (as per the SO post) is to use Assert when nulls are not valid.

I have (so far) used nulls extensively in Test Projects. It looks strange to me to use Asserts statement in normal code (other than test projects).

Why -> Because i have never used it this way, never read in any book about it also.

Questions
1. Is it Ok to use Asserts for preconditions
2. Pros/Cons of Asserts over Checking the parameters and throwing Argument___Exception

If it matters, I am asking for .NET (not for java)

like image 585
Tilak Avatar asked Dec 19 '12 16:12

Tilak


3 Answers

You might want to look into Code Contracts. They provide both static and runtime checking of your methods, and you can apply them to your interfaces as well, so that the contracts become part of your public API.

As an example, from copied from some of my own code (this code implements contracts on an interface):

using System;
using System.Diagnostics.Contracts;

using Project.Contracts.Model.Accounts;
using Project.Contracts.Services;

/// <summary>
/// Data access for accounts.
/// </summary>
[ContractClass(typeof(AccountRepositoryContract))]
public interface IAccountRepository
{
    /// <summary>
    /// Gets the user by id.
    /// </summary>
    /// <param name="userId">The user id.</param>
    /// <returns>The user, or <c>null</c> if user not found.</returns>
    [Pure]
    User GetUserById(int userId);
}

/// <summary>
/// Contract class for <see cref="IAccountRepository"/>.
/// </summary>
[ContractClassFor(typeof(IAccountRepository))]
internal abstract class AccountRepositoryContract : IAccountRepository
{
    /// <summary>
    /// Gets the user by id.
    /// </summary>
    /// <param name="userId">The user id.</param>
    /// <returns>
    /// The user, or <c>null</c> if user not found.
    /// </returns>
    public User GetUserById(int userId)
    {
        Contract.Requires<ArgumentException>(userId > 0);

        return null;
    }
}

A simpler, yet more comprehensive, example:

public class Foo
{
    public String GetStuff(IThing thing)
    {
        // Throws ArgumentNullException if thing == null
        Contract.Requires<ArgumentNullException>(thing != null);

        // Static checking that this method never returns null
        Contract.Ensures(Contract.Result<String>() != null);

        return thing.ToString();
    }
}
like image 195
Steve Czetty Avatar answered Sep 26 '22 11:09

Steve Czetty


This post is only about Microsoft.VisualStudio.TestTools.UnitTesting.Assert, not Debug.Assert.

I would not recommend that, since the Assert class is in the Microsoft.VisualStudio.TestTools.UnitTesting namespace, which is testing stuff rather than production tools. They're in a separate assembly too which you should not need to reference from non-testing code.

ArgumentException (illegal argument, with ArgumentNullException and ArgumentOutOfRangeException for further splitting) and and InvalidOperationException (illegal state) are there for condition checking.

As an alternative, there are code contracts too. See Steve's answer.

like image 38
Matthias Meid Avatar answered Sep 25 '22 11:09

Matthias Meid


They are effectively two different Asserts. The Assert you use in unit tests is for triggering a test failure (and of course, they are always tested). The Asserts you use in other code are a separate function (System.Diagnostics.Debug.Assert()), and are used as a helping tool during development, to alert you if expected conditions are not satisfied. However, these assertions are only tested in debug builds. If you make a release build, the assert will have no effect. So it is not a general error-handling tool, and you should not plan on it to catch errors.

It is simply to catch logic errors during testing and development, to tell you if your assumptions hold. And as such, yes, it is very useful for testing pre- and postconditions.

Note that this kind of Assert is a bit contentious, because it may become a crutch you end up using instead of proper error handling, and since it has no effect in release builds, you may end up releasing software with nonexistent error handling. (For example, don't use Assert to check that a file exists. It's a real error that can actually occur in the real world, so it needs real-world error handling.) But personally, I think it's a very useful tool, for testing pre- and postconditions without having to worry about the performance overhead (since the test gets removed in release builds, it is effectively free, and it triggers the debugger when the assertion fails, rather than just throwing an exception)

like image 27
jalf Avatar answered Sep 22 '22 11:09

jalf