使用Assert作为前提条件是否可以?

时间:2012-12-19 16:48:44

标签: c# .net null assert illegalargumentexception

我正在经历与处理空值相关的post

其中一个建议(根据SO帖子)是在空值无效时使用Assert。

我(到目前为止)在测试项目中广泛使用了null。在普通代码(测试项目除外)中使用Asserts语句对我来说很奇怪。

为什么 - >因为我从未以这种方式使用它,所以也不要在任何关于它的书中阅读。

问题
1.在先决条件下使用断言是否可以 2.检查参数和抛出参数___例外的断言的优缺点

如果重要,我要求.NET(不适用于java)

4 个答案:

答案 0 :(得分:7)

您可能需要查看Code Contracts。它们提供方法的静态和运行时检查,并且您也可以将它们应用于接口,以便合同成为公共API的一部分。

作为一个例子,从我自己的一些代码中复制(这段代码在接口上实现契约):

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;
    }
}

一个更简单但更全面的例子:

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();
    }
}

答案 1 :(得分:2)

此帖子关于 Microsoft.VisualStudio.TestTools.UnitTesting.Assert ,而不是Debug.Assert

我不建议这样做,因为Assert class位于Microsoft.VisualStudio.TestTools.UnitTesting命名空间中,它正在测试内容而不是生产工具。它们也在一个单独的程序集中,您不需要从非测试代码中引用它。

ArgumentException(非法参数,ArgumentNullExceptionArgumentOutOfRangeException用于进一步拆分)和InvalidOperationException(非法状态)用于条件检查。

作为替代方案,也有代码合同。请参阅Steve's answer

答案 2 :(得分:2)

它们实际上是两种不同的断言。您在单元测试中使用的Assert用于触发测试失败(当然,它们总是经过测试)。您在其他代码中使用的断言是一个单独的函数(System.Diagnostics.Debug.Assert()),在开发过程中用作帮助工具,以在不满足预期条件时提醒您。但是,这些断言仅在调试版本中进行 测试。如果您进行发布构建,则断言将不起作用。所以它不是一般的错误处理工具,你不应该计划它来捕获错误。

只是在测试和开发过程中捕获逻辑错误,告诉你你的假设是否成立。因此,是的,它对测试前置和后置条件非常有用。

请注意,这种断言有点引起争议,因为它可能会成为您最终使用而不是进行正确错误处理的拐杖,并且由于它在发布版本中没有任何影响,您可能会结束发布不存在错误处理的软件。 (例如,不要使用Assert来检查文件是否存在。这是真实的错误,实际上可能发生在现实世界中,所以它需要真实的错误处理。)但就个人而言,我认为这是一个非常有用的工具,用于测试前置条件和后置条件而不必担心性能开销(因为在发布版本中删除了测试,它实际上是免费的,并且在断言失败时触发调试器,而不是仅仅抛出异常)

答案 3 :(得分:0)

就个人而言,如果我在方法中处理空值,那么我肯定会在所述方法中使用assert。相反,正如你所说的抛出异常。

基本上,当一个断言失败时 - 无论如何它基本上都是一个例外。如果你要抛出一个ArgumentNullException,我会避免在方法本身中使用try catch来对它做任何事情,但是在你的单元测试中处理它。

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;
    }  
  }
}