One aspect of unit tests is to have the specs in runnable form.
However when employing a declarative style, that directly maps the
formalized specs to language semantics, is it even actually possible
to express the specs in runnable form in a separate way, that adds
value?
If you have specs which can be mapped directly to function declarations - fine. But typically those are two completely different levels of abstractions. Unit tests are intended to test single pieces of code, written as white box tests by the same developer who is working on the function. Specs normally look like "when I enter this value here and press on this button, this and that should happen". Typically such a spec leads to far more than one function to be developed and tested.
However a simple mistake like using x
instead of y (both being coordinates) in your code cannot be covered.
However such a mistake could also arise while writing the test code,
so I am not sure whether it is worth the effort.
Your misconception is here that unit tests are actually for finding bugs in your code first hand - that's not true, at least, it is only partially true. They are made to prevent you from introducing bugs later when your code evolves. So when you first had your function tested and your unit test working (with "x" and "y" properly in place), and then while refactoring you use x instead of y, then the unit test will show that up to you.
Unit tests do introduce redundancy, which means that when requirements
change, the code implementing them and the tests covering this code
must both be changed. This overhead of course is about constant, so
one could argue, that it doesn't really matter. In fact, in languages
like Ruby it really doesn't compared to the benefits, but given how
statically typed functional programming covers a lot of the ground
unit tests are intended for, it feels like it's a constant overhead
one can simply reduce without penalty.
In engineering, most safety systems rely on redundancy. For example, two breaks in a car, a redundant parachute for a sky diver etc. The same idea applies to unit tests. Of course, having more code to change when the requirements change can be a disadvantage. So especially in unit tests it is important to keep them DRY (follow the "Dont Repeat Yourself" principle). In a statically typed language, you may have to write some less unit tests than in a weakly typed language. Especially "formal" tests may be not necessary - which is a good thing, since it gives you more time to work on the important unit tests which test substantial things. And don't think just because you static types, you don't need unit tests, there is still plenty of room to introduce bugs while refactoring.
Short Answer: Absolutely positively.
Long Answer: Unit tests are one of the most important practices I try and influence at my place of work (large bank, fx trading). Yes they are extra work, but it's work that pays back again and again. Automated unit tests not only help you actually execute code you're writing and of course verify your expectations but they also act as a kind of watch dog for future changes that you or someone else might make. Test breakage will result when someone changes the code in undesirable ways. I think the relative value of unit tests declines in correlation with the level of expected change and growth in a code base, but initial verification of what the code does make it worthwhile even where the expected change is low. Unit test value also depends on the cost of defects. If the cost (where cost is loss of time/money/reputation/future effort) of a defect is zero, then the relative value of a test is also zero; however this is almost never the case in a commercial environment.
We generally don't hire people anymore who don't routinely create unit tests as part of their work - it's just something we expect, like turning up every day. I've not seen a pure cost benefit analysis of having unit tests (someone feel free to point me to one), however I can say from experience that in a commercial environment, being able to prove code works in a large important system is worthwhile. It also lets me sleep better at night knowing that the code I've written provably works (to a certain level), and if it changes someone will be alerted to any unexpected side effects by a broken build.
Test driven development, in my mind is not a testing approach. It's actually a design approach/practice with the output being the working system and a set of unit tests. I'm less religious about this practice as it's a skill that is quite difficult to develop and perfect. Personally if I'm building a system and I don't have a clear idea of how it will work I will employ TDD to help me find my way in the dark. However if I'm applying an existing pattern/solution, I typically won't.
In the absence of mathematical proof to you that it makes sense to write unit tests, I encourage you to try it over an extended period and experience the benefits yourself.
Best Answer
You should definitely take the same if not better care of your unit tests than your production code in terms of quality and readability. The unit tests are often the first thing you look at when trying to grasp what some piece of code does, and the reader should quickly understand what's at stake when looking at the test. Unit tests also tend to change a lot and will break a lot if their design is not robust, which kind of nullifies the benefits of having tests.
Violation of the Law of Demeter is definitely a problem in your unit tests for that reason as well as 2 others that come off my mind :
If your tests break the Law of Demeter in their Arrange or Act sections, it's probably a sign that your production code also does, since your unit tests are just another consumer of your code and will probably call and operate the class under test in the same way that any other production code would do.
If your tests break the Law of Demeter in their Assert sections (ie you verify the value of something that is deeply nested in the dependencies graph of the object under test), it might be that those are really integration tests rather than unit tests. In other words, if in TestA you assert that A.B.C.D equals something, it might be that you're actually trying to test D and A rather than just A.
By the way, when you say
you should be aware that writing
is actually no better Demeter wise than
if that's what you meant.