93

I've been reading a bit about Literate Programming recently, and it got me thinking... Well-written tests, especially BDD-style specs can do a better job at explaining what code does than prose does, and have the big advantage of verifying their own accuracy.

I've never seen tests written inline with the code that they test. Is this just because languages don't tend to make it simple to separate application and test code when written in the same source file (and nobody's made it easy), or is there a more principled reason that people separate test code from application code?

Chris Devereux
  • 931
  • 2
  • 7
  • 9
  • 34
    Some programming languages like python with doctest allows you to do that. – Simon Bergot Feb 25 '13 at 13:40
  • 2
    You may feel the BDD-style specs are better than prose at explaining the code, but that doesn't mean the combination of the two isn't better. – JeffO Feb 25 '13 at 13:49
  • 5
    Half of the arguments here apply to inline documentation as well. – CodesInChaos Feb 25 '13 at 14:23
  • 3
    @Simon doctests are too simplistic for serious testing, mostly because they aren't designed for it. They were intended for, and excel at, having code examples in the documentation that can be verified automatically. Now, some people use them for unit testing as well, but lately (as in, for the past years) this took a lot of flak, because it tends to end in fragile messes, overly verbose "documentation", and other messes. –  Feb 25 '13 at 14:34
  • Take into consideration that most IDEs/editors have shortcuts to quickly jump to the test file, if you follow a naming convention. – Hakan Deryal Feb 25 '13 at 16:51
  • 7
    Design by Contract allows for inline specifications that make testing straightforward. – Fuhrmanator Feb 25 '13 at 17:20
  • Determining code coverage of your tests would be at least a little harder, I would think, if the test code was in the same place as the code it was testing. – Mark Allen Feb 25 '13 at 21:16
  • 1
    I think your premise is somewhat flawed. Other than doctest which Simon already mentioned, most of C++ code has tons of asserts scattered in it. These are, essentially, tests. Unit testing etc. is used for separation of concerns and code reuse (the setup / teardown parts). – TC1 Feb 26 '13 at 07:31
  • On the earlier days of junit testing the most common pattern was to have a static inner class Test<ClassUnderTest> extends junit.framework.TestCase for tests. The only advantage was to have access to private fields and methods. – duros Feb 26 '13 at 07:35
  • 1
    Here is another language with an idea similar to doctest: D. It has unit tests as a built-in feature. – Elias Zamaria Feb 26 '13 at 17:38
  • 1
    The language being used to test may be different from the language used in the application. e.g. It is natural to test Java with Scala or Groovy. – Michael Easter May 03 '13 at 18:59
  • 2
    Most of the answers are pretty negative, but this is still a neat idea. I'm thinking about what it would take to do something like this in Ruby. – Chris Pitman May 03 '13 at 21:13
  • 1
    @ChrisPitman Agreed. I suspect the negative responses are partially caused by resistance to change :) The only way to effectively test this idea would be to try it and see its actual pros and cons, instead of arguing from a purely theoretical (and dogmatic) point of view. – Andres F. May 05 '13 at 15:38
  • I put my tests inline. The main advantage is that unit tests are less likely to go stale. – Pete Alvin May 28 '18 at 10:45

12 Answers12

87

The only advantage I can think of for inline tests would be reducing the number of files to be written. With modern IDEs this really isn't that big a deal.

There are, however, a number of obvious drawbacks to inline testing:

  • It violates separation of concerns. This may be debatable, but to me testing functionality is a different responsibility than implementing it.
  • You'd either have to introduce new language features to distinguish between tests/implementation, or you'd risk blurring the line between the two.
  • Larger source files are harder to work with: harder to read, harder to understand, you're more likely to have to deal with source control conflicts.
  • I think it would make it harder to put your "tester" hat on, so to speak. If you're looking at the implementation details, you'll be more tempted to skip implementing certain tests.
samthebrand
  • 368
  • 2
  • 12
  • 27
