Hacker News new | ask | show | jobs
by latch 5534 days ago
How do you test code that relies on the current time in C#/Java. I've seen a couple solution. Injecting some type of abstract Clock/SystemClock everywhere being common. C#'s lambda's let you do something nicer, like:

  class SystemTime
  {
    public Func<DateTime> Now => () = DateTime.Now;
  }
Ultimately though, you're having to work around the fact that Java and C# are class-oriented, rather than object-oriented. Classes are useful - they serve as a template for your object, but in most (all?) dynamic languages, the class itself is a living, breathing, modifiable, runtime object.

What does all this mean? How do you get the current time in a testable manner in Ruby? Use Time.now. You don't implement patterns or change your code. You don't fear the evil static..because really, it isn't a static - we don't even call it a static. It's just another method on another class, which we can rewrite at runtime as need be.

How do you stub it? Well, using a mocking framework:

Time.stub!(:now).and_return(Time.local(2011, 5, 3, 0, 1, 22))

The mechanics of how this is internally done isn't too complicated once you know about Ruby's metaprogramming (which probably takes around the same amount of time as getting familiar with C# or Java's reflection capabilities).

I know, it's a simple example. But it's surprisingly common. And I find it a little ridiculous that to get the time, we need to start creating interfaces and injecting parameters. While C#'s lambda's largely solve this problem, move to something just slightly more complicated, and you're back at square one.

I dunno, not sure if that explained it without being condescending. I'm tired...that stub is actually returning a pretty accurate time for me.

1 comments

That's a nice explanation, without any of the bits I disliked from the original post, I appreciate it. :)

Essentially the benefit we're pushing here is cheap decoupling where in static languages you're having to set this kind of arduous plumbing up yourself, aye?

Right, with dynamic languages, decoupling is a language feature, not a design decision. It's one of the things that I feel make dynamic languages so much more productive. I work with large Java/C# applications. The loss of productivity due to dealing with traditional dependency handling, both at the implementation and at the test level, is significant.

I'm not going to say that it's as big a leap as automatic garbage collection (because, I don't think it is), but I see similarities between a C++ developer who doesn't understand why Java/C# don't have a malloc and delete method, and a Java/C# developer who doesn't understand why XYZ don't have interfaces nor use DI.

We spend so much time doing something a certain way, that it's hard to see how, given a different context (a new language) what we were trying so hard to avoid in the first place, just works.