views:

435

answers:

5

Its been a while I have ready Mcconnell's "Code Complete". Now I read it again in Hunt & Thomas' "The Pragmatic Programmer": Use assertions! Note: Not Unit Testing assertions, I mean Debug.Assert().

Following the SO questions When should I use Debug.Assert()? and When to use assertion over exceptions in domain classes assertions are useful for development, because "impossible" situations can be found quite fast. And it seems that they are commonly used. As far as I understood assertions, in C# they are often used for checking input variables for "impossible" values.

To keep unit tests concise and isolated as much as possible, I do feed classes and methods with nulls and "impossible" dummy input (like an empty string).

Such tests are explicitly documenting, that they don't rely on some specific input. Note: I am practicing what Meszaros' "xUnit Test Patterns" is describing as Minimal Fixture.

And that's the point: If I would have an assertions guarding these inputs, they would blow up my unit tests.

I like the idea of Assertative Programming, but on the other hand I don't need to force it. Currently I can't think of any use for Debug.Assert(). Maybe there is something I miss? Do you have any suggestions, where they could be really useful? Maybe I just overestimate the usefulness of assertions? Or maybe my way of testing needs to be revisited?

Edit: Best practice for debug Asserts during Unit testing is very similar, but it does not answer the question which bothers me: Should I care about Debug.Assert() in C# if I test like I have described? If yes, in which situation are they really useful? In my current point of view such Unit Tests would make Debug.Assert() unnecessary.

Another point: If you really think that, this is a duplicate question, just post some comment.

+2  A: 

If you're doing exhaustive unit testing which covers all the odd edge cases you might encounter, then I don't think you are going to find assertions very useful. Most people who don't unit test place assertions to establish similar constraints as you catch with your tests.

mquander
For me that means: Assertions are good, when there are no unit tests in place. Although they can't replace unit tests, it's better than nothing.
Theo Lenndorff
Frankly, I would say that assertions (in the form that most people use them) are almost the same thing as unit tests. People usually stick them in to verify that the input going into a function is something they can deal with, and that the output from a function is what they expect it to be, which is exactly what your unit test should do if it tests that function. The biggest difference is that your unit test suite often isn't quite in the same spot as your code, whereas assertions are inline.
mquander
Of course, assertions sometimes also check more fine-grained things than unit tests, like making sure memory got allocated correctly, or that a connection got made properly.
mquander
"whereas assertions are inline". That brings me to another point: Assertions have some documentative character in the code. They are yelling to the developer: DON'T DO THAT!
Theo Lenndorff
They still blow up my tests, if I would e.g. assert that a connection is opened correctly, I would need to stub/mock/fake this behavior in the unit test, which might be cumbersome.
Theo Lenndorff
+2  A: 

In theory, you're right - exhaustive testing makes asserts redundant. In theory. In parctice, they're still useful for debugging your tests, and for catching attempts by future developers who might try to use interfaces not according to their intended semantics.

In short, they just serve a different purpose from unit tests. They're there to catch mistakes that by their very nature aren't going to be made when writing unit tests.

I would recommend keeping them, since they offer another level of protection from programmer mistakes.

They're also a local error protection mechanism, whereas unit tests are external to the code being tested. It's far easier to "inadvertently" disable unit tests when under pressure than it is to disable all the assertions and runtime checks in a piece of code.

Ori Pessach
"... for catching attempts by future developers who might try to use interfaces not according to their intended semantics.". Hmmm ... I can think of niche, where they are useful: When the semantics of an interface is not apparent, putting an assert makes this very explicit.
Theo Lenndorff
Thinking again about it, this makes pretty much sense: Using assertions this way is beneficial during development and are aimed to uncover programming errors (rather some input error from a user/database/webservice/...)
Theo Lenndorff
Exactly. It's another aspect of programming by contract, where the contract can't be enforced at compile time but the runtime can catch violations.
Ori Pessach
+2  A: 

I'm using both unit testing and assertions, for different purposes.

Unit testing is automated experimentation showing that your program (and its parts) function as specified. Just like in mathematics, experimentation is not a proof as long as you cannot try every possible combination of input. Nothing demonstrates that better than the fact that even with unit testing, your code will have bugs. Not many, but it will have them.

Assertions are for catching dodgy situations during runtime that normally shouldn't happen. Maybe you've heard about preconditions, postconditions, loop invariables and things like that. In the real world, we don't often go through the formal process of actually proving (by formal logic) that a piece of code yields the specified postconditions if the preconditions are satisfied. That would be a real mathematical proof, but we often don't have time to do that for each method. However, by checking if the preconditions and postconditions are satisfied, we can spot problems at a much earlier stage.

DrJokepu
I don't do the whole 100% unit testing thing, but I thought that covering preconditions and postconditions was pretty much the point of it; automated testing that really covered all of your edge cases, fed all the possible kinds of data that might go in, and tested all the data coming out.
mquander
Assertions are not explicitly made for checking preconditions and postconditions, but are still useful to emulate it. That's another point where assertions are useful.
Theo Lenndorff
A: 

I think the idea behind Unit Testing in this case is to move those assertions over to the test cases to ensure that, instead of having Debug.Assert(...) your code under test handles it without throwing up (or ensures that it's throwing up correctly).

SnOrfus
+4  A: 

I generally see asserts being used for sanity checks on internal state rather than things like argument checking.

IMO the inputs to a solid API should be guarded by checks that remain in place regardless of the build type. For example, if a public method expects an argument that is a number in between 5 and 500, it should be guarded with an ArgumentOutOfRangeException. Fail fast and fail often using exceptions as far as I'm concerned, particularly when an argument is pushed somewhere and is used much later.

However, in places where internal, transient state is being sanity-checked (e.g. checking that some intermediate state is within reasonable bounds during a loop), it seems the Debug.Assert is more at home. What else are you meant to do when your algorithm has gone wrong despite having valid arguments passed to it? Throw an EpicFailException? :) I think this is where Debug.Assert is still useful.

I'm still undecided on the best balance between the two. I've stopped using using Debug.Asserts so much in C# since I started unit testing, but there's still a place for them IMO. I certainly wouldn't use them to check correctness in API use, but sanity checking in hard to get to places? Sure.

The only downside is that they can pop up and halt NUnit, but you can write an NUnit plugin to detect them and fail any test that triggers an assert.

Mark Simpson
Lol at EpicFailException... I will have to use that in my code (combined with Over9000Exception).
DrJokepu
"e.g. checking that some intermediate state is within reasonable bounds during a loop" ... Assertions for checking loop invariants! That's a good point. That's something I can't really check with unit tests (at least not with more or less mocking/stubbing and introducing some spy variables).
Theo Lenndorff
Those pop ups are quite annoying ... dunnot know what happens, when an assertion strikes when it happens in windows service ... might be worth another question ;-)
Theo Lenndorff