vaughandroid
  • 7,599
  • 9
    That's interesting. I guess the advantage I can see is that when you've got your "coder" hat on, you want to be thinking about tests, but it's a good point that the reverse is not true. – Chris Devereux Feb 25 '13 at 14:24
  • 2
    Along these lines, it's possible (and perhaps desirable) to have one person creating the tests and a second actually implementing the code. Putting the tests inline makes this more difficult. – Jim Nutt Feb 25 '13 at 16:07
  • Agree with the "hats" point especially... TDD using red/green (stop and go) approach, usually means toggling between units on purpose, so that you can only see the interface (not the implementation) that you are testing or implementing, not both. Putting both in one file would be a Mess. – Warren P Feb 25 '13 at 18:13
  • 7
    would downvote if i could. How is this an answer in any way at all? Implementers not writing tests? People skipping tests if they look at implementation details? "Just too hard" Conflicts on big files?? ANd how in any way could a test be confused with an implementation detail??? – bharal Feb 26 '13 at 11:15
  • @bharal erm... because if you're using something like the gtest system, your test is some well-written c++ code, and your implementation is some well-written c++ code. You have to drop down a level when scanning to tell which is which. – deworde Feb 26 '13 at 11:32
  • 7
    @bharal Also, wrt to "Just too hard", masochism is a fool's virtue. I want everything to be easy except for the problem I'm actually trying to solve. – deworde Feb 26 '13 at 11:33
  • 1
    @bharal I've given a list of reasons why I think it's a bad idea; I don't think it's impossible but deworde has hit the nail on the head when he says "masochism is a fool's virtue". If you disagree, I encourage you to post your own answer! – vaughandroid Feb 26 '13 at 14:49
  • 1
    The answer i'd probably give would be code clutter, which isn't much of an answer. I'm not sure about the machismo thing - i'm not arguing for build conflicts. On the other hand, i honestly don't think that keeping files small because of a fear of code conflicts is reason enough. – bharal Feb 26 '13 at 15:36
  • 1
    On the other hand, while i agree with your "separation of concerns" point, i agree from the point of view of minimising code clutter, not wrt having a developer write code but another test it. I still don't think that point 4. is valid - people look at implementation details all the time to write tests. That is, in fact, the heart of TDD. – bharal Feb 26 '13 at 15:39
  • @bharal Smaller files is something you're always looking for, Baqueta mentioned only one benefit of smaller files, there are many others. You'd be better off pointing to why tests should be on the same file instead of complaining about how big files are no big deal... – Ruan Mendes May 03 '13 at 21:31
  • 1
    The problem with "separation of concerns" is that it has blurry limits. What about annotations in Java? Modern Java development has moved away from xml config files and tends to put config as annotations. Annotations are in practice "a separate language". Isn't that breaking this principle? And if you accept it, why not inline tests? – Andres F. May 04 '13 at 22:44
  • 1
    Very strong disagree. Testing code is the only way you know if what you've written actually works -- especially when you're modifying existing code. If you don't have an existing base of tests, it's virtually impossible in a non-trivial code base to know if what you just did broke anything. – xaxxon May 04 '13 at 22:46
  • 3
    Unit test can be considered documentation. That suggests unit tests should be included in the code for the same reason as comments - to improve readability. However, the problem with that is there tend to be a lot of unit tests, and a lot of test-implementation overhead that doesn't specify expected results. Even comments within code should be kept succinct, with larger explanations moved out of the way - to a comment block outside the function, to a separate file, or maybe to a design document. Unit tests are IMO rarely if ever short enough to keep in the tested code like comments. –  May 05 '13 at 01:11
  • 1
    @ChrisDevereux the opposiite might be true, too. While you code, tests flow out naturally out of your mind, like: "oh, this one here could turn out wrong this way". Getting the "tester" cap on, instead means doing a lot of backtracking. – ZJR May 05 '13 at 14:21
  • 2
    @JuanMendes i disagree that "smaller files are something you're always looking for". What you're really looking for is "clear, easily maintainable code". The size of the file, then, is traditionally linked to this goal. BUT note that whether in one file or in 5000, you have the same amount of lines of code. There are times when i'd rather have one large file (spring config files, anybody?) and in the case of embedding test with prod, it might even make sense. But as others have noted, my main gripe is divvying coding from testing. – bharal May 08 '13 at 14:07
  • Another reason for separating tests is that you often use additional or even different libraries for testing than for the actual implementation. If you mix tests and implementation, accidential usage of test libraries in the implementation cannot be caught by the compiler. Also, tests tend to have way more code lines than the implementation parts they test, so you'll have trouble finding the implementation between the tests. :-) – Dr. Hans-Peter Störr Jun 04 '13 at 08:50
  • +1 for just mentioning separation of concerns, enough said. – Songo Jun 04 '13 at 09:32
  • I happen to use this answer as an example of 1) not thinking outside the box, 2) confirmation bias, 3) breaking the rule of "5 whys" and not least of all 4) doesn't pass the negation test. – zumalifeguard Dec 11 '15 at 03:43
37

I can think of some:

  • Readability. Interspersing "real" code and tests will make it harder to read the real code.

  • Code bloat. Mixing "real" code and test code into the same files / classes / whatever is likely to result in larger compiled files, etc. This is particularly important for languages with late binding.

  • You may not want your customers / clients to see your test code. (I don't like this reason ... but if you are working on a closed source project, the test code is unlikely to help the customer anyway.)

Now there are possible workarounds for each of these issue. But IMO, it is simpler not to go there in the first place.


It is worth observing that in the early days, Java programmers used to do this kind of thing; e.g. including a main(...) method in a class to facilitate testing. This idea has almost completely disappeared. It is industry practice to implement tests separately using a test framework of some kind.

It is also worth observing that Literate Programming (as conceived by Knuth) has never caught on in the software engineering industry.

Stephen C
  • 25,178
  • 4
    +1 Readability issues - test code might be proportionally greater to the implementation code, especially in OO designs. – Fuhrmanator Feb 25 '13 at 17:22
  • 2
    +1 for pointing out using test frameworks. I can't imagine using a good test framework concurrently with production code. – joshin4colours Feb 25 '13 at 17:23
  • 1
    RE: You may not want your customers / clients to see your test code. (I don't like this reason ... but if you are working on a closed source project, the test code is unlikely to help the customer anyway.) -- It may be desirable to run the tests on the clients machine. Running the tests may help to quickly identify what the issue is and to id differences in the clients env.. – sixtyfootersdude Feb 25 '13 at 18:24
  • 1
    @sixtyfootersdude - that is a pretty unusual situation. And assuming that you were developing closed source, you wouldn't want to include your tests in your standard binary distro just in case. (You would create a separate bundle containing the tests that you want the customer to run.) – Stephen C Feb 26 '13 at 00:02
  • I really don't like these types of answers because it's basically saying, "don't do it because it's not done". What everyone happens to be wrong and this is the right way? Presume for a second that that is the case and now apply critical thought to justify what that cannot be the case. – zumalifeguard Dec 11 '15 at 03:47
  • 1
  • Did you miss the first part of my answer where I gave three actual reasons? There was some "critical thought" involved there .... 2) Did you miss the second part where I said that Java programmers used to do this, but they don't now? And the obvious implication that programmers stopped doing this ... for good reason?
  • – Stephen C Dec 11 '15 at 12:39