Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

You can find an endless list of blogs, articles and websites promoting the benefits of unit testing your source code. It's almost guaranteed that the developers who programmed the compilers for Java, C++, C# and other typed languages used unit testing to verify their work.

So why then, despite its popularity, is testing absent from the syntax of these languages?

Microsoft introduced LINQ to C#, so why couldn't they also add testing?

I'm not looking to predict what those language changes would be, but to clarify why they are absent to begin with.

As an example: We know that you can write a for loop without the syntax of the for statement. You could use while or if/goto statements. Someone decided a for statement was more efficient and introduced it into a language.

Why hasn't testing followed the same evolution of programming languages?

share|improve this question
24  
Is it really better to make a language specification more complex by adding syntax, as opposed to designing a language with simple rules that can easily be extended in a generic way? –  KChaloux Jul 3 at 16:58
6  
What do you mean by "at the syntax level"? Do you have any detail/idea about how it would be implemented? –  SJuan76 Jul 3 at 16:58
19  
Could you give a pseudo-code example of testing as a syntax-supported feature? I'm curious to see what you think it would look like. –  FrustratedWithFormsDesigner Jul 3 at 17:11
11  
@FrustratedWithFormsDesigner See the D language for an example. –  Doval Jul 3 at 17:44
4  
Another language with built-in unit testing features is Rust. –  Sebastian Redl Jul 3 at 18:32

6 Answers 6

up vote 34 down vote accepted

As with many things, unit testing is best supported at the library level, not the language level. In particular, C# has numerous Unit Testing libraries available, as well as things that are native to the .NET Framework like Microsoft.VisualStudio.TestTools.UnitTesting.

Each Unit Testing library has a somewhat different testing philosophy and syntax. All things being equal, more choices are better than less. If unit testing were baked into the language, you'd either be locked into the language designer's choices, or you'd be using... a library, and avoiding the language testing features altogether.

Examples

  • Nunit - General purpose, idiomatically-designed unit testing framework that takes full advantage of C#'s language features.

  • Moq - Mocking framework that takes full advantage of lambda expressions and expression trees, without a record/playback metaphor.

There are many other choices. Libraries like Microsoft Fakes can create "shims..." mocks that don't require you to write your classes using interfaces or virtual methods.

Linq isn't a language feature (despite it's name)

Linq is a library feature. We got a lot of new features in the C# language itself for free, like lambda expressions and extension methods, but the actual implementation of Linq is in the .NET Framework.

There's some syntactic sugar that was added to C# to make linq statements cleaner, but that sugar is not required to use linq.

share|improve this answer
1  
I do agree with the point that LINQ isn't a language feature but to make LINQ happen there needed to be some underlying language features to happen -- particularly generics and dynamic types. –  Wyatt Barnett Jul 3 at 19:36
    
@WyattBarnett: Yes, and the same is true for easy unit testing - like reflection and attributes. –  Doc Brown Jul 3 at 19:41
7  
LINQ needed lambdas and expression trees. Generics already were in C# (and .Net in general, they're present in the CLR), and dynamics have nothing to do with LINQ; you can do LINQ without dynamic. –  Arthur van Leeuwen Jul 3 at 19:57
    
Perl's DBIx::Class is an example of LINQ-like functionality implemented more than 10 years after all the features needed to implement it has been in the language. –  slebetman Jul 4 at 1:14
2  
@Carson63000 I think you mean anonymous types in combination with the var keyword :) –  M.Mimpen Jul 4 at 8:12

There's lots of reasons. Eric Lippert has stated many times that the reason feature X isn't in C# is because it's just not in their budget. Language designers don't have an infinite amount of time nor money to implement things, and each new feature has maintenance costs associated with it. Keeping the language as small as possible isn't just easier for the language designers - it's also easier for anyone writing alternative implementations and tools (e.g. IDEs) Additionally, when something is implemented in terms of the language rather than part of it, you get portability for free. If unit testing is implementing as a library, you only need to write it once and it'll work in any conforming implementation of the language.

It's worth noting that D does have syntax-level support for unit testing. I don't know why they decided to throw that in, but it's worth noting that D is meant to be a "high level systems-programming language". The designers wanted it to be viable for the kind of unsafe, low-level code C++ has traditionally been used for, and a mistake in unsafe code is incredibly costly - undefined behavior. So I suppose it made sense to them to expend extra effort on anything that helps you verify that some unsafe code works. For example, you can enforce that only certain trusted modules can perform unsafe operations like unchecked array accesses or pointer arithmetic.

Quick development was also a priority for them, so much so that they made it a design goal that D code compiles fast enough to make it usable as a scripting language. Baking unit tests right into the language so you can run your tests by just passing an extra flag to the compiler helps with that.

However, I think a great unit testing library does much more than just find some methods and run them. Take Haskell's QuickCheck for example, which lets you test things such as "for all x and y, f (x, y) == f (y, x)". QuickCheck is better described as a unit test generator and allows you to test things at a higher level than "for this input, I'm expecting this output". QuickCheck and Linq aren't all that different - they're both domain-specific-languages. So rather than bolting on unit testing support unto a language, why not add the features needed to make DSLs practical? You'll end up with not just unit testing, but a better language as a result.

