Hacker News new | past | comments | ask | show | jobs | submit login
How to write untestable code (googletesting.blogspot.com)
74 points by bigsassy on Nov 5, 2009 | hide | past | favorite | 29 comments



Could someone explain: "Be Defensive - They're out to Get Your Code! - Defensively assert about the state of parameters passed in methods, constructors, and mid-method."?

which, desarcastified, I take to mean:

"Don't defensively assert about the state of parameters passed in methods, constructors, and mid-method."

Specifically:

"You see, there are testing freaks out there that like to instantiate your object, or call a method under test and pass in nulls"

Isn't it the case that a calling a method with (say) null parameters where they are supposed to be non-null should raise an exception? Isn't an assertion failure reasonable in this case?

I understood and agreed with a lot of the other stuff (although it struck me as a bit dogmatic) but this one I don't get.


I think what the author is getting at is testing a particular method on an object without having to mock up all the data required by the entire object. I guess?

Agreed that the whole article would be more useful without the sarcasm.


So that's basically allowing operations on a half-broken object, just so that you can test it without creating all the dependencies?... I see how it might be useful, but I just can't agree with that.

The tests are not the goal - a working product is. That's just making a trade-off between less lines in a test -vs- code that doesn't explode mid-transaction when that null suddenly occurs in a real-world scenario. If I'm supposed to write my code properly, testies should not be too lazy while writing the tests.


It's no longer a unit-test once you're putting the object into the living system. It becomes a system test and then you might as well test the whole system instead of faking things with mockups.


If you use Java, EasyMock makes this a non-issue.

I have found that with a test framework like EasyMock, you no longer have to jump through so many of these goofy tdd hoops. You can mock methods, return values, thrown exceptions, parameters to methods to another object inside a method, etc. It still doesn't warrant writing crap code using anti-patterns, but nevertheless, it makes life more bearable by putting the focus back on the logic at hand and less on trying to follow the tdd mantra of design.


I interpret this to mean two things:

1. If you are going to assert these things, don't do it in the middle of the method - do it in a place that makes sense (start of the method).

2. One of the members of my team likes to make assertions about parameters that aren't actually valid but just sound like they are, for example, wrapping the entire code in a method with an int id parameter with a

  if (id > 0) { 
     ...
  }
block so that the method silently does nothing if you pass in a negative number. Except of course that there's nothing in our domain model to say that IDs can't wrap zero into negative space or that this isn't a valid scenario.

So in other words, having a bunch of unnecessary assertions that spring up in seemingly random places makes your code hard to test.


> 1. If you are going to assert these things, don't do it in the middle of the method - do it in a place that makes sense (start of the method).

Sure, I can see that -- assertions in the middle of a method might well indicate that maybe that bit should be split into more than one method.

But the article goes further than that: they seem to complain about assertions guarding parameter values. I really don't get that. Actually, I'll go one further: complaining about that is unjustifiable.

Anyway, I also don't always get the particular flavour of OOP dogma that Java devs sometimes seem to promote. It certainly feels different to me from Python world, and I suppose it is.

When I see the phrase "silently does nothing" I feel vaguely uneasy.


> Isn't it the case that a calling a method with (say) null parameters where they are supposed to be non-null should raise an exception?

Not really. Checking parameters in every method (especially an OO system) is a sure way to have poor performance. Null values as other kinds of testing should be done in the areas of the system that create and receive objects from outside.

Normal methods should just work find even if a parameter is NULL. Although this doesn't look nice, it is a way to make the system testable, in the absence of true NullObjects.


> Checking parameters in every method (especially an OO system) is a sure way to have poor performance.

This seems overly general to me. I'm not sure it's a "sure way to have poor performance." It seems to me that (for example) a null-check is pretty fast.

> Normal methods should just work find even if a parameter is NULL.

What's a "normal method"? And if the business logic of the method requires a particular parameter to be non-null (or has some other constraint), what should happen when a null parameter is passed-in (by a test or otherwise)?


