5

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)

Community
  • 1
  • 1
Tilak
  • 30,108
  • 19
  • 83
  • 131
  • 1
    Isn't `Assert` method conditioned to the DEBUG switch (via `[Conditional("DEBUG")]`, and thus, useful only at development time? – Steve B Dec 19 '12 at 16:50

4 Answers4

8

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();
    }
}
Steve Czetty
  • 6,147
  • 9
  • 39
  • 48
  • +1 If the intent is testing assumptions (for example, the in-parameters have values) in a method, then code contracts are the way to go. – Chuck Conway Dec 19 '12 at 17:10
  • Then again you might not want to look at because even if was not a buggy library you really do not need yet another source of complexity and friction with little or no material benefit. – Rick O'Shea Jan 18 '17 at 19:18
2

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.

Community
  • 1
  • 1
Matthias Meid
  • 12,455
  • 7
  • 45
  • 79
  • There's a `Debug.Assert` as well as the UnitTesting.Assert`. They're completely separate, and the former is intended for use outside unit tests. :) – jalf Dec 19 '12 at 16:56
2

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)

jalf
  • 243,077
  • 51
  • 345
  • 550
0

Personally, if I were to handle nulls in a method then I would definatly not use an assert within said method. Instead, as you say throw an exception.

Essentially, when an assert fails - it is basically an exception anyway. If you were to throw a ArgumentNullException I would refrain from using try catch within the method itself to do anything with it, but handle that within your unit test.

public class A
{
  public double Add(double a, double b)
  {
    if (a == null || b == null)
      throw new ArgumentNullException("");
    return a + b;
  }
}

[TestClass()]
public UnitTest
{
  [TestMethod()]
  public void ATest()
  {
    try
    {
      double d = new A().Add(null, 1);
    }
    catch(ArgumentNullException ex)
    {
      //Fail the test
      Assert.Fail("");
      //Or throw, this will allow you to double click the exception in the unit test and go to the line of code which failed
      throw ex;
    }  
  }
}
LukeHennerley
  • 6,344
  • 1
  • 32
  • 50
  • [ExpectedException](http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.expectedexceptionattribute(v=vs.80).aspx) can be used instead of try/catch for cleaner syntax. – Tilak Dec 19 '12 at 17:01
  • @Tilak Thanks! Wasn't aware of that :) How will this attribute handle the exception? Will it just fail the test or display the exception so that when you double click it, it will hit the line of occurence? – LukeHennerley Dec 19 '12 at 17:03
  • 1
    [Example](http://msdn.microsoft.com/en-us/library/ms379625%28VS.80%29.aspx#vstsunittesting%5Ftopic5) – Tilak Dec 19 '12 at 17:06