C# 如何为“编写单元测试”;T必须是引用类型“;?

C# 如何为“编写单元测试”;T必须是引用类型“;?,c#,unit-testing,tdd,C#,Unit Testing,Tdd,考虑: class MyClass<T> where T : class { } class MyClass,其中T:class { } 在这种情况下,where子句强制规定MyClass只是引用类型的泛型 理想情况下,我应该有一个单元测试来测试这个规范。然而,这个单元测试显然不起作用,但它解释了我试图实现的目标: [Test] [DoesNotCompile()] public void T_must_be_a_reference_type() { var test =

考虑:

class MyClass<T> where T : class
{
}
class MyClass,其中T:class
{
}
在这种情况下,where子句强制规定MyClass只是引用类型的泛型

理想情况下,我应该有一个单元测试来测试这个规范。然而,这个单元测试显然不起作用,但它解释了我试图实现的目标:

[Test]
[DoesNotCompile()]
public void T_must_be_a_reference_type()
{
    var test = new MyClass<int>();
}
[测试]
[DoesNotCompile()]
public void T_必须是_a_reference_type()
{
var test=新的MyClass();
}
如何测试通过不允许编译代码实现的规范

编辑

更多信息:好的,我这样做的理由(哈哈)是我一直遵循一种TDD方法,在这种方法中,除非单元测试失败,否则不能编写任何代码。假设你有这个:

class MyClass<T> { }
类MyClass{}
如果T不是一个类,你能写什么测试会失败?类似于
default(T)==null的内容

进一步编辑


因此,在对此进行了“根本原因分析”之后,问题在于我依赖于
default(T)
以一种隐式的方式在此类消费者中为
null
。我能够将消费者代码重构到另一个类中,并在那里指定一个泛型类型限制(将其限制为
),如果有人要删除我上面提到的类的限制,这将有效地使代码无法编译。

您不应该测试编译器是否工作。如果您在代码中指定这一点,这就足够了。从代码的角度来看,这与以下内容大致相同:

[Test]
public void Test_whether_add_works()
{
    int i = 1 + 2;

    Assert.AreEqual(3, i);
}

您是否编写了正确的单元测试?它看起来像是要测试C#编译器,但不是你的代码。

为什么需要单元测试?您是否为以下方法编写单元测试:

public void Foo(string x)
检查它是否只能接受字符串,而不能接受整数?如果没有,你认为有什么区别

编辑:只是稍微少一点异想天开:在本例中,规范由声明验证。测试通常应该测试行为。这是我喜欢代码契约的一个方面:我觉得没有必要对契约进行单元测试,除非它们表达了一些复杂的东西——在这种情况下,我要测试的是复杂性,而不是“契约被强制执行”这一点

编辑:要回答问题,请编辑:

如果T不是一个类,你能写什么测试会失败

你可以这样写:

Type definition = typeof(MyClass<>);
Assert.Throws<ArgumentException>(() => definition.MakeGenericType(typeof(int)));
Type definition=typeof(MyClass);
抛出(()=>definition.MakeGenericType(typeof(int));

然而,这似乎违背了测试的真正目的…

这是一个很好的问题!非常同意您的测试驱动方法。你所挣扎的实际上是两种范式的冲突。旧范式认为程序应该在不运行它们的情况下使用数学证明是正确的(这是我们职业的数学祖先的遗产),新范式认为程序应该通过执行示例用例(即测试)来证明是正确的。所以你要尝试的是将新范式的实践应用到旧范式的工件上,这当然不会真正起作用


关于类型和测试的二分法的更多信息,请参阅Chris Smith的这篇优秀文章,

好吧,我正试图让我的单元测试包括被测单元的所有规格。@Jon有趣的是,我们的想法完全相同example@ScottWhitlock-但代码编译只是你的测试。@Scott:我认为TDD不需要这个。同样,您不会编写测试来确保不能传递无效参数。您的测试驱动设计的积极方面(“我可以使用引用类型参数”),但不一定是消极方面。归根结底,所有测试都应该是务实的——但我认为,测试编译器并不是对你时间的务实利用。好吧,你解释了为什么我不需要测试,并给了我一个有效的答案,为你+2!:为什么不单元测试类名为
MyClass
?@SLaks:technology-var-test=new-MyClass();我想@SLaks是在用讽刺来证明这一点,你不需要测试编译器。。。这是微软的工作。@TheCloudlessky:我的观点是,如果你遵循TDD,除非你编写了第一个单元测试,否则你不会创建这个类,除非你使用了一个不存在的类的名称,否则这个类就不会编译。是的,您的第一个单元测试隐式地测试了类的名称。“[test]public void Running1984(){Assert.AreEqual(5,2+2);}”