在.NET中如何进行单元测试属性?

3
我正在开发一款想要以开源形式发布的库,我已经开始为代码编写测试,并且我想知道如何测试.Net对象中的属性。比如说我有以下代码: public class Person{ #region variables private string _name = String.Empty; private string _surname = String.Empty; #region properties public string Name{ get{ return _name; } } public string Surname{ get{ return _surname; } set{ _surname = value; } } }
我的两个问题与此代码有关:
1. 如何对只有getter的属性进行单元测试(例如示例中的Name)? 2. 如何对既有setter又有getter的属性进行单元测试(例如示例中的Surname)?
我希望测试这些简单的属性,因为我已经在其他代码中发现了错误,Intellisense做了错误的自动完成,导致属性没有返回正确的变量。
更新:

2
提供一个更接近你实际工作的例子,这样回答会更容易。 - Rytmis
1
“它们确实有一些逻辑,并且很难调试。”听起来可能需要进行一些重构,然后您可以对逻辑的特定元素进行单元测试。 - Steven Evers
9个回答

10
不要浪费时间编写有关getter和setter的愚蠢测试,但是你应该测试任何公开的属性,如果不测试属性,则存在某些人可能在其中添加一些逻辑从而破坏其功能的风险。此外,不应该依赖其他测试对其进行测试。这使得测试易于断裂,并使得很难确定问题所在,因为测试将会测试多个内容。请注意保留HTML标记。

1
你当然可以为所有公共成员编写测试,但如果你关心单元测试的成本效益方面,那么不要为所有公共成员编写单元测试。实际上,你不想在像只包含简单 getter/setter 的属性这样的东西上编写测试。你从那个测试中得到什么好处呢?如果你的论点是后来可能会有人添加逻辑,那么这就是应该添加测试的时候了。 - Scott Marcus
一个很好的时机来对 setter 进行单元测试,就是当 setter 改变数据时,比如检查字符串长度、掩盖除最后 4 个字符以外的所有内容等。在 getter 中存在预期逻辑的情况下,这样的事情是可以进行单元测试的。我认为这是可以接受的。 - Casey ScriptFu Pharr

8

如何对只有getter的属性(如示例中的Name)进行单元测试

与测试有setter的属性并没有太大区别,只需要找到另一种确定输出结果的方法。可以在构造函数中或者通过其他setter/对象操作的结果得到。

[Test]
public void NamePropTest()
{
    Person p = new Person();

    //Some code here that will set up the Person object
    //  so that you know what the name will be

    Assert.AreEqual("some known value...", p.Name);
}

如果我们有Name和SurName的setter,但只有FullName的getter,测试可能如下所示:
[Test]
public void NamePropTest()
{
    Person p = new Person();

    p.Name = "Sean";
    p.Surname = "Penn";

    Assert.AreEqual("Sean Penn", p.FullName);
}

5

您应该测试属性。也要测试自动属性!

单元测试是为了确保对程序的更改不会破坏该程序。

您可能最终需要更改属性实现,因此您希望通过测试确保程序仍按预期工作。您可以使用测试来做到这一点。

即使您使用自动属性(作为字段/成员变量的替代),将它们设置为属性的原因在于以防以后您想要更改它们的实现。那么您会希望有测试存在。

编辑:(针对shahkalpesh的评论...)

如果您更改了实现,则测试可能还需要更改。因此,我不知道为什么有人要测试简单的get/set?

从这个类开始:

public class TimeOfDay
{
    public int Hour{get; private set;}
    public int Minute{get; private set;}

    public TimeOfDay(int hour, int minute)
    {
        Hour = hour;
        Minute = minute;
    }
}

更改实现时,测试仍然有效!
public class TimeOfDay
{
    public int _minutesSinceMidnight = 0;

    public int Hour
    {
        get { return _minutesSinceMidnight / 60; }
        set { _minutesSinceMidnight = value * 60 + Minutes; }
    }

    public int Minute
    {
        get { return _minutesSinceMidnight % 60; }
        set { _minutesSinceMidnight = Hour * 60 + value; }
    }

    public TimeOfDay(int hour, int minute)
    {
        Hour = hour;
        Minute = minute;
    }
}

加入一些日期和时间算术函数之类的东西,我希望测试能够表明一切仍然正常...


如果您更改了实现,测试也可能需要更改。所以,我不知道为什么有人要测试简单的get/set? - shahkalpesh
@shahkalpesh:我仍会编写测试。更改需求通常会导致更改测试(不仅是属性)。我不能不编写测试,因为将来可能需要更改。(我还在我的回答中添加了一个示例,说明为什么要这样做。) - Arjan Einbu
我能提出另一个观点吗?如果需要编写复杂逻辑,尤其是可能引发异常的情况下,从语义上讲,编写方法比编写属性更好。然后,对于该方法是否有覆盖范围,讨论也会更容易些。 - Sudhanshu Mishra

4

我认为如果你像之前那样写,应该进行测试。毕竟你可能会打错某些东西。

就像这样简单。

var person = New Person();
person.Surname = "test";
Assert.AreEqual("test", person.Surname);

总的来说,TDD和单元测试都是为了尽可能避免最多的错误。

但如果不小心写成这样。

public class Person{
    #region variables
    private string _name = String.Empty;
    private string _surname = String.Empty;
    #region properties
    public string Name{
        get{
             return _name;
        }
    }
    public string Surname{
        get{
            return _name;
        }
        set{
            _name = value;
        }
    }
}

如果你没有这样做,那么你可能会有一个错误。

测试自动属性可能不太有价值。但这是另一个问题。


那么,如果测试失败了,是getter还是setter出了问题呢?示例中的属性很简单,但现实世界中的属性并非如此,它们很难调试。 - mandel
然后提出你想要问的问题,不要试图过于简化。人们只会回答问题,而不是你认为的问题。 - chrissie1
@chrissie 我认为编写200行代码不是一个好主意,如果我使用简单属性,甚至不会编写它们,而是让编译器为我完成工作。 - mandel
那么你肯定应该对它们进行测试。但如果你的 setter 有 200 行代码,那么你就有 SRP 问题。如果我们不得不猜测问题,那么我们无法给出建议。 - chrissie1
我认为单一职责原则(SRP)侧重于类而不是方法或属性。在这种情况下,对象负责处理它的数据,唯一的问题是它以一种慢吞吞的方式操作并且必须检查数据库。 - mandel
归根结底,TDD和单元测试总的来说都是为了尽可能避免最多的错误。-- TDD关乎设计,低错误率只是一个愉快的副作用。 - Esko Luontola

1
我的问题更多地涉及如何编写测试。我一直在思考,我需要访问私有值以确保属性按预期工作,否则我不知道该怎么做。https://stackoverflow.com/users/59332/mandel 好的...既然你坚持要知道如何做...
[Test]
TestMethod()
{

    Person p = new Person();
    p.Name = "a name";
    p.Surname = "a surname";

    Assert.That(p.Name, Is.EqualTo("a name"));
    Assert.That(p.Surname, Is.EqualTo("a surname"));
}

然而,这仅适用于您拥有setter的情况下...

如果您只有getter,则我只能想到两种方法来实现此目的。

  1. 预先知道返回值并针对该值进行断言。
  2. 使用反射设置该值,然后针对该已知值进行断言。

更好的建议是放弃测试那些没有实际增加软件价值的getter和setter。除非它们背后有复杂的东西...但这种情况很少见。


是的,但在这种情况下,您的测试使得getter和setter相关,这意味着在某些时候您假定getter或setter肯定有效。它还假定您在Name中有一个getter,但实际上并非如此...这些属性很简单,因为它们只是示例... - mandel

1

这些属性应该存在,因为你有需要它们的用途。至于这些,应该有单元测试,你应该已经对它们进行了覆盖。 如果没有需要它们的情况,它们可能根本不应该存在。


我的问题更多地涉及如何编写测试。我一直在思考,我需要访问私有值以确保属性按预期工作,否则我不知道该怎么做。 - mandel

1

据我所知,您不应该测试属性(即那些简单的get/set属性)。

我不确定您使用的是哪个版本的c#。但是,您可以使用自动属性来避免您面临的简单set/get问题。

请参见此链接


1

不要浪费时间编写有关getter和setter的愚蠢测试。

另一个测试可能会设置名称,然后获取该属性,以便您可以覆盖getter的代码。


@mandel明确表示他的属性具有逻辑性,因此测试必须支持该逻辑。 - Peter Lillevold

1

我认为你的只读属性使用了私有字段或其他私有数据。如果你需要设置它们,唯一的方法是通过反射获取它们(请参阅System.Reflection中的所有(something)Info类)。但是这是否是一个好的实践存在争议。


如果他创建的变量只能通过反射访问,我会感到惊讶。这些变量将具有一个值,并且可能还有一种设置该值的方式(如果它不是默认值),即使这种方式不像设置属性那样直接明了。 - Arjan Einbu
没错,但我认为他想测试这个单独的元素,以便那些私有字段的输入不会干扰。 - Migol

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