Search code examples
flutterdartunit-testingasynchronoustesting

How can I fix the error of "unassigned variable" even tho the variable has been initialized?


I have this code :

void main() {
  RethinkDb r = RethinkDb();
  Connection connection;
  UserService sut;

  setUp(() async {
    connection = await r.connect(host: "127.0.0.1", port: 28015);
    await createDb(r, connection);
    sut = UserService(r, connection); // second line
  });

  test('creates a new user documenet in database', () async {
    final user = User(
      userName: 'test',
      photoUrl: 'url',
      active: true,
      lastSeen: DateTime.now(),
    );
    final userWithId = await sut.connect(user); // first line
    expect(userWithId.id, isNotEmpty);
  });
}

I'm having an error on "first line" that says that the sut variable must be initialized, but when you look at "second line", you can see that the sut is indeed initialized, and the setUp() function is called before the test().


Solution

  • There are cases where you don't need to explicitly use late even with non-nullable variables with no initial value. For example:

    void main() {
      int x;
      x = 42;
      print(x);
    }
    

    or:

    import 'dart:math';
    
    void main() {
      int x;
      if (Random().nextBool()) {
        x = 1;
      } else {
        x = 0;
      }
      print(x);
    }
    

    In those cases, through control flow analysis, the compiler can guarantee that all code paths result in x being initialized before it's used.

    but when you look at "second line", you can see that the sut is indeed initialized, and the setUp() function is called before the test().

    The problem is that the semantics of setUp() being called before test() (or more precisely, that setUp's callback is executed before test's callback) is the behavior described by the test framework, not by the language. It's non-trivial for the compiler to determine that the setUp callback is guaranteed to be executed before the test callback. Determining that would involve performing flow analysis on the implementations of those functions (and of any functions they call, and so on), which could be prohibitively expensive.

    That's the whole reason why the late keyword exists: to tell the compiler that you know more than it does. Using late means that you personally guarantee that the variable will be initialized before it's used.