Examples

Testing Setup Example

A ready-to-copy test suite harness for Authyra-based auth, fake provider, InMemoryStorage, no network.

A complete test/auth_test.dart you can adapt directly. No network, no singleton, no mocking framework.

test/auth_test.dart
import 'package:test/test.dart';
import 'package:authyra/authyra.dart';

const _validEmail = 'alice@example.com';
const _validPassword = 'secret';

AuthyraClient _makeClient({AuthCallbacks? callbacks}) => AuthyraClient(
  providers: [
    CredentialsProvider.withTokens(
      id: 'email',
      authorize: (creds) async {
        if (creds?.email == _validEmail && creds?.password == _validPassword) {
          return AuthSignInResult(
            user: AuthUser(id: '1', email: _validEmail),
            accessToken: 'fake-access',
            refreshToken: 'fake-refresh',
            expiresAt: DateTime.now().add(const Duration(hours: 1)),
          );
        }
        return null;
      },
    ),
  ],
  storage: InMemoryStorage(),
  callbacks: callbacks,
);

void main() {
  group('sign in / sign out', () {
    late AuthyraClient client;

    setUp(() async {
      client = _makeClient();
      await client.initialize();
    });

    test('returns user on valid credentials', () async {
      final user = await client.signIn('email', params: CredentialsSignInParams(
        email: _validEmail,
        password: _validPassword,
      ));
      expect(user.email, _validEmail);
    });

    test('stores an access token in the session', () async {
      await client.signIn('email', params: CredentialsSignInParams(
        email: _validEmail, password: _validPassword,
      ));
      expect(await client.getAccessToken(), 'fake-access');
    });

    test('throws on wrong password', () {
      expect(
        () => client.signIn('email', params: CredentialsSignInParams(
          email: _validEmail, password: 'wrong',
        )),
        throwsA(isA<AuthenticationFailedException>()),
      );
    });

    test('isAuthenticated is false after sign out', () async {
      await client.signIn('email', params: CredentialsSignInParams(
        email: _validEmail, password: _validPassword,
      ));
      await client.signOut();
      expect(await client.isAuthenticated(), isFalse);
    });
  });

  group('events', () {
    test('emits SignInEvent on success', () async {
      final client = _makeClient();
      await client.initialize();

      SignInEvent? captured;
      client.events.on<SignInEvent>((e) => captured = e);

      await client.signIn('email', params: CredentialsSignInParams(
        email: _validEmail, password: _validPassword,
      ));

      expect(captured, isNotNull);
      expect(captured!.user.email, _validEmail);
    });
  });

  group('callbacks', () {
    test('a denial blocks sign-in and persists nothing', () async {
      final client = _makeClient(callbacks: _DenyAllCallbacks());
      await client.initialize();

      expect(
        () => client.signIn('email', params: CredentialsSignInParams(
          email: _validEmail, password: _validPassword,
        )),
        throwsA(isA<AuthenticationFailedException>()),
      );
      expect(await client.isAuthenticated(), isFalse);
    });
  });
}

class _DenyAllCallbacks extends AuthCallbacks {
  @override
  Future<CallbackResult> onBeforeSignIn(String providerName, AuthSignInParams? params) async {
    return const CallbackResult.deny('blocked for test');
  }
}

Isolation

Give each test its own InMemoryStorage(), it's a plain in-memory map with no cross-test cleanup, so reusing one instance leaks state between tests.

If you use Authyra.instance in the code under test

tearDown(() async {
  if (Authyra.isInitialized) {
    await Authyra.instance.dispose();
  }
});

Prefer testing against an injected AuthyraClient wherever your app's structure allows it; it sidesteps the singleton entirely.


See also

Copyright © 2026