C# 将现有代码迁移到测试驱动开发
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/167079/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Moving existing code to Test Driven Development
提问by Matthew Scharley
Having recently discovered this method of development, I'm finding it a rather nice methodology. So, for my first project, I have a small DLL's worth of code (in C#.NET, for what it's worth), and I want to make a set of tests for this code, but I am a bit lost as to how and where to start.
最近发现了这种开发方法,我发现它是一种相当不错的方法。所以,对于我的第一个项目,我有一个小的 DLL 代码(在 C#.NET 中,它的价值),我想对这段代码进行一组测试,但我对如何和从哪儿开始。
I'm using NUnit, and VS 2008, any tips on what sort of classes to start with, what to write tests for, and any tips on generally how to go about moving code across to test based development would be greatly appreciated.
我正在使用 NUnit 和 VS 2008,任何关于从什么类型的类开始、为什么编写测试的提示,以及关于如何将代码转移到基于测试的开发的任何提示,将不胜感激。
采纳答案by Anthony
See the book Working Effectively with Legacy Codeby Michael Feathers.
请参阅Michael Feathers所著的《有效处理遗留代码》一书。
In summary, it's a lot of work to refactor existing code into testable and tested code; Sometimes it's too much work to be practical. It depends on how large the codebase is, and how much the various classes and functions depend upon each other.
综上所述,将现有代码重构为可测试和已测试代码需要大量工作;有时工作量太大,不切实际。这取决于代码库有多大,以及各种类和函数相互依赖的程度。
Refactoring without tests will introduce changes in behaviour (i.e. bugs). And purists will say it's not really refactoring because of the lack of tests to check that the behaviour doesn't change.
没有测试的重构将引入行为的变化(即错误)。纯粹主义者会说这并不是真正的重构,因为缺乏测试来检查行为是否不会改变。
Rather than adding test across the board to your whole application at once, add tests when you work in an area of code. Most likely you'll have to return to these "hotspots" again.
与其一次在整个应用程序中添加测试,不如在您处理某个代码区域时添加测试。很可能您将不得不再次返回这些“热点”。
Add tests from the bottom up: test little, independent classes and functions for correctness.
自下而上添加测试:测试小而独立的类和函数的正确性。
Add tests from the top down: Test whole subsystems as black boxes to see if their behaviour changes with changes in code. And so you can step through them to find out what's going on. This approach will probably get you the most benefit.
自上而下添加测试:将整个子系统作为黑盒进行测试,看看它们的行为是否随着代码的变化而变化。因此,您可以逐步查看它们以了解发生了什么。这种方法可能会给您带来最大的好处。
Don't be too concerned at first with what the "correct" behaviour is while you are adding tests, look to detect and avoid changes in behaviour. Large, untested systems often have internal behaviours that may seem incorrect, but that other parts of the system depend on.
在添加测试时,一开始不要太在意“正确”行为是什么,寻找并避免行为变化。未经测试的大型系统通常具有看似不正确的内部行为,但系统的其他部分却依赖于此。
Think about isolating dependencies such as database, filesystem, network, so that they can be swapped out for mock data providers during testing.
考虑隔离数据库、文件系统、网络等依赖项,以便在测试期间将它们替换为模拟数据提供程序。
If the program doesn't have internal interfaces, lines which define the boundary between one subsystem/layer and another, then you may have to try to introduce these, and test at them.
如果程序没有内部接口,即定义一个子系统/层与另一个子系统/层之间边界的线,那么您可能必须尝试引入这些并对其进行测试。
Also, automatic mocking frameworks like Rhinomocksor Moqmight help mock existing classes here. I haven't really found the need for them in code designed for testability.
此外,Rhinomocks或Moq等自动模拟框架可能有助于模拟此处的现有类。我还没有真正发现在为可测试性而设计的代码中需要它们。
回答by Olaf Kock
Testable code is easy to spot - by the accompanying tests. If there are some, it must be testable. If there are none - assume the opposite. ;)
可测试的代码很容易被发现——通过随附的测试。如果有一些,它必须是可测试的。如果没有 - 假设相反。;)
That said: Test Driven Development (TDD) is not so much a testing strategy as it is a design strategy. The Tests that you write first help in designing the interface of your classes, as well as in getting the scope of your classes (or subsystems for that matter) right.
也就是说:测试驱动开发 (TDD) 与其说是一种测试策略,不如说是一种设计策略。您编写的测试首先有助于设计类的接口,以及正确确定类(或与此相关的子系统)的范围。
Having the tests that you created during TDD and executing them later makes good tests, but is merely a (very welcome) side effect of that design philosophy.
拥有您在 TDD 期间创建的测试并在以后执行它们是很好的测试,但这只是该设计理念的(非常受欢迎的)副作用。
This said, expect some resistance from your code against being tested. Listen to your code and change the interface in order to be easily testable. You'll most likely redesign it when you start writing tests.
这就是说,预计您的代码会抵制被测试。聆听您的代码并更改界面以易于测试。当您开始编写测试时,您很可能会重新设计它。
回答by David Alpert
Working Effectively with Legacy Codeis my bible when it comes to migrating code without tests into a unit-tested environment, and it also provides a lot of insight into what makes code easy to test and how to test it.
在将没有测试的代码迁移到经过单元测试的环境中时,有效地使用遗留代码是我的圣经,它还提供了很多关于什么使代码易于测试以及如何测试的见解。
I also found Test Driven Development by Exampleand Pragmatic Unit Testing: in C# with NUnitto be a decent introduction to unit testing in that environment.
我还发现Test Driven Development by Example和Pragmatic Unit Testing: in C# with NUnit是该环境中单元测试的一个不错的介绍。
One simple approach to starting TDD is to start writing tests first from this day forward and make sure that whenever you need to touch your existing (un-unit-tested) code, you write passing tests that verify existing behavior of the system before you change it so that you can re-run those tests after to increase your confidence that you haven't broken anything.
启动 TDD 的一种简单方法是从今天起首先开始编写测试,并确保每当您需要接触现有(未经过单元测试的)代码时,编写通过的测试以在更改之前验证系统的现有行为这样您就可以在之后重新运行这些测试,以增加您对没有破坏任何东西的信心。
回答by philant
Your DLL provides some sort of service. For every service, what do you have to do before getting this service, what parameters should you pass to get this service, how would you know that the requested service has correctly be executed ?
您的 DLL 提供了某种服务。对于每一个服务,在获取这个服务之前你需要做什么,你应该传递什么参数来获取这个服务,你怎么知道请求的服务已经正确执行了?
Once you have the answers to those questions, you can write a first test. Such tests would rather be called Characterization tests than unit tests, but would probably be easier to write than unit-tests if the DLL was not developped using TDD.
一旦你有了这些问题的答案,你就可以编写第一个测试。这样的测试宁愿被称为特性测试而不是单元测试,但如果 DLL 不是使用 TDD 开发的,则可能比单元测试更容易编写。
Characterization tests are also discussed in M. Feathers' "Working Effectively with Legacy Code", which is recommended in other responses.
M. Feathers 的“Working Effectively with Legacy Code”中也讨论了特性测试,这在其他回复中被推荐。
Also, be sure to write a failing test before to add any new line of code.
此外,请务必在添加任何新代码行之前编写失败的测试。
回答by S.Lott
I call it "Test Driven Reverse Engineering".
我称之为“测试驱动逆向工程”。
Start "at the bottom" -- each class can be separately examined and a test written for it. When in doubt, guess.
从“底部”开始——可以单独检查每个类并为其编写测试。如有疑问,请猜测。
When you're doing ordinary TDD in the forward direction, you treat the test as sacred and assume that the code is probably broken. Sometimes the test is wrong, but your starting-off position is that it's the code.
当您在正向进行普通 TDD 时,您将测试视为神圣的,并假设代码可能已损坏。有时测试是错误的,但你的出发点是它是代码。
When you're doing TDRE, the code is sacred -- until you can provethat the code has a long-standing bug. In the reverse case, you write tests around the code, tweaking the tests until they work and claim the code works.
当你在做 TDRE 时,代码是神圣的——直到你能证明代码有一个长期存在的错误。在相反的情况下,您围绕代码编写测试,调整测试直到它们工作并声称代码工作。
Then, you can dig into the bad code. Some bad cade will have sensible test cases -- this just needs to be cleaned up. Some bad code, however, will also have a test case that's senseless. This may be a bug, or clumsy design that you may be able to rectify.
然后,您可以深入研究错误代码。一些糟糕的 cade 会有合理的测试用例——这只是需要清理。然而,一些糟糕的代码也会有一个毫无意义的测试用例。这可能是您可以纠正的错误或笨拙的设计。
To judge if the code's actually wrong, you also need to start at the top with overall test cases. Live data that actually works is a start. Also, live data that produces any of the known bugs, also a good place to start.
要判断代码是否真的有错,您还需要从总体测试用例的顶部开始。真正有效的实时数据只是一个开始。此外,产生任何已知错误的实时数据也是一个很好的起点。
I've written little code generators to turn live data into unittest cases. That way, I have a consistent basis for testing and refactoring.
我编写了一些代码生成器来将实时数据转换为单元测试用例。这样,我就有了一致的测试和重构基础。