如何对我的类在 Dart 中侦听流并正确响应进行单元测试



考虑以下类:

enum LoginState { loggedOut, loggedIn }
class StreamListener {
final FirebaseAuth _auth;
LoginState _state = LoginState.loggedOut;
LoginState get state => _state;
StreamListener({required FirebaseAuth auth}) : _auth = auth {
_auth.userChanges().listen((user) {
if (user != null) {
_state = LoginState.loggedIn;
} else {
_state = LoginState.loggedOut;
}
});
}
}

我想测试当用户登录时状态从 loggedOut 更改为 loggedIn,请参阅以下测试代码:

class FakeUser extends Fake implements User {}
@GenerateMocks([FirebaseAuth])
void main() {
StreamController<User?> controller = StreamController<User?>();
final User value = FakeUser();
setUp(() {
controller = StreamController.broadcast();
});
tearDown(() {
controller.close();
});
test('Stream listen test', () {
final MockFirebaseAuth mockAuth = MockFirebaseAuth();
when(mockAuth.userChanges()).thenAnswer((_) => controller.stream);
StreamListener subject = StreamListener(auth: mockAuth);
controller.add(value);
expect(subject.state, LoginState.loggedIn);
});
}

但是,由于异步行为,登录状态仍处于注销状态。我怎样才能正确测试?

我不认为你可以以一种严格正确的方式进行测试。 您的StreamListener类承诺更新state以响应Stream事件,但它是异步的,您无法正式保证这些更新何时发生,并且您无法在这些更新最终发生时通知调用方。 您可以通过修改StreamListener来提供每当_state更改时发出的广播Stream<LoginState>来解决此问题。

从足够好的实际角度来看,您可以做一些事情:

  • 依靠 Dart 的事件循环来同步调用所有Stream的侦听器。 换句话说,在将事件添加到Stream后,允许 Dart 返回事件循环,以便Stream的侦听器可以执行:

    test('Stream listen test', () async {
    ...
    StreamListener subject = StreamListener(auth: mockAuth);
    controller.add(value);
    await Future<void>.value();
    expect(subject.state, LoginState.loggedIn);
    
  • 由于您使用的是广播Stream,因此您也可以依靠多个Stream侦听器按注册顺序触发。(我没有看到任何正式的文档保证这种排序,但我认为这是最明智的行为。在对象注册其侦听器后,测试可以注册自己的侦听器,使用expectAsync1验证测试的侦听器是否已调用,并让测试的侦听器验证对象的状态:

    StreamListener subject = StreamListener(auth: mockAuth);
    controller.stream.listen(expectAsync1((event) {
    expect(event, value);
    expect(subject.state, LoginState.loggedIn);
    }));
    controller.add(value);
    
  • 或者结合这些方法:

    test('Stream listen test', () async {
    ...
    var eventReceived = Completer<void>();
    StreamListener subject = StreamListener(auth: mockAuth);
    controller.stream.listen((_) => eventReceived.complete());
    controller.add(value);
    await eventReceived.future;
    expect(subject.state, LoginState.loggedIn);
    

相关内容

  • 没有找到相关文章

最新更新