Could someone explain this one?

> Be Defensive [...] Defensively assert about the state of parameters passed in methods, constructors, and mid-method. If someone can pass in a null, you've left your guard down. You see, there are testing freaks out there that like to instantiate your object, or call a method under test and pass in nulls!

I'm not sure what he wants to change. Surely if the code would crash when I pass a null as an argument, it's better to make it throw the exception as early as possible. You can test that by asserting an exception when you call that_func(null);.

Why is that a bad thing? (Or did I misunderstand him?)


In the comments he admits that public APIs should be defensive, but internal APIs should be 'known good' via testing.


I'm pretty sure this was copied word for word from the employee guide of the firm that originally wrote the software I now trudge through every day. Except it wasn't sarcastic. And I think there was some other rule in there about avoiding spacing in the source code at all costs.


Jesus, basically don't worry about the product, just worry about having testable code.

I thought "having testable" code, is never the end, maybe the means perhaps, but not necessary,

If you have done mobile development, you know you have to break most of these rules at some point. Sorry, I'd rather have a shipping/peformant product, than one that is a hog, but makes OO purists happy, and is easy to test.

At the end of the day, keeping your customer happy, that is the end game.


What's wrong with

Create Utility Classes and Functions/Methods - For instance, you have a String which is a URL you're passing around (obeying "Use Primitives Wherever Possible"). Create another class with static methods such as isValidUrl(String url)

if the function is a deterministic function (I guess it could be implemented as a simple test against regex) why is is difficult to test? I get that its better to use the system one or your own URL class, but thats really about OO and not testing!


The keyword is "static". You'll often want to use a mock implementation rather than connecting to a remote server, checking for a 404 error and returning true or false - every time you run your tests.

Loosely coupled objects will allow you to do that, but how would you redefine the behavior of something static?


It's too bad Java/C++ were designed after unit testing became commonplace. Some of the advice here will actually improve your production code but a lot of this is just a case of the programmer having to work around the language.

For example, the advice to not "instantiate objects using new in the middle of methods" wouldn't be a big deal if if your test code could easily instruct the compiler to replace the concrete object with a mock when the test is run.


Nothing prevents that being implemented in existing compilers. In fact, you can already do that in Java with class paths.


Should be "How to write untestable code in Java".

Makes me glad I'm not writing Java, fewer than half of these are problems for me.


A little too vague - don't use if? switch? Surely they are useful. Better to advise against putting application logic into the wrong object - which is what I THINK they are arguing against.


Mostly assumes object-oriented programming, of course.


With a smug smile on my face, I notice that most of this list is impossible to do in Haskell.


Greenlight if-branches and switch statements

OO cowboys will want to have a whole polymorphic soup of collaborating objects

Of Course! When I implement, say, Bubble Sort, I do not implement the classes BubbleList with the methods sort and addObject: anObject and BubbleObject with the method bubbleMaybe: aBubbleList, just to see my tests for BubbleObject fail when I switch to Quicksort.


Some pretty darn good advice in here. This might be slightly embarrassing, but it actually makes me appreciate the value of Factories; something I always thought was a little overkill for anything but GUI code.


They are overkill. They're java man's substitute for lambdas. But if all you have is java, everything looks like an object.


I don't understand this. Are you using closures as types? If so, what can they offer that factories can't? Trying to understand, genuinely curious.


They offer the same functionality (more or less)... Factory objects just feel like overkill - usually they carry no state of their own, so why are they objects? If you could pass around functions, you could just use them instead.

    x_factory_proto :: GlobalParams -> InitParams -> X
    ...
    x_factory = x_factory_proto some_params


except ints, chars and bools.


Further proof that premature optimization is the root of all evil. (Compilers can automatically solve the performance issue of boxed native types now, so there is no reason to provide the programmer with unboxed types. But of course Java does because... it's Java.)


How about a million lines of code in VB6?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: