TDD:它是否阻碍了良好的APIdevise?
我从来没有写过TDD代码,但是在这里我已经看到了大量的讨论。 我最关心的是,好像一般的APIdevise(灵活性,易用性,界面的简单性和性能)似乎有时候会让代码变得模糊,超模块化,超越了任何API使用的必要案例等。例如,TDD支持者经常build议将事情作为parameter passing,从API抽象的angular度来看,被调用的方法应该“只是知道”,或者将类和方法考虑在内,以便于testing,这不一定是最好地涉及问题领域的方式。
对于在TDD和APIdevise方面更有经验的人员:您是否发现TDD经常妨碍良好的APIdevise? 如果是这样,你如何反击呢?
我已经使用TDD好几年了,我发现它从一开始就为API提供了两个不同的客户端,从而使APIdevise朝着更有用的devise发展。 你有生产代码和testing代码,都希望以不同的方式驱动API。
有时候,为了使testingAPI更容易,我添加了一些东西,但是我几乎总是发现,为了testing的目的,我认为我只是为了监控目的而使用了这些东西。 因此,例如, FooAllocator
最终可能会有一个可选的构造函数参数,它是一个监视接口( IMonitorFooAllocations
),这对于在testing过程中嘲笑非常有用,可以让我窥视内部,但是当您突然发现你在生产过程中必须向世界其他地方展示一些分配指标。 我现在倾向于想到可能要添加的额外位,以便于在可选生产监控的双重用途方面进行简单的testing。 我通常写服务器代码,并能够揭示事件的内部作为perfmon计数器是非常有用的…
同样,你也可以正确地说,组成一个API的对象经常会显式地使用其他的对象,而不是从一个已知的地方伸出来,但这是一件好事。 相信我,一旦你习惯了处理明确的依赖关系,你不会想要回头去深入了解课堂上的内容,找出当Widgets
没有提示他们想要的时候, Widgets
如何访问活动目录的原因和原因做这样的事情。 在devise和testing过程中,通常情况下你可以打开这些依赖关系,然后在你把所有的东西放在一起时再把它们隐藏起来。 你仍然是从上面参数化的,但是往往不是API的对象模型可能意味着你从来没有真正看到“上面”作为API的用户。 你最终得到一个地方去configuration它所需要的东西,而且如果你有大量的单例和全局variables以及隐藏的依赖关系,这看起来和它看上去没什么两样。
但请记住,TDD是一个工具,当它不适合不使用它。
不,我发现TDD通常会鼓励好的devise。 易于testing的东西在生产中也很容易使用…因为当提出一个testing时,你会想:“接下来我想做什么? 然后让它工作。
在练习TDD时,你不得不考虑一个API的“用例”,程序员将如何使用这些类/方法,最终你会得到一个非常有用的框架。 如果您必须创build一百个FooFactory
和BarAuthenticator
,或者您的API如同您所说的那样变得“超模块化”,那么在编写testing代码时可能会考虑到这一点,并考虑如何简化它。
至于参数和dependency injection – 我通常会发现使用TDD依赖关系变得更清晰。 它们通常是构造函数的参数,而不是方法参数,但是要明确的说API实现需要一个authentication器,一个随机性的来源等等,对于理解它在做什么是有用的。 你可以肯定的是,代码不会打到networking或远程数据库,因为它将这些东西隐藏在其实现中。 这些隐藏的细节是API在testing环境中难以使用的原因。
请注意,当依赖关系是构造函数调用的一部分时,它不是类可能实现的接口的一部分 – 接口内的依赖关系通常是隐藏的,但是TDD意味着实现以一种明显的方式公开它们。
有关dependency injection和TDD的很好的信息来源可以在Googletesting博客上find。 请特别注意MiškoHevery的post或者在Youtube上观看他的一些video: 不要看东西 等等 。
TDD导致了紧急devise ,最终产生了一个非常有用和可扩展的API。 TDD不是关于testing。 这是关于在您的代码中创build接缝,您可以用来添加行为,因为你发现更多关于你的项目。
TDD 是一种APIdevise技术。 每次编写unit testing时,您都要首次创build一个API,或者使用以前创build的API。 这些testing中的每一个都可以让您“感觉”API使用起来如此简单或困难。 每个新的testing都迫使您从新的angular度考虑API。 deviseAPI几乎没有比TDD强迫你更好的方式。
事实上,这就是TDD被认为是devise技术而不是testing技术的原因。 当你练习TDD时,你不会真空devise你的API。 你用他们来devise他们 !
大多数你被列为TDD缺点的东西被许多人认为是优秀devise的元素。
如果特别注意某种东西被传入的概念,而不是某种方法应该“只知道”的东西 – 后者往往导致单身或其他反粘性devise。
虽然最终可能会有一些方面只是为了支持可testing性而devise的,但这不一定是坏事。
但是,通常情况下,如果你不得不重新考虑你的devise,使其可testing,那么你一般会得到一个更具有凝聚力和适用性的devise – 虽然它仍然很灵活,可以很容易地改变不pipe你的未来需求是通过重构,因为你有testing,让你有信心快速做出改变。
我使用TDD并认为它在API构build方面运行良好,但是我认为API,尤其是那些向外部客户公开的API,是一个比使用TDD时需要更多前期devise的领域。 TDD严重依赖于重构,就像它在testing优先时那样。 使用API,您并不总是能够重构方法签名并保持简洁的devise。 如果你不小心devise你的界面,你可以find一个丑陋的界面,支持多种方法做非常相似的事情,只是为了保持向后兼容现有的API,同时移动你的devise。
如果我有一个我知道我会暴露给外部用户的API,我通常会花更多的时间考虑签名,并在开始开发过程之前尽可能地接近“正确”。 我也很早就经常向客户提供反馈信息,以便我们尽可能快地融合到稳定的接口中。 在不改变接口的情况下重构幕后改进实现并不是什么问题,但是我想快速获得一个稳定的接口,并愿意投入更多的时间来获得它。
使用传统的APIdevise,可以很容易地将自己build立在一个angular落:您可以最终得到一个具有大量隐藏的依赖关系的API(如类A需要B需要C需要D,如果您更改类初始化的顺序,事情开始打破)。
TDD确保你分开的部分保持分开。 它也允许你从一个非常不寻常的angular度看你的API:作为一个用户/消费者。 你的第一个问题是“我如何使用这个?” 而不是“我怎样才能让API看起来像? 后者可以引诱你进入一个隐藏的陷阱,而第一个导致的东西,我称之为“直观的API”:它的行为如预期。
一个忠告:不要让TDD成为你的信仰。 这是一个工具,有些工具无法解决一些问题。 所以如果TDD因为任何原因不适合你,那就没关系。 尽可能多地使用TDD。 不要狂热。 多年来,你会发现你的舒适区。
你可能想看一下Gilad Bracha的新闻语言工作,看看一些相关的API和语言devise。
我同意100%的答案。 但这真的取决于你的“良好的APIdevise”是什么意思。 TDD导致可testing的,模块化的工作代码 – 但最终,TDD代码最重要的方面是可testing性。
你会发现它导致不同的devise比其他的过程产量。 你会发现可testing的代码可能比纯API可能暴露更多的内部部分。
在很多情况下,使用TDD构build工作代码可能是有意义的,然后 – 作为一个单独的步骤 – 将API抽出。 否则,你有两个冲突工作的力量:一个简单的API和可testing的代码。
然而,这是一个微妙的点,整体TDD将提供比从象牙塔devise更好的API。
相反,由于您正在通过编写testing来将自己的方法用作用户,所以您会发现用户在编写testing时遇到的问题。 事实上,你将得到一个更清洁和用户友好的界面,你会写什么,否则。
如果你的API由于你的对象的内部需求而受到影响,那就是门面模式的用途。 并非所有对象都需要公开。
TDD的许多痛点实际上是devise中的痛点。 如果要创build一个类是很困难的,因为它需要传递18个依赖关系,最根本的问题是这个类具有太多的依赖关系,并且会变得非常脆弱。 它也可能做得太多了。 这种情况下的“TDD痛苦”是一件好事,因为它使其他问题更加明显。
TDD不妨碍良好的APIdevise。
嘲笑阻碍了良好的APIdevise。
这两者并不是同义词 – 我独立开始使用TDD之前,其他人写在书上,只是因为它清楚地表明要求是可testing和devise符合要求。
然而,我认为嘲笑是不好的,因为它影响了devise和实现,并引入了其他的人为要求。 它也暴露了不应该实现的内部实现,并使testing变得脆弱。 它testing如何做,而不是做了什么。
如何对付它:使用TDD,但不要使用模拟。
关于TDD的一点是我们编写testing,在devise时调用代码。 因此,我们最终应该有一个非常有用的界面。 当然,这并不是偶然的。 我们仍然必须做出正确的select。
我没有注意到,TDD根本就不能做出糟糕的APIselect。
相反,通过首先创build一个清晰的框架,API也更加清晰,对用户更加明显。