NUnit 3.0的TestCase常量自定义对象参数

6

我已经编写了类SomeObject,我想定义一个const实例来在我的TestCase中保留/重复使用。我应该如何重写下面的代码以实现这种行为?

[TestFixture]
public class SomeObjectTests
{
    private const SomeObject item0 = new SomeObject(0.0); // doesn't work

    [TestCase(item0, ExpectedResult = 0.0)]
    public double TestSomeObjectValue(SomeObject so)
    {
        return so.Value;
    }

    [TestCase(item0, ExpectedResult = "0.0")]
    public string TestSomeObjectValueString(SomeObject so)
    {
        return so.ValueString;
    }
}

我得到了以下错误消息:

除字符串以外的引用类型的常量字段只能用null进行初始化。


从你的代码中删除“const”,它只是静态的,不会保持不变。 - Habib
4
使用 static readonly 代替 const。在 C# 中,并非所有类型都支持 const。该类型必须是编译时常量。请参见此处:https://dev59.com/DW025IYBdhLWcg3wEhrr。 - M.kazem Akhgary
编辑:添加了我收到的错误消息。我的问题是关于NUnit,还是我在C#方面缺少一些基本知识? - budi
1
在C#中,这是一些基本的东西。对象引用不能是编译时常量,因此不符合const的条件。然而,string是一个例外。 - Wai Ha Lee
1
@M.kazemAkhgary,您可以将const任何类型一起使用。每种类型都至少有一个编译时常量字面值。大多数类型无法创建任何有用的const值,因为该类型的大多数有意义的值没有编译时字面值。 - Servy
@WaiHaLee,您可以在自己的评论中解释为什么您的评论是错误的。没有根本规则说对象引用不能是常量。规则是所有常量必须是编译时字面量;只是您能创建的编译时字面量对象引用只有字符串而已。 - Servy
3个回答

9

实现你想要的功能更好的方法是使用TestCaseSource。在您的情况下:

[TestFixture]
public class SomeObjectTests
{
    [TestCaseSource(typeof(TestSomeObject),"TestCasesValue")]
    public double TestSomeObjectValue(SomeObject so)
    {
        return so.Value;
    }

    [TestCaseSource(typeof(TestSomeObject),"TestCasesValueString")]
    public string TestSomeObjectValueString(SomeObject so)
    {
        return so.ValueString;
    }
}

public class TestSomeObject
{
  public static IEnumerable TestCasesValue
  {
    get
    {
        yield return new TestCaseData( new SomeObject(0.0) ).Returns( 0.0 );
        yield return new TestCaseData( new SomeObject(1.0) ).Returns( 1.0 );
    }
  }

  public static IEnumerable TestCasesValueString
  {
    get
    {
        yield return new TestCaseData( new SomeObject(0.0) ).Returns( "0.0" );
        yield return new TestCaseData( new SomeObject(1.0) ).Returns( "1.0" );
    }
  }
}

TestCaseData是一种描述数据以及你想要对数据进行的操作的方式。通常情况下,你可以直接返回SomeObject,但由于你使用测试方法的返回值,因此你可以使用TestCaseData来描述预期的返回值。 - Daniel Rose

5
C# 语言规范第10.3节指出(重点在于“我”):

当需要符号名称表示一个常量值,但是该值的类型不允许在常量声明中使用,或者该值无法通过常量表达式在编译时计算时,可以使用只读字段(第10.4.2节


令人恼火的是,属性也有一些限制——请参见C# 语言规范第17.2节(同样,重点在于“我”):

如果以下所有语句都为真,则表达式 E 是一个属性参数表达式:

  • E 的类型是属性参数类型(第17.1.3节)。

  • 在编译时,E 的值可以解析为以下之一:

    • 一个常量值。

    • 一个 System.Type 对象。

    • 一个属性参数表达式的一维数组。

第17.1.3节:“属性参数类型”说:

属性类的位置和命名参数的类型限制为属性参数类型,它们是:

  • 以下类型之一:boolbytechardoublefloatintlongshortstring
  • object 类型。
  • System.Type 类型。
  • 枚举类型,前提是它具有公共可访问性,并且其中嵌套的类型(如果有)也具有公共可访问性(第17.2节)。
  • 上述类型的单维数组。

1: 引用的文本来自较早版本的 C# 规范 - 在 C# 5.0 版本中,还提到了四个额外的类型: sbyteuintulongushort


换句话说,你最好做的就是像这样:

[TestFixture]
public class SomeObjectTests {

    private static readonly SomeObject item0 = new SomeObject(0.0);

    private static SomeObject getObject(string key) {
        if ( key == "item0" )
            return item0;

        throw new ArgumentException("Unknown key");
    }

    [TestCase("item0", ExpectedResult = 0.0)]
    public double TestSomeObjectValue(string key) {
        SomeObject so = getObject(key);
        return so.Value;
    }

    [TestCase("item0", ExpectedResult = "0.0")]
    public string TestSomeObjectValueString(string key) {
        SomeObject so = getObject(key);
        return so.ValueString;
    }
}

这样,属性的参数是编译时常量,getObject方法可以处理获取SomeObject实例的操作。


没问题。不得不这样做有点可怕,但不幸的是这就是C#的方式。很高兴能帮助你! - Wai Ha Lee
引用的声明是错误的。您可以创建任何类型的const标识符,包括用户定义的类、结构体、数组等。您只是受限于将编译时常量值分配给它们,对于所有这些类,通常没有有意义的编译时常量需要分配给它们。 - Servy
1
@WaiHaLee 您可以使用编译时字面量 default(TheTypeOfVariableGoesHere) 创建任何有效的 C# 类型(值类型或引用类型)的 const 标识符。您还可以使用 typeof 创建类型为 Type 的编译时常量值。重新阅读后,我发现引语实际上是技术上正确的,这是我的错误,但您对它的结论不正确。即使您无法创建大多数类型的合理值,仍然可以创建这些类型的 const 标识符,并至少有一个有效值可分配给它。 - Servy
1
@JeppeStigNielsen - 经过进一步调查,你是正确的。语言规范 §10.3 (C# 5.0 中的 10.4) 表示:“常量声明中指定的类型必须是 sbytebyteshortushortintuintlongulongcharfloatdoubledecimalboolstring、一个枚举类型或引用类型。每个常量表达式必须产生目标类型的值或可以通过隐式转换转换为目标类型的类型的值”。 - Wai Ha Lee
1
同样,§17.1.3:“属性参数类型说:“属性类的位置和命名参数的类型仅限于属性参数类型,这些类型包括:(a)以下类型之一:boolbytechardoublefloatintlongshortstring,(b)object类型,(c)System.Type类型,(d)枚举类型,只要它具有公共可访问性并且它所嵌套的类型(如果有)也具有公共可访问性(§17.2),(e)上述类型的单维数组。”。 - Wai Ha Lee
显示剩余11条评论

0
1. 只需移除 const。它将成为每个实例的私有变量。 2. 将其设置为 static,它将成为该类的单例模式。 3. 用 readonly 替换 const。这会标记它为不应被更改的内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接