Search code examples
dartdart-async

Waiting for my class to initialize (or how to wait for a Future to complete)?


Futures in Dart are the bane of my existence.

I have a class, which calls an async (Future) function to start up the database instance like so:

class DataManager {
  bool DbIsReady = false;
  Db _db;

  DataManager() {
    init_mongo_db();
  }

  void init_mongo_db() {
    print("Initializing MongoDB");
    _db = new Db("mongodb://127.0.0.1/test");
    _db.open().then((_) {
      DbIsReady = true;
    });
  }  

  Future<List> attemptLogin(String username, String password) {
    users = _db.collection("users");

    return // ... rest of code cut out for clarity
  }
}

This works OK server-side because when the server first starts up, the database is initialized. By the time a user actually attempts to log in, the database has been initialized and all is well. However, this fails miserably when I try to create an integration test for this. When I create the class, the database isn't yet initialized, so running the AttemptLogin routine fails.

DataManager db = new DataManager();
// This fails miserably, because db hasn't initialized yet.
db.AttemptLogin("test", "test"); 

Worse, the code uses the Dart DI framework, so I don't actually have direct control over the initialization of the DataManager class. This is the actual setup of the class:

setUp(() {
  app.addModule(new Module()
            ..bind(DataManager)
  app.setUp();
});

And then this is the call to test the login functionality, which ultimately calls the attemptLogin function which fails:

var req = new MockRequest("/user/login", contentType: 'JSON', method:'POST',
    body:JSON.encode(
      {"username" : 'test',
       "password" : 'test' }));

How does one deal with the async nature of the the database init and still do mock testing? Specifically, is there a way to force the attemptLogin() Future to somehow wait for the completion of the DataManager class initialization?

Thanks for your help, Greg


Solution

  • what about using @lrn s solution like

    setUp(() {
      return DataManager.createNew().then((dm) {
        app.addModule(new Module()
                ..bind(DataManager, toValue: dm);
        app.setUp();
      }
    });
    

    This way you already pass an initialized DataManager instance into the DI. If you request it later on you always can be sure it is already initialized.