Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I prefer development driven testing or DDT. Don't write tests until the refactoring churn has settled down a bit. Then write unit tests to to document expected behavior and prevent anyone from breaking that code in the future.


I like to treat tests as extended compiler warnings. E.g. in Haskell the type system is so strong, that once the compiler stops yelling at you, your code is more often than not correct. To catch the latter case, I like to write properties in QuickCheck.

QuickCheck is a Haskell framework. It allows you to formulate properties about your functions. Like --- given any list of comparable items, `sort' should conserve its length, produce an ordered list, and conserve the occurrence (and non-occurence) of each item. It should also be idempotent.

QuickCheck then generates a lot of random inputs and tests whether your properties hold. You (almost) never have to write test cases by hand. And since Haskell is pure, you never have to care about setting up the right state before invoking your tests.

This approach allows me to code with success, even when I am not intensively focused. (Of course Haskell helps with this even without QuickCheck. Not having to worry about state, assignment, order of execution, loop indices or recursion keeps the number of items I need to hold in my working memory low.)


Right. With Haskell and OCaml, a lot of testing can be pushed onto the type system. The language is better about letting you express semantic constraints via types, without making you declare every single instance of everything the way C and its descendants do.

I wrote a QuickCheck-eqsue library for Lua, FWIW: http://silentbicycle.com/projects/lunatest/ (It's the first released version, and the documentation still needs work...) For table "objects" and userdata, it checks the metatable for how to generate an arbitary instance.


> With Haskell and OCaml, a lot of testing can be pushed onto the type system.

Yes. There's even a paper about how to guarantee the balance invariants in a red-black tree with the type system. (Can't find it now, it's probably written by Ralf Hinze.) You can see an implementation at http://www.cs.kent.ac.uk/people/staff/smk/redblack/rb.html


The problem with that is that unit tests are so useful during refactoring. If you have enough unit tests, you can pretty much refactor at will without worrying about your program silently breaking.


If you are using a language that is amenable to it, you can refactor at will and the IDE will verify its correctness.

Many people use unit tests as a crutch for a lack of strong typing.


This isn't always (or even usually) true. Sure, the IDE will verify the compile time correctness of the code, but unit tests check for run time correctness. So if you refactor a method to add an additional parameter, unit tests will catch the null that Eclipse inserted into all method calls to make the code compile.


But that is not refactoring, that's adding a new feature!


If you are changing the signature of a method that is part of the implementation in order to improve readability, clean up design, or remove dead code, it is refactoring. Refactoring isn't just as simple as renaming classes and methods; it often requires more significant changes than that.

Example:

  /* old */
  LoadWeapon(WeaponTypeEnum type);
  UnloadWeapon(WeaponTypeEnum type)
  LaunchWeapon(WeaponTypeEnum type);
  /* new */
  WeaponCommand(WeaponTypeEnum type, WeaponCommandEnum command);
Note... assume that the interface is a GUI, so changing these methods doesn't change any external interface.


Not really. Reordering declarations, for instance, is a "safe" refactoring, though not when you're using reflection at some point.


The nulls point to a lacking type system.


You'd need a halting problem solving IDE for this to work in general.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

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

Search: