unit testing应该写在吸气和吸气器上吗?

我们是否应该为我们的获得者和制定者编写testing或者是否过分矫枉过正?

我会说不。

@威尔说,你应该瞄准100%的代码覆盖率,但在我看来,这是一个危险的分心。 你可以编写100%覆盖率的unit testing,但是什么都不testing。

unit testing在那里以一种富有performance力和有意义的方式来testing你的代码的行为,getter / setter只是达到目的的一种手段。 如果你testing使用getters / setters来实现testing“真实”function的目标,那就足够了。

另一方面,如果你的getter和setter做的不仅仅是获取和设置(即他们是复杂的方法),那么是的,他们应该被testing。 但是不要写一个unit testing用例来testing一个getter或者setter,这是浪费时间。

TDD的回答是肯定的


注意 :这个答案不断得到upvotes,尽pipe可能是一个坏build议。 为了理解为什么,看看它的小妹妹 。

如果你在这里加强你的信仰 – 我们都喜欢增援。 但是如果你想要更聪明,考虑一下姐姐。


有争议的是,但我认为任何回答“否”的人都缺lessTDD的基本概念。

对我来说,如果你遵循TDD,答案是肯定的 。 如果你不是,那么不是一个合理的答案。

TDD中的DDD

TDD经常被认为具有主要优点。

  • 防御
    • 确保代码可能会改变,不会 改变 其行为
    • 这允许重构的这么重要的实践。
    • 你获得这个TDD与否。
  • devise
    • 您可以指定应该做什么, 在执行之前应该如何performance。
    • 这通常意味着更多的知情实施决定
  • 文档
    • testing套件应作为规范 (要求)文件。
    • 为此目的使用testing意味着文档和实现总是处于一致的状态 – 更改为一个意味着更改为其他。 与单独的文档文件保存需求和devise相比较。

分开负责执行

作为程序员,把属性看作是有意义的东西是非常诱人的,而把getter和setter作为某种开销。

但属性是一个实现细节,而setter和getter是实际上使程序工作的契约界面。

拼出一个对象应该更加重要:

允许其客户改变其状态

允许其客户查询其状态

那么这个状态是如何被实际存储的(对于哪个属性是最常见的,但不是唯一的方式)。

一个testing如

(The Painter class) should store the provided colour 

对于TDD的文档部分很重要。

当你编写testing时,最终实现是微不足道的(属性)并且没有防御利益的事实应该是未知的。

缺乏往返工程…

系统开发世界中的一个关键问题是缺乏往返工程 1 – 系统的开发过程被分割成不相关的子过程,其文档(文档,代码)往往不一致。

1 Brodie,Michael L.“John Mylopoulos:缝制概念模型的种子”。 概念build模:基础和应用。 斯普林格柏林海德堡,2009年1-9。

…以及TDD如何解决这个问题

TDD的文档部分确保系统规范和代码始终保持一致。

先devise,稍后再实施

在TDD内我们先写失败的验收testing,然后写出让他们通过的代码。

在更高层次的BDD中,我们首先编写场景,然后让它们通过。

为什么要排除setter和getter?

从理论上讲,在TDD中完全有可能为一个人编写testing,而另一个人则实现使其通过的代码。

所以问问自己:

编写class级考试的人应该提到getter和setter。

由于getter和setter是类的公共接口,答案显然是肯定的 ,否则将无法设置或查询对象的状态。 然而 ,这样做的方式不一定是通过单独testing每种方法,更多地看到我的其他答案 。

显然,如果你先写代码,答案可能就不那么清楚了。

Roy Osherove在他着名的“unit testing艺术”一书中说:

属性(Java中的getter / setter)是通常不包含任何逻辑的代码的好例子,不需要testing。 但要小心:一旦你在房产内添加任何支票,你就要确保逻辑正在被testing。

tl; dr: 是的,应该和OpenPojo一起,这是微不足道的。

  1. 你应该在你的getter和setter中做一些validation,所以你应该testing它。 例如, setMom(Person p)不应该允许任何比他们年轻的人作为自己的母亲。

  2. 即使你现在没有这样做,将来你也会有机会,那么这对于回归分析是有利的。 如果你想允许把母亲设置为null那么你应该有一个testing,如果稍后有人改变,这将加强你的假设。

  3. 一个常见的错误是void setFoo( Object foo ){ foo = foo; } void setFoo( Object foo ){ foo = foo; }它应该是void setFoo( Object foo ){ this.foo = foo; } void setFoo( Object foo ){ this.foo = foo; } 。 (在第一种情况下,正在写入的foo 参数 不是 对象上的foo字段)。

  4. 如果你正在返回一个数组或者集合,你应该testing这个getter是否会在返回之前传递给setter的数据的防御性拷贝 。

  5. 否则,如果你有最基本的setter / getters,那么对它们进行unit testing最多可能会增加10分钟,那么这个损失是多less呢? 如果你添加行为,你已经有一个框架testing,你可以免费得到这个回归testing。 如果你使用的是Java,那么你没有任何借口,因为有OpenPojo 。 您可以启用一组现有的规则,然后与您的整个项目一起扫描,以确保它们在您的代码中一致地应用。

从他们的例子 :

 final PojoValidator pojoValidator = new PojoValidator(); //create rules pojoValidator.addRule( new NoPublicFieldsRule () ); pojoValidator.addRule( new NoPrimitivesRule () ); pojoValidator.addRule( new GetterMustExistRule () ); pojoValidator.addRule( new SetterMustExistRule () ); //create testers pojoValidator.addTester( new DefaultValuesNullTester () ); pojoValidator.addTester( new SetterTester () ); pojoValidator.addTester( new GetterTester () ); //test all the classes for( PojoClass pojoClass : PojoClassFactory.getPojoClasses( "net.initech.app", new FilterPackageInfo() ) ) pojoValidator.runValidation( pojoClass ); 

