Error message when doing unit test in flutter - flutter

I am a beginer in flutter and I have to write some unit test for a project but I get an error and can't find the solution to solve it.
Here is the function that I want to test:
addUser(name, password) async{
if (name.toString().isEmpty || password.toString().isEmpty) {
return "Error";
}
return await dio.post('https://test.test.com/adduser',
data: {"name": name, "password": password},
options: Options(contentType: Headers.formUrlEncodedContentType));
}
Here is the test code:
void main() {
test('Empty field', () {
var auth = AuthService();
String result = auth.addUser("", "");
expect(result, "Error");
});
}
And I get this error:
type 'Future<dynamic>' is not a subtype of type 'String'
test\widget_test.dart 17:18 main.<fn>
Can someone help to find where I made a mistake please ? Thanks for yours answers.

There is a missing await in the auth.addUser call and also a missing async in the test function.
It should look like this:
void main() {
text('Emtpy field', () async { // <- Here
var auth = AuthService();
String result = await auth.addUser("", ""); // <- Here
expect(result, "Error");
});
}

Related

Unhandled Exception: type 'Null' is not a subtype of type 'ApiResponse<Map<String, dynamic>>'

I'm getting the following error while fetching data from server but I know this error is not about how I'm fetching data. It is related to the null safety which was added in latest flutter update. I'm not so much familier with it.
Here is the Errors
enter image description here
I tried to check if there was something wrong on the line highlighted and for me it was ok.
Here is the code of servers_http.dart:
ApiResponse<Map<String, dynamic>> resp = await get<Map<String, dynamic>>("detail/random");
if (resp.success ?? false) {
return StarConfig.fromJson(resp.data!);
}
return null;
}```
**and this stars_provider.dart**
```///Initialize engine and load last server
void initialize(BuildContext context) {
engine = OpenStar(onStarStageChanged: onStarStageChanged, onStarStatusChanged: onStarStatusChanged)
..initialize(
lastStatus: onStarStatusChanged,
lastStage: (stage) => onStarStageChanged(stage, stage.name),
groupIdentifier: groupIdentifier,
localizedDescription: localizationDescription,
providerBundleIdentifier: providerBundleIdentifier,
);
Preferences.instance().then((value) async {
starConfig = value.getServer() ?? await ServersHttp(context).random();
notifyListeners();
});
}```
what I need to change?
Slution 1:
my understanding that you are requesting data from the server using
ApiResponse<Map<String, dynamic>> resp = await get<Map<String, dynamic>>("detail/random");
if so all you need to do is to change it to :
ApiResponse<Map<String, dynamic>>? resp = await get<Map<String, dynamic>>("detail/random");
if (resp?.success ?? false) {
return StarConfig.fromJson(resp.data!);
}
Slution 2:
i believe this function is the problem and starConfig doesn't accept null and base on your code :
Preferences.instance().then((value) async {
starConfig = value.getServer() ?? await ServersHttp(context).random();
notifyListeners();
});
this part is returning null when his only job is to return ApiResponse<Map<String, dynamic>>
await ServersHttp(context).random()
now this i believe this will work if you
Preferences.instance().then((value) async {
ApiResponse<Map<String, dynamic>> test;
starConfig = value.getServer() ?? test.fromJson({});
notifyListeners();
});
Note : i don't know what ServersHttp(context).random() do.

Flutter unit test: type 'Null' is not a subtype of type 'T' in type cast

I tried to run unit test but i found an issue like below:
type 'Null' is not a subtype of type 'LoginResponse' in type cast
dart:async new Future.value
test/mock_utils.mocks.dart 259:22 MockBaseRepository.call
package:swap_recycling/features/auth/data/repository/login/login_repository_impl.dart 33:42 LoginRepositoryImpl.login
test/features/auth/data/repository/login/login_repository_impl_test.dart 30:38
and these my codes:
test.dart
void main() {
late MockLoginApiService _mockLoginApiService;
late MockSecureStorage _mockSecureStorage;
late MockBaseRepository _mockBaseRepository;
setUp(() {
_mockLoginApiService = MockLoginApiService();
_mockSecureStorage = MockSecureStorage();
_mockBaseRepository = MockBaseRepository();
});
test('Given LoginRepository When login Then return loginResponseSuccess',
() async {
final _repository = LoginRepositoryImpl(
_mockLoginApiService,
_mockSecureStorage,
_mockBaseRepository,
);
final result = await _repository.login(loginRequest);
expect(result, loginResponseSuccess);
verify(_mockLoginApiService.login(loginRequest)).called(1);
});
Login_repository.dart
final loginRepositoryProvider = Provider<LoginRepository>((ref) {
final loginApiService = ref.watch(provideLoginApiService);
final secureStorage = ref.watch(secureStorageProvider);
final baseRepository = ref.watch(baseRepositoryProvider);
return LoginRepositoryImpl(loginApiService, secureStorage, baseRepository);
});
class LoginRepositoryImpl extends LoginRepository {
final LoginApiService _loginApiService;
final SecureStorage _secureStorage;
final BaseRepository _baseRepository;
LoginRepositoryImpl(
this._loginApiService,
this._secureStorage,
this._baseRepository,
);
#override
Future<LoginResponse> login(LoginRequest request) async {
final result = await _baseRepository.call<LoginResponse>(
() => _loginApiService.login(request),
);
return result;
}
base_repository.dart
final baseRepositoryProvider = Provider<BaseRepository>((ref) {
return BaseRepository();
});
class BaseRepository {
Future<T> call<T>(FutureOr<T> Function() call) async {
try {
return await call();
} on DioError catch (e) {
if (e.error is SocketException) {
throw Failure(message: e.message);
}
if (e.response?.statusCode == serverErrorCode) {
throw Failure(
message: 'Server has some issue, please try again',
code: e.response?.statusCode,
);
}
throw Failure(
message: e.response?.statusMessage ?? 'Something went wrong',
code: e.response?.statusCode,
exception: e,
);
}
}
}
I tried to create a reuse function to return HTTP calls or handle errors.
I set generic type and send LoginResponse to tell BaseRepository that I want to return LoginResponse.
I can run it on an emulator but it doesn't work in a unit test.
Why It returns Null type?
I used mockito to generate mock class
mocks.dart
#GenerateNiceMocks([
//Repository
MockSpec<LoginRepository>(),
MockSpec<SettingRepository>(),
//Service
MockSpec<LoginApiService>(),
MockSpec<SecureStorage>(),
//Base
MockSpec<BaseRepository>(),
MockSpec<LoginRepositoryImpl>(),
])

Return type 'Null' is not a subtype of type 'Future<void>' in mocktail

I am trying to test cash articles with HIVE package put it throws a type 'Null' is not a subtype >of type 'Future
I stubed it but I don't know why
test:
group("cache last gotten articles", () {
test('should cache the last gotten articles', () async {
// arrange
final expectedJsonArticles = jsonEncode(fixture('cached_articles'));
when(() => mockHive.openBox(articles))
.thenAnswer((_) async => mockHiveBox);
when(() => mockHiveBox.put(articles, expectedJsonArticles)).thenAnswer((_) async =>true);
print(mockHiveBox.put(articles, expectedJsonArticles).runtimeType) ;
final x = mockHiveBox.put(articles, expectedJsonArticles);
// act
await articleLocaleDataSourceImpl.cacheArticleLocale(tArticlesList);
// assert
verify(()=>x).called(1);
verify(() => mockHive.openBox(articles)).called(1);
});
});
function:
Future<void> cacheArticleLocale(List<ArticleEntity> articles) async {
final box = await hive.openBox(LocaleDbKeys.articleBox);
final Map<String, dynamic> parsedArticles = {};
parsedArticles['articles'] =
articles.map((article) => (article as ArticleModel).toJson()).toList();
box.put(
LocaleDbKeys.articleBox, jsonEncode(parsedArticles['articles']));
}
I solve it...
the problem was with the data I put on put expectedJsonArticles on the test file and the data on the production file
mockHiveBox.put(articles, expectedJsonArticles)
box.put(LocaleDbKeys.articleBox, jsonEncode(parsedArticles['articles']));
is not the same
but the error message tells me that I didn't stub this!
in case you faced this error this will help you
another case
if you didn't stub a function it will return a null value instead
please check this:
https://pub.dev/packages/mocktail#:~:text=type%20%27Null%27%20is%20not%20a%20subtype%20of%20type%20%27Future%3Cvoid%3E%27

Flutter Test Error - type 'Null' is not a subtype of type 'Future<Response>'

I am trying to create a simple test but I keep getting this error.
type 'Null' is not a subtype of type 'Future'
test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:async/async.dart';
import 'package:http/http.dart' as http;
import 'package:mocktail/mocktail.dart';
class MockClient extends Mock implements http.Client {}
void main() {
group('signin', () {
final client = MockClient();
final api = AuthApi('https://baseUrl', client);
final credential = Credential(
email: 'test#test.com',
type: AuthType.email,
password: 'pass',
);
test('should return error when status code is not 200', () async {
registerFallbackValue(Uri.parse(''));
when(() => client.post(any(), body: {}))
.thenAnswer((_) async => http.Response('{}', 404));
final result = await api.signIn(credential);
expect(result, isA<ErrorResult>());
});
});
}
Error is at line
final result = await api.signIn(credential); expect(result,
isA());
If I remove those lines I don't see the error.
auth_api.dart
class AuthApi implements IAuthApi {
AuthApi(this.baseUrl, this._client);
final http.Client _client;
String baseUrl;
#override
Future<Result<String>> signIn(Credential credential) async {
final endpoint = Uri.parse(baseUrl + '/auth/signin');
return await _postCredential(endpoint, credential);
}
#override
Future<Result<String>> signUp(Credential credential) async {
final endpoint = Uri.parse(baseUrl + '/auth/signup');
return await _postCredential(endpoint, credential);
}
Future<Result<String>> _postCredential(
Uri endpoint,
Credential credential,
) async {
final response =
await _client.post(endpoint, body: Mapper.toJson(credential));
if (response.statusCode != 200) {
return Result.error('Server Error');
}
var json = jsonDecode(response.body);
return json['auth_token'] != null
? Result.value(json['auth_token'])
: Result.error(json['message']);
}
}
I checked other similar question answers also but none of them worked. I am using mocktail package & http for post.
The problem is in that line:
when(() => client.post(any(), body: {}))
.thenAnswer((_) async => http.Response('{}', 404));
It means that when there's a client.post() method invoked with any() URL and a specific empty body {}, then it should return a mocked response.
What you want is to return a mocked response when there's any URL and any body, so it should be like this:
when(() => client.post(any(), body: any(named: 'body')))
.thenAnswer((_) async => http.Response('{}', 404));
However, if you want to test if a specific error is thrown, that code should be modified:
test('should return error when status code is not 200', () async {
when(() => client.post(any(), body: any(named: 'body')))
.thenThrow(ErrorResult(Exception()));
expect(() async => await api.signIn(credential),
throwsA(isA<ErrorResult>()));
});
First, you specify that calling API should throw an error (when(...).thenThrow(...)) and then you check if an error was thrown (expect(..., throwsA(...)))

Flutter mockito post always return null

I'm trying to test my code that makes a post to login from an API using a mocked http client, but instead of returning what I asked for, it returns null, I did the same test but changing the endpoint and method to GET and it worked perfectly. I'm currently using flutter's http to make the requests, but I've already tested it with Dio and the result was the same, below is my code
Future<String> signIn(String email, String password) async {
final Map<String, dynamic> body = {"email": email, "password": password};
final String url = url_base + Urls.auth_login;
final Map<String, String> customHeader = {
"Content-type": "application/json",
};
String returnCode;
try {
var x = jsonEncode(body);
http.Response response = await client.post(Uri.parse(url), body: x, headers: customHeader);
var parsedJson = json.decode(response.data);
if (parsedJson.containsKey("token")) {
returnCode = parsedJson["token"];
} else {
returnCode = parsedJson["non_field_errors"][0];
}
}catch (e) {
throw ServerException();
}
if (returnCode == null) {
throw ServerException();
} else {
return returnCode;
}
}
and the test case:
class ClientMock extends Mock implements http.Client {}
void main() {
RemoteData remoteData;
group('Test signIn', () {
test('Login with email and wrong password', () async {
final clientMock = ClientMock();
remoteData = RemoteData(client: clientMock);
String jsonMockResponse =
'{non_field_errors: [Unable to log in with provided credentials.]}';
when(clientMock.post(any))
.thenAnswer((_) async => http.Response(jsonMockResponse, 400));
String loginReturn =
await remoteData.signIn('test#email.com', 'password123');
expect(loginReturn,throwsA(const TypeMatcher<ServerException>()));
});
}
I've already tested some things like changing 'any' for exactly the same thing the real function gets and it didn't work either.
The actual test return 'Instance of 'ServerException'', an in debug mode i could see that the return is null, and the last if is the one who throws this exception.