share|improve this answer
3  
To stay in the .Net world, there's FsCheck, which is a port of QuickCheck to F#, with some helpers for use from C#. –  Arthur van Leeuwen Jul 3 at 19:58
    
To stay in the .NET world? This question has nothing to do with .NET. –  Miles Rout Jul 9 at 11:08

Because testing, and particularly test-driven development, is a deeply counter-intuitive phenomenon.

Almost every programmer begins their career believing they are much better at managing complexity than they actually are. The fact that even the greatest programmer cannot write large and complex programs without severe errors unless they use lots of regression tests is seriously disappointing and even shameful to many practitioners. Hence the prevalent disinclination against regular testing even among professionals who should already know better.

I think the fact that religious testing is slowly becoming more mainstream and expected is largely due to the fact that with the explosion in storage capacity and computing power, larger systems than ever are being built - and extremely large systems are particularly prone to complexity collapse that cannot be managed without the safety net of regression tests. As a result, even particularly obstinate and deluded developers now grudgingly admit that they need testing and will always need testing (if this sounds like the confession at an AA meeting, this is quite intentional - needing a safety net is psychologically hard to admit for many individuals).

Most languages popular today date from before this attitude shift, so they have little built-in support for tests: they have assert, but no contracts. I'm fairly sure that if the trend persists, future languages will have more support on the language rather than the library level.

share|improve this answer
3  
You overstate your case a bit. While unit testing is unquestionably of paramount importance, building programs the proper way, the way that minimizes unnecessary complexity, is equally important, if not more so. Languages like Haskell have type systems that improve the reliability and provability of programs written in them, and best practices in coding and design avoid many of the pitfalls of untested code, making unit testing less critical than it otherwise would be. –  Robert Harvey Jul 3 at 20:08
1  
@RobertHarve ...and unit testing is a very good way to indirectly push developers towards that. Working on the API while creating tests, rather than while using it with other code, helps to put them in the right mindset to limit or remove leaky abstractions or odd method calls. I don't think KillianFoth is overstating anything. –  Izkata Jul 3 at 23:05
    
@Robert: The only thing I beleive he overstates is "religious testing is slowly becoming more mainstream" If slowly is defined in terms of Geographic time frames he is probably not far off..... :) –  mattnz Jul 4 at 2:21
    
+1 In my current job, I'm struck by the tidal shift in development attitudes with the newer libraries and technologies available. For the first time in my career, these guys are lecturing me to use a more sophisticated development process, instead of me feeling like I'm on top of a hill shouting at the wind. I agree it's just a matter of time before these attitudes find expression in native language syntax. –  Rob Y Jul 4 at 2:41
1  
Sorry, one more thought re: Robert Harvey's comment. Right now type systems are a nice check, but really fall amazingly short of defining constraints on types -- they address the interface, but don't constrain correct behavior. (i.e. 1 + 1 == 3 will compile, but may or may not be true, depending on implementation of +) I wonder if the next trend will to see more expressive type systems that combine what we consider to be unit testing now. –  Rob Y Jul 4 at 2:44

A lot of languages have support for testing. C's asserts are tests that the program can fail. That's where most languages stop, but Eiffel and more recently Ada 2012 have preinvariants (things the arguments to a function must pass) and postinvariants (things the output of a function must pass), with Ada offering the ability to reference the initial arguments in the postinvariant. Ada 2012 also offers type invariants, so whenever a method is called on an Ada class, the type invariant is checked before return.

That's not the type of full-out testing a good testing framework can give you, but it's an important type of testing that languages can best support.

share|improve this answer
2  
Having done a little bit of work in Eiffel and being an extensive user of assertions, my experience has been that anything you can do that's contractual in nature helps to ferret out a lot of bugs. –  Blrfl Jul 4 at 15:21

Some proponents of strongly typed functional languages would argue that these language features reduce or eliminate the need for unit tests.

Two, imho, good example of this for this is from F# for Fun and Profit here and here

Personally, I am still believe in the value of unit tests, but there are some valid points. E.g. if an illegal state is unrepresentable in code, then it is not only unnecessary to write a unit tests for this case, it is impossible.

share|improve this answer

I would argue that you have missed the addition of some of the necessary features because they weren't being highlighted as for unit testing.

For example, unit testing in C# is mainly driven by using attributes. The Custom Attributes feature provides a rich extension mechanism that allows frameworks like NUnit to iterate and compete, with things like Theory-based and Parameterized testing.

This brings me to my second major point - we don't know enough about what makes good testability to bake it into a language. The pace of innovation in testing is much faster than other language constructs so we need to have flexible mechanisms in our languages to leave freedom to innovate.

I'm a user but not fanatic about TDD - it is very helpful in some cases especially for improving your design thinking. It is not necessarily useful for larger legacy systems - higher-level testing with good data and automation will probably yield more benefit along with a strong code inspection culture.

Other relevant reading:

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.