Hacker News new | ask | show | jobs
by palish 5534 days ago
A better question might be, "Would this pattern impact our ability to write unit tests?" I believe the answer is "No."

Let's say the module Foo depends on the Graphics subsystem. That is, Foo.cpp has the code:

  #include "GrSubsys.h"

  //-------------------------------------------------------
  Foo::Foo( const string& name )
  {
    // fetch a handle to our model (loading it if necessary).
    _model = gGrSubsys->GetModel( "models/" + name );
  }

  //-------------------------------------------------------
  bool
  Foo::IsValid()
  {
    return ( _model != NULL );
  }
In order to write a unit test that takes into account the aforementioned Singleton pattern, you might write:

  //-------------------------------------------------------
  void
  Test_EngineComponents()
  {
    // prepare for science.
    AppStartup();
   
    //==========================
    // Test #1 - Foo
    //==========================
    {
      // load a Foo entity.
      Foo* sunTzu = new Foo( "test/warlord" );
    
      // verify the entity loaded successfully.
      assert( sunTzu->IsValid() );

      // shutdown.
      delete sunTzu;
    }

    // conclude our science.
    AppShutdown();
  }

and AppStartup() is the function which initializes the subsystem singletons (and those will initialize their manager singletons).

It's about discipline. Any fool can butcher with any tool.

1 comments

Unless I have missed something about unit testing somewhere I am pretty sure that fails the "unit" part of unit testing. Unless that "#include GrSubsys.h" is actually importing a mock graphics subsystem.
A mock graphics subsystem isn't possible since it depends directly on the video card.
My initial point was that the code isn't really unit testable. From what I know, this is common in video games..too much code tied too hardware.

I'm certainly not saying this is bad. I am saying that, from my experience, video game programming is very different than other sorts of programming.

Ah. I gave an example of running a unit test against the hardware. I'd like to understand why it's not really testable? Not to defend myself -- to better myself.
Again, video programming is different..you might want to seek out the advice of people from that field specifically.

We have an analogue in enterprise/web programming. Code that interacts against the database. Do you, or don't you, stub/mock out the data layer code? Traditionally people did mock this out. Then Rails came along with ActiveRecord...and now hitting the DB isn't only common, I think a lot of people agree that, at a point, it's the right approach. Because, after all, what are you really testing if you don't hit an actual DB?

But, that's specific to code that has dependencies on outside systems (like the DB, or your graphic subsystem). When I look at your Foo class and the IsValid method, I see a pretty specific unit that really can and should be tested separately from an actual GrSubsys implementation. I mean, you've given it a dependency on GrSubsys, but I'm not sure that's right.

Should the test read:

"it is invalid when the model is null"

or

"it is invalid when the graphic subsystem returned null"

?

The difference is subtle, but important. The first is decoupled from the implementation. Foo is invalid when it's model is null. The second is coupled to the depedency: Foo is only invalid when the graphic subsytem returned null.

What value does that extra coupling, within your test, get you? What should IsValid (either the test, or the implementation) care that why the model is null? Why add that extra complexity which makes your test more likely to break due to changes to the implementation of the dependency?

If you test Foo independently, and then you test that gGrSubsys returns null on an invalid model separately, the two unit tests end up working together in isolation from changes you might make to the other.

Sorry fella but you don't understand the concept of unit testing. I suggest you do some reading on the subject.

http://en.wikipedia.org/wiki/Unit_testing

"In a unit test, mock objects can simulate the behavior of complex, real (non-mock) objects and are therefore useful when a real object is impractical or impossible to incorporate into a unit test."

Why would you want to simulate the graphics subsystem (and therefore the video card)?

The goal here isn't performance testing. It's to check whether the engine works.

You aren't testing the engine. You are testing that the unit, in your example the class Foo, responds correctly to the state and inputs you provide it. Decoupling those responses from other units such as the graphics subsystem gives you the assurance that under the tested conditions the unit works as intended.

What you are talking about is a regression or functional test. Same genus different species, still very useful but not the same tool.

Edit: Going to bed, if someone would like to explain by morning why this needed to be down voted so brutally I would be much obliged!

It's impossible to unit test a component whose criteria for 'working' is tightly coupled to dependencies out of your control (video hardware & drivers).

Thus you'll never be able to unit-test a video game completely. Or even fulfill other forms of testing with the same breadth as is possible in other software, unless you have every possible configuration to test on, or stick with consoles. Games are among the buggiest of all software, but it's not for want of trying!