Search code examples
dartunit-testing

How much code should we place into a constructor?


How much initialisation code should we place in a constructor? The problem is that putting in a lot of code results in problems during testing because some fields may depend on platform specific things, for example BLE or NFC. For example:

class MyClass{
 late Hardware hardware;

 MyClass()
 {
   hardware = Hardware();        //platform dependent object 
   hardware.onEvent.listen(..);  //subscribe to hardware events
    ...
 }

}

Then i want to test MyClass and i am getting problems because Hardware isn't valid during the test.

test('test something', () {
  var myClass = MyClass();//errors or bad behaviour here
  
   ...
});

});

Maybe it has sense to make the constructor as simple as possible and then use an "init" function?


Solution

  • The solution to this particular problem would be to have the class refer to the "hardware" through an interface that defines its behaviour but not its implementation.

    If the Hardware class doesn't have an interface (or it is too specific for you) you can create your own and create an adapter class around the hardware that implements the interface.

    The actual hardware object (possibly wrapped in an adapter) should then be passed to the class either in the constructor or using a setter (rather than using new to create the implementation). That way, the class doesn't know the specifics of the hardware that it is dealing with.

    In testing, you can create mock hardware classes to emulate the real one.

    The actual hardware implementation object would be created externally to this class in some piece of code whose job it is to know the specifics. You might even construct the hardware object dynamically.

    You might find it useful to look up "dependency injection" and frameworks like Spring.

    Another alternative way of solving the "test object" problem is using a library such as Mockito that creates mock objects.