I am new to unit testing, and currently working on a new release of one of my packages to be testable.
Currently, I am struggling to see when and what to test. Take the following script for reference:
class AuthService {
AuthService({
required this.client,
});
final SimpleGraphQl client;
Future<LoginResponseDto> login({
required String username,
required String password,
}) async {
const query = r'''
query Login($username: String, $password, String) {
login(username: $username, password: $password) {
success
token
}
}''';
final variables = {'username': username, 'password': password};
final response = await client.query<LoginResponseDto>(
variables: variables,
query: query,
resultBuilder: (data) {
return LoginResponseDto.fromJson(
data['login'] as Map<String, dynamic>,
);
});
return response;
}
}
class LoginResponseDto {
LoginResponseDto({
required this.success,
required this.token,
});
final bool success;
final String? token;
factory LoginResponseDto.fromJson(Map<String, dynamic> json) {
return LoginResponseDto(
success: json['success'] as bool,
token: json['token'] as String?,
);
}
}
This service handles authentication logic using a GraphQL wrapper called SimpleGraphQl, which takes the GraphQL query, variables, and a callback for response serialization. The service examples include just the login
method.
When deciding which case scenarios I should test, I decided to test:
- Client query
variables
parameter must include username and password keys. - Test success and failure login attempts (e.g. when
success
in response is eithertrue
orfalse
).
So, I wrote the following tests:
void main() {
group('AuthService', () {
late SimpleGraphQlMock client;
setUp(() {
client = SimpleGraphQlMock();
});
test('login mutation includes "username" and "password" variables',
() async {
final service = AuthService(client: client);
when(
() => client.query<LoginResponseDto>(
query: any(named: 'query'),
variables: any(named: 'variables'),
resultBuilder: any(named: 'resultBuilder'),
),
).thenAnswer(
(_) async => LoginResponseDto.fromJson(
{'success': true, 'token': 'test_eyJhb....sw5c'},
),
);
await service.login(username: 'john_doe', password: '12345');
final captured = verify(
() => client.query<LoginResponseDto>(
query: any(named: 'query'),
variables: captureAny(named: 'variables'),
resultBuilder: any(named: 'resultBuilder'),
),
).captured;
/// Login mutation includes username and password variables.
expect(captured[0].containsKey('username'), true);
expect(captured[0].containsKey('password'), true);
});
test('should login successfully using correct credentials', () async {
final service = AuthService(client: client);
when(
() => client.query<LoginResponseDto>(
query: any(named: 'query'),
variables: any(named: 'variables'),
resultBuilder: any(named: 'resultBuilder'),
),
).thenAnswer(
(_) async => LoginResponseDto.fromJson(
{'success': true, 'token': 'test_eyJhb....sw5c'},
),
);
final response = await service.login(
username: 'john_doe',
password: '12345',
);
expect(response.success, true);
expect(response.token, 'test_eyJhb....sw5c');
});
test('login should fail when incorrect credentials are sent', () async {
final service = AuthService(client: client);
when(
() => client.query<LoginResponseDto>(
query: any(named: 'query'),
variables: any(named: 'variables'),
resultBuilder: any(named: 'resultBuilder'),
),
).thenAnswer(
(_) async => LoginResponseDto.fromJson(
{'success': false, 'token': null},
),
);
final response = await service.login(
username: 'john_doe',
password: 'wrong password',
);
expect(response.success, false);
expect(response.token, null);
});
});
}
Note: I’m using the mocktail library.
However, my concern is that I don’t see value in the second scenario about testing successful and failed login attempts because I’m just forcing a returned object rather than testing the JSON response (which, as far as I know, is a responsibility of the package and not my app). I would see value on this test if this was a dynamically typed language like TypeScript and wanted to ensure that login always returns the correct object. Still, Dart’s type system makes it irrelevant. Is that test relevant, or should I take another approach to it?
In conclusion, I would like to know if the test scenarios I posed are relevant and sufficient and if the test code is correct according to the login use case.