如果getter和/或setter的圈复杂度是1(通常是这样),那么答案是否定的,你不应该这样做。

所以除非你有一个需要100%代码覆盖率的SLA,否则不要打扰,并专注于testing软件的重要方面。

PS记住要区分getter和setter,即使在像C#这样的语言中,属性可能看起来像是一样的东西。 二传手的复杂度可以高于吸气器,从而validation一个unit testing。

尽pipe属性有合理的理由,但有一个共同的面向对象devise的观点认为,通过属性暴露成员状态是不好的devise。 罗伯特·马丁关于开放闭原则的文章扩展了这一点,他指出,属性鼓励耦合,因此限制了修改类的能力 – 如果修改属性,所有类的消费者也需要改变。 他认为,暴露成员variables不一定是坏的devise,它可能只是风格不佳。 但是,如果属性是只读的,滥用和副作用的可能性就会降低。

我可以为unit testing提供最好的方法(这可能看起来很奇怪)是尽可能多地保护属性或内部属性。 这将阻止耦合,同时阻止写入愚蠢的testing吸气剂和制定者。

读/写属性应该使用的显而易见的原因,例如绑定到input字段的ViewModel属性等等。

实际上,unit testing应该通过公共方法来驱动function。 如果您正在testing的代码恰好使用这些属性,则可以免费获得代码覆盖率。 如果事实certificate,这些属性从来没有被代码覆盖强调,那么存在以下非常强的可能性:

  1. 您缺less间接使用属性的testing
  2. 这些属性是未使用的

如果你为getter和setter编写testing,你会得到错误的覆盖感,并且无法确定这些属性是否被function行为实际使用。

是的,但并不总是孤立的

请允许我详细说明:

什么是unit testing?

有效使用遗留代码 1

术语unit testing在软件开发方面有着悠久的历史。 对于unit testing的大多数概念来说,共同的观点是它们是单独testing软件的组件。 什么是组件? 定义各不相同,但在unit testing中,我们通常关心系统中最primefaces的行为单元。 在程序代码中,单位通常是function。 在面向对象的代码中,单位是类。

请注意,在OOP中,您可以findgetter和setter,单位是 ,不一定是单个方法

什么是好的testing?

所有要求和testing遵循Hoare逻辑的forms:

{P} C {Q}

哪里:

  • {P}是先决条件( 给定
  • C是触发条件( 何时
  • {Q}是后置条件( 当时

然后来了这个箴言:

testing行为,而不是执行

这意味着你不应该testingC如何达到后置条件,你应该检查{Q}是否是C的结果。

说到OOP, C是一个类。 所以你不应该testing内部效应,只能testing外部效应。

为什么不单独testingbean getter和setter

Getters和setter可能涉及到一些逻辑,但是这个逻辑只要这个逻辑没有外部影响 – 使它们成为bean访问器 2 )一个testing将不得不看着对象的内部,这不仅违反了封装,而且还testing了实现。

所以你不应该单独testingbean getter和setter。 这不好:

 Describe 'LineItem class' Describe 'setVAT()' it 'should store the VAT rate' lineItem = new LineItem() lineItem.setVAT( 0.5 ) expect( lineItem.vat ).toBe( 0.5 ) 

虽然setVAT会抛出一个exception,但是由于现在一个外部的影响,所以相应的testing是合适的。

你应该如何testinggetters和setter?

如果这样的变化对外界没有影响,即使这种影响稍后出现,也几乎没有任何意义可以改变对象的内部状态。

因此,对二传手和吸引者的testing应该关注这些方法的外部效应,而不是内部方法。

例如:

 Describe 'LineItem class' Describe 'getGross()' it 'should return the net time the VAT' lineItem = new LineItem() lineItem.setNet( 100 ) lineItem.setVAT( 0.5 ) expect( lineItem.getGross() ).toBe( 150 ) 

你可能会想:

等一下,我们正在testinggetGross()这里不是 setVAT()

但是,如果setVAT()发生故障,testing应该会失败。

1 Feathers,M.,2004。使用遗留代码有效地工作。 学徒霍尔专业。

2 Martin,RC,2009. Clean code:敏捷软件工艺手册。 培生教育。

一个幽默,但明智的采取: Testivus的方式

“今天写testing你可以”

如果你是一个经验丰富的testing人员,那么testinggetter / setter可能会过度,这是一个小项目。 但是,如果你刚刚开始学习如何进行unit testing,或者这些getter / setter可能包含逻辑(比如@ ArtB的setMom()例子),那么编写testing是一个好主意。

我对JUnit代码本身的覆盖范围做了一些分析 。

一类未发现的代码是“太简单了” 。 这包括简单的getter和setter,JUnit的开发者不会testing。

另一方面,JUnit没有任何(不被弃用的)方法长于3行,没有被任何testing覆盖。

这实际上是我和我之间最近的一个话题。我们拍摄了80%的代码覆盖率。 我的团队认为getter和setter是自动实现的,编译器在后台生成一些基本的代码。 在这种情况下,如果生成的代码是非侵入式的,那么testing编译器为您创build的代码是没有意义的。 我们也有关于asynchronous方法的讨论,在这种情况下,编译器会在后台生成一大堆代码。 这是一个不同的案例,我们正在testing。 长话短说,把它带上你的团队,自己决定是否值得testing。

另外,如果您正在使用像我们这样的代码覆盖率报告,则可以添加[ExcludeFromCodeCoverage]属性。 我们的解决scheme就是使用getter和setter属性的模型,或者属性本身。 这样,当代码覆盖率报告运行时,它不会影响总代码覆盖率%,假设您正在使用它来计算代码覆盖率百分比。 快乐的testing!