TDD:“仅测试”方法
在这里寻找一些实用的建议以及人们在类似情况下的任何经历。
我们使用 BDD/TDD 系统方法来构建我们的软件(相当大/复杂的应用程序),最终结果是……源自业务需求的行为规范(Given/When/Then 风格)、反映这些需求的单元测试以及反映这些需求的代码测试的要求。
然而,最近我们的测试部门已经开始运行集成测试,可以理解的是,他们希望使用我们(已经通过的)业务逻辑代码来设置和拆除测试状态(而不是直接处理数据库),因为他们主要关心的是通过应用程序的 UI 进行测试,并且不想花一整天的时间来处理数据库。
问题是,一些实体存储库没有删除方法,因为尚未表达这些方法的业务需求。许多都有存档/恢复/备份等(并且可能有积压的删除待处理)。
所以现在我们有一个测试部门。删除的要求(但与业务用户故事相冲突)
所以......我的问题是......如果我要专门为测试部门添加方法......处理这些的最佳方法是什么。我知道这在“TDD Utopia”中通常被认为是不好的做法,但实际上,您是如何处理这种冲突的?
我的第一个想法是要么使用命名...
void TestOnly_Delete(Guid id){}
...属性...
[TestOnly]
void Delete(Guid id){}
...或编译器指令...
#if TESTBUILD
void Delete(Guid id){}
#endif
所以至少,开发人员可以知道不要调用 TestOnly 方法,并且最多,测试方法是未部署在生产版本中。
...或者只是作弊并添加一个用户故事来以这种方式管理它;-)
有任何经验或建议吗?
提前致谢。
Looking for some practical advice here and any experiences people have had in a similar situation.
We use a BDD/TDD sytle methodology for building our software (quite a large/complex application) The end result is.. behavioral specifications (Given/When/Then style) derived from business requirements, unit tests that reflect these and code that reflects the requirements of tests.
However, recently our test department has started running integration tests, and understandably they want to use our (already passing) business logic code to set up and tear down test state (rather than having to deal directly with a database) as they are mainly concerned with testing via the UI of the application and do not want to spend all day wrangling databases.
The problem is, some of the Entity Repositories do not have delete methods as there has been no business requirement expressed for these yet. Many have Archive/Reinstate/backup etc. (and may have delete pending on the backlog).
So now we have a test dept. requirement for deletion (but one which conflicts with business user stories)
So.... My question is... if I were to add methods specifically for the test department... what't the best way of handling these. I understand that this is generally considered bad practice in "TDD Utopia" but realistically, how have you dealt with this kind of conflict?
The first thoughts I have had are either use naming...
void TestOnly_Delete(Guid id){}
...attributes...
[TestOnly]
void Delete(Guid id){}
... or compiler directives...
#if TESTBUILD
void Delete(Guid id){}
#endif
So at a minimum, developers can know not to call TestOnly methods and at a maximum, test methods are not deployed in production builds.
... or just cheat and add a user story to manage it that way ;-)
Any experience or advice gratefully appreciated?
Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您首先关心的问题应该是添加此功能会增强还是恶化当前的 API?TDD 中的一个典型场景是(最初)仅出于可测试性的原因需要类成员,但它符合所有正常的 API设计指南,因此它不会造成任何损害(并且通常随后也会成为生产代码中有价值的补充)。
如果这是真的,那么无论如何都可以添加它,如果你可以轻松做到这一点。
然而,有时情况恰恰相反。在这种情况下,您必须克制住添加会员的冲动。但是,通常您可以遵循开放/封闭原则并开放您的API,以便其他人将能够添加所需的功能。 可测试性实际上只是开放/封闭原则的另一个词。
对于您的情况,最简单的解决方案可能只是确保相关类未密封,然后要求测试部门从这些类派生并自行实现功能。如果他们没有这种能力,那么您可以为他们做这件事,同时仍然保持子类仅用于测试。
Your first concern should be does adding this functionality enhance or deteriorate the current API? A typical scenario in TDD is that a class member is (initially) only required for testability reason, yet it conforms to all normal API design guidelines, so it does no harm (and often subsequently turn out to be a valuable addition in production code as well).
When this is true, then by all means just add it if you can do so easily.
Sometimes, however, the reverse is the case. In this case, you must resist the urge to add the member. However, often you can follow the Open/Closed Principle and open up your API so that others would be able to add the desired functionality. Testability is really just another word for the Open/Closed Principle.
In your case, the simplest solution might simply be to ensure that the classes in question are unsealed and then ask the Test Department to derive from those classes and implement the functionality themselves. If they don't have this capability, then you can do it for them, while still keeping the sub-classes test-only.
在我的代码中,我创建一个子类:例如,如果我有一个
DataLayer
类,它具有访问数据层的所有公共方法,那么我可以使用Test_DataLayer
对其进行子类化code> 子类,它从DataLayer
继承方法,并进一步实现您可能需要的任何其他方法,例如Delete
,并且其实现可以访问基类的内部或受保护的方法。In my code, I create a subclass: for example, if I have a
DataLayer
class which has all the public methods to access the data layer, then I might subclass it with aTest_DataLayer
subclass, which inherits methods fromDataLayer
and which furthermore implements any additional methods that you might want likeDelete
, and whose implementation can access internal or protected methods of the base class.我通常最终会得到一个“仅测试”项目/库,其中包含我的测试所需的任何模拟/帮助程序类。在此模拟库中添加必要的类/方法(生产代码中未包含这些类/方法)似乎很自然地适合此要求。如果您还没有这样的库,并且不想创建一个库,那么我会在可能的情况下将这些方法标记为内部方法,并且仅将它们公开给测试框架。您没有指定平台,但我知道这对于 C#/.NET 是可能的,并且我经常使用此功能让我的测试访问在库外部不可用的方法。
另一方面,我想说测试人员与实际客户一样都是需求分析的一部分。就像我们有一些纯粹出于开发需要的要求一样,我们也有一些测试的要求。正如您所发现的,诀窍在于以尽可能满足所有利益相关者需求的方式开发代码。当需求发生冲突时,我们尝试使用尽可能最佳的折衷方案。 IMO,允许从仅测试库中删除是对客户数据安全需求(不删除)的合理妥协。
I usually end up with a "test-only" project/library holding whatever mock/helper classes that I need for my tests. Adding the necessary classes/methods in this mock library -- which isn't included by production code -- seems like a natural fit for this requirement. If you don't already have such a library, and don't want to create one, then I would mark -- if possible -- the methods internal and only expose them to the testing framework. You don't specify platform, but I know that this is possible with C#/.NET and I often use this ability to give my tests access to methods that would otherwise be unavailable outside the library.
On another note, I would say that the testers are as much a part of your requirements analysis as the actual customers. Just like we have some requirements that are purely for our development needs, we have some requirements for testing as well. The trick, as you're discovering, is to develop the code in a way that all of the stakeholders needs are satisfied as much as possible. When the needs conflict, we try to use the best compromise possible. IMO, making it possible to delete from test-only libraries is a reasonable compromise with the customer's need for data security (no delete).
这是一个有趣的问题。我有一些想法,但不确定这是否能解决您的问题。
我将亲自创建一组显式接口,继承自实体已有的接口(例如 IIntTest_Customer: ICustomer),并在那里定义删除方法。如果可能的话,尝试将所有这些接口放在单独的项目中,这样开发人员甚至不会从通常的项目中引用它们,并避免意外使用它们。然后,测试部门将使用这个特定的内部测试接口来实现其测试目的。
为了实现这一点,您可能必须重构当前的代码并更改实体以实现这些内部测试接口,并且使用继承层次结构,您的所有现有代码应该仍然可以引用基本接口。
This is an interesting question. I have some thought but not sure if it will be an answer to your problem.
I will personally create an explict set of interfaces inheriting from what you already have for the entity such as IIntTest_Customer: ICustomer and define the delete method there. If possible, trying to put all these interface in separate project so that way the developer dont even have reference to it from the usual project and avoid the accidental use of them. The test department will then use this specific internal test interfaces for their testing purpose.
To achieve this you perhaps have to refactor your current code and change the entity to implement these internal test interfaces instead and with the inheritance hierarchy all your existing code should still work that refer to the base interface.
我绝不会仅仅为了支持自动化测试而在代码中添加删除或其他清理方法。
有很多选择,从智能事务管理到数据库自动恢复。
I will never add delete or other cleanup methods to my code only to support automated testing.
There are so many alternatives, ranging from intelligent transaction management to automatic restore of databases.