5 ms·
Copy-paste-change tests may be interesting, though. Too much fancyness makes tests difficult to follow in my experience. Ideally, tests should take three steps
by jaimebuelta 10y ago
Copy-paste-change tests may be interesting, though. Too much fancyness makes tests difficult to follow in my experience.
Ideally, tests should take three steps:
- setup
- perform a single operation (the test)
- check operation result is the expected one
If each step is simple and readable, is actually easy to follow, even if a lot of cases uses almost the same initial setup and expected result, making copy-paste-change a good way of adding tests.
If a change in functionality if big enough to require change a lot of tests, this way makes obvious is changing the behaviour in lots of cases, something good to keep in mind. Most times a simple editor replace suffices, but shows up how big the change is.
Refactoring should be focused in making those steps easy to write and read...
- vinceguidry 10y agoI'd argue that having setup in your tests is an anti-pattern. The setup should be, at most, a single message send to the object under test. Simply call it in your expectation. If you're putting significant code in your test, consider moving that logic to the app.
- scaryclam 10y agoQuite a bit of the time, you don't want to rely on application logic to do your setup for you. You end up needing the application logic to put things into the DB, without any bugs, in order to be sure your test is actually testing the part of the code it's meant to be testing. Allowing your application logic to do this for you creates all sorts of opportunity for hard to debug issues that have nothing to do with the thing you were actually testing. Putting data into the test DB directly makes for more setup, but makes for much more isolated test cases and much more certainty that your tests are doing what you think they're doing. It also means you can write tests as you go without having to build a whole level of application logic to support your single test.
- 88e282102ae2e5b 10y agoI think a problem with this is that when you bring an actual database into the test, it ceases to be a unit test and becomes an integration test (not that integration tests aren't useful). I would prefer for the application to be separated enough from the database that I can test the business logic entirely on its own.
- aisofteng 10y agoI would suggest that, aside from at the boundaries of a system, if you find yourself requiring complex setup code then you should evaluate whether your abstractions are neatly decoupled and reduced to minimal necessary complexity.
- sbov 10y agoI disagree. IMHO, modifying your app code for the sake of test code is an anti pattern. It should be viewed as an unfortunate yet necessary, rather than celebrated, action. It taints your design and makes your application harder to understand.
- lisivka 10y agoNobody even declared code, which is easy to test (verify), as an anti-pattern. Quote, please.
- aisofteng 10y agoI disagree. If you write tests only after you implement your application and find that they are difficult to test, it is likely not that you are using an anti-pattern in the "unfortunate yet necessary" action of making your code more testable - it is, instead, far more likely, that by failing to write tests as you wrote your implementation, you implemented a poor design that does not decouple abstractions as well as they could be. It is not a chore to unit test code, it is an extremely useful development tool that forces you to check your assumptions and verify your design as you go to give you a better product. The attitude of this comment is along the lines of the sorts of attitudes towards testing it takes a lot of effort to un-learn junior engineers of.
- deleted 10y ago[deleted]
- sbov 10y agoJuniors make plenty of mistakes, I would rather they make them then explain why they should change their code rather than them relying on "testable code = better code". I would admit that a junior working alone, or without code review/a mentor, is probably better off relying on that mantra though. Or, perhaps even anyone designing an application significantly larger than they have in the past (without any outside input). However, everything has a cost, even abstractions. The more powerful the abstraction, the higher the cost. I find that for most of our projects, the sorts of abstractions unit testing forces on us is much more powerful - and thus higher cost - than necessary for the task at hand. If you are not taking advantage of that power, then all you really have left is the high cost.
- 88e282102ae2e5b 10y agoIf I have some object I want to test in multiple ways, I find it more convenient to create a single instance during setup and then testing various aspects of just that single instance. Since the constructor of the object in question needs a bunch of values passed in, and each unit test only checks the effects of one at a time, I'm just going to copy and paste a bunch of object initialization code if I don't have a setup step first.
- aisofteng 10y agoThis is not a good practice because a unit test should be able to assume that no other code can possibly modify the data it is using. Later, another engineer could possibly come in with the intent of modifying a single unit test in accordance with an isolated functional change in such a way that the data being used by other unit tests gets modified. Then some poor fellow ends up redesigning the test suite to actually follow the assumptions of a unit test's test data being only ever accessed by that unit test. I have been that fellow, and it can be, bluntly, quite a pain in the ass; as you're doing it, you can't help but think, "whoever wrote this was too lazy to set up the test data properly, I shouldn't have to spend the time to fix this now."
- 88e282102ae2e5b 10y agoAh yes, I should have stated that the object is immutable.
- rhizome 10y agoWhere are you getting the object to which you're going to send messages?
- vinceguidry 10y agoFrom the class. expect(Klass.new.special_case).to have_this_behavior Anything more complicated in test code should be considered an anti-pattern.
- diggan 10y agoYeah, I've done the same under something I've learned as AAA; Arrange, Act and Assert, which I think is the more familiar naming of a system like this. C2 has some more information about it over here: http://wiki.c2.com/?ArrangeActAssert http://wiki.c2.com/?ArrangeActAssert
- aisofteng 10y agoI often find it interesting the propensity to name things. In my opinion, this approach should be self-evident once the concept of unit tests is correctly understood - prepare the data, run the code, check the output. I suppose this is another case where a common approach is given a name so that people can refer to it to each other and pass it onto others. I wouldn't have thought to name this one, though - I would rather teach a junior engineer how to think about what a test should do, instead of telling him to do "AAA". With experience, I have seen the effect of something being given a name eventually leading to less experienced people treating it as a universal principle and wielding it as if all they have is a hammer, perhaps before having formed the habits of checking assumptions before making decisions, etc.