Flutter testing : when testing server failure returns null - flutter

I'm using flutter test to do unit testing. The package use to mocking is mocktail package (https://pub.dev/packages/mocktail). When I run the test which is to test the server failure scenario, the actual result I get from this is null.
But the expected result is <Instance of 'Failure'>.
How can I fix this issue?
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
class MockHttpService extends Mock implements HttpService {}
class MockAthleteEnv extends Mock implements AthleteEnv {}
class MockSharedPrefsService extends Mock implements SharedPrefsService {}
class MockAthleteContactDetailsReq extends Mock implements AthleteContactDetailsReq{}
void main() {
late ContactDetailsService contactDetailsService;
late MockHttpService mockHttpService;
late MockAthleteEnv mockAthleteEnv;
late MockSharedPrefsService mockStorageService;
late MockAthleteContactDetailsReq mockAthleteContactDetailsReq;
setUp(() {
mockHttpService = MockHttpService();
mockAthleteEnv = MockAthleteEnv();
mockStorageService = MockSharedPrefsService();
mockAthleteContactDetailsReq = MockAthleteContactDetailsReq();
contactDetailsService = ContactDetailsService(
httpService: mockHttpService,
athleteEnv: mockAthleteEnv,
storageService: mockStorageService);
});
group('add athlete contact details', () {
const tAthleteId = 001;
const tAthleteEndpoint = 'https://endpoint/';
const tData = {
'firstName':'John',
'lastName':'Doe',
'contactNumber': '0932445178',
'areaCode':'94',
'email':'johndoe3#mailinator.com',
};
test(
'should return server failure when the call to remote data source is unsuccessful',
() async {
// Arrange
when(() => mockStorageService.getFromDisk(any()))
.thenAnswer((_) => tAthleteId);
when(() => mockAthleteEnv.API_ATHLETE_ENDPOINT)
.thenAnswer((_) => tAthleteEndpoint);
when(() => mockAthleteContactDetailsReq.toJson()).thenAnswer((_) => tData );
when(() => mockHttpService.post(any(), any())).thenThrow(
DioError(
response: Response(
statusCode: 400,
requestOptions: RequestOptions(path: ""),
),
requestOptions: RequestOptions(path: ""),
)
);
// Act
final result = await contactDetailsService.addAthleteContactDetails(mockAthleteContactDetailsReq);
// Assert
expect(result, throwsA(const TypeMatcher<Failure>()));
});
});
}

Related

Expected: <false> Actual: <Closure: () => Future<bool>> in flutter unit testing

I'm writing unit tests to test the authorise function below which is in AuthService class.
Future<bool> authorize() async {
AuthorizationTokenResponse? authResult;
try {
authResult = await appAuth.authorizeAndExchangeCode(
AuthorizationTokenRequest(
bsEnv.AD_CLIENT_ID!,
));
if (authResult != null) {
return true;
}
} on PlatformException catch (e) {
forgetPassword();
}
}
return false;
}
I am using Mocktail to make the mocks. Below is one written test for the authorise function mentioned in the above code.
class MockBEnv extends Mock implements BEnv {}
class MockSharedPrefsService extends Mock implements SharedPrefsService {}
class MockFlutterAppAuth extends Mock implements FlutterAppAuth {}
class MockAuthorizationServiceConfiguration extends Mock
implements AuthorizationServiceConfiguration {}
class MockAuthorizationTokenResponse extends Mock
implements AuthorizationTokenResponse {}
class MockAuthorizationTokenRequest extends Mock
implements AuthorizationTokenRequest {}
class MockTokenResponse extends Mock implements TokenResponse{}
class MockTokenRequest extends Mock implements TokenRequest{
}
void main() {
late AuthService authService;
late MockFlutterAppAuth mockFlutterAppAuth;
late MockBEnv mockBEnv;
late MockSharedPrefsService mockStorageService;
late MockAuthorizationTokenResponse mockAuthorizationTokenResponse;
late MockAuthorizationTokenRequest mockAuthorizationTokenRequest;
const tUrl = 'https://endpoint/';
const tUserImp = 'user';
const tAppLan = 'sv-SE';
const tClientId = 'client';
const tIosUri = 'ios';
const tTokenEndPoint = 'abcdeff';
setUp(() {
mockFlutterAppAuth = MockFlutterAppAuth();
mockBEnv = MockBEnv();
mockStorageService = MockSharedPrefsService();
mockAuthorizationTokenRequest = MockAuthorizationTokenRequest();
mockAuthorizationTokenResponse = MockAuthorizationTokenResponse();
authService = AuthService(
appAuth: mockFlutterAppAuth,
bsEnv: mockBEnv,
sharedPrefs: mockStorageService);
});
group('authorize', () {
test(
'should return true when the call to authorized method if result is not null',
() async {
// Arrange
when(() => mockBEnv.AD_CLIENT_ID).thenAnswer((_) => tClientId);
when(() => mockFlutterAppAuth
.authorizeAndExchangeCode(mockAuthorizationTokenRequest))
.thenAnswer((_)=> Future.value(mockAuthorizationTokenResponse));
// Act
final result = authService.authorize;
// Assert
expect(() => result(), equals(false));
});
});
}
My test fails and below is the error message I get,
Expected: < false >
Actual: <Closure: () => Future>
How can I fix this issue?
authService.authorize is an asynchronous method, returning a Future<bool>.
So your testcase should be
final result = await authService.authorize();
expect(result, equals(false));
The await keyword leads to an async execution that resolves the Future into an actual value

Flutter: How to unit testing moor/drift?

I already implementation of Drift for local storage, and want make it testable function. But I get stack and idk how to fix it the unit test.
HomeDao
#DriftAccessor(tables: [RepositoriesTable])
class HomeDao extends DatabaseAccessor<AppDatabase> with _$HomeDaoMixin {
HomeDao(AppDatabase db) : super(db);
Future<List<RepositoriesTableData>> getRepositories() async =>
await select(repositoriesTable).get();
}
AppDatabase
#DriftDatabase(
tables: [RepositoriesTable],
daos: [HomeDao],
)
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
#override
int get schemaVersion => 1;
}
QueryExecutor _openConnection() {
return SqfliteQueryExecutor.inDatabaseFolder(
path: 'db.sqlite',
logStatements: true,
);
}
LocalDataSources
abstract class GTHomeLocalDataSource {
const GTHomeLocalDataSource();
Future<List<RepositoriesTableData>> getRepositories();
}
class GTHomeLocalDataSourceImpl implements GTHomeLocalDataSource {
final AppDatabase appDatabase;
const GTHomeLocalDataSourceImpl({required this.appDatabase});
#override
Future<List<RepositoriesTableData>> getRepositories() async =>
await appDatabase.homeDao.getRepositories();
}
UnitTesting
void main() => testGTHomeLocalDataSource();
class MockDatabaseHandler extends Mock implements AppDatabase {}
void testGTHomeLocalDataSource() {
late GTHomeLocalDataSource localDataSource;
late AppDatabase databaseHandler;
setUp(() {
databaseHandler = MockDatabaseHandler();
localDataSource = GTHomeLocalDataSourceImpl(
appDatabase: databaseHandler,
);
});
group("GTHomeLocalDataSource -", () {
test(''' \t
GIVEN Nothing
WHEN call getRepositories
THEN databaseHandler select function has been called and return list of RepositoriesTableData
''', () async {
// GIVEN
when(() => databaseHandler.homeDao.getRepositories())
.thenAnswer((_) => Future.value(repositoriesDummyTable));
// WHEN
final result = await localDataSource.getRepositories();
// THEN
verify(() => databaseHandler.homeDao.getRepositories());
expect(result, isA<List<RepositoriesTableData>>());
expect(result.length, repositoriesDummyTable.length);
expect(result.first.language, repositoriesDummyTable.first.language);
});
});
tearDown(() async {
await databaseHandler.close();
});
}
My function is work well for get data from the local db and show it in the app, but when running as unit test, I stacked with this error.
package:gt_core/local/database/database_module.g.dart 424:22 MockDatabaseHandler.homeDao
package:gt_home/data/data_sources/gt_home_local_datasource.dart 20:25 GTHomeLocalDataSourceImpl.getRepositories
test/data/data_sources/gt_home_local_datasource_test.dart 35:44 testGTHomeLocalDataSource.<fn>.<fn>
test/data/data_sources/gt_home_local_datasource_test.dart 29:12 testGTHomeLocalDataSource.<fn>.<fn>
type 'Null' is not a subtype of type 'Future<void>'
package:drift/src/runtime/api/db_base.dart 125:16 MockDatabaseHandler.close
test/data/data_sources/gt_home_local_datasource_test.dart 47:27 testGTHomeLocalDataSource.<fn>
test/data/data_sources/gt_home_local_datasource_test.dart 46:12 testGTHomeLocalDataSource.<fn>
===== asynchronous gap ===========================
dart:async _completeOnAsyncError
test/data/data_sources/gt_home_local_datasource_test.dart testGTHomeLocalDataSource.<fn>
test/data/data_sources/gt_home_local_datasource_test.dart 46:12 testGTHomeLocalDataSource.<fn>
type 'Future<List<RepositoriesTableData>>' is not a subtype of type 'HomeDao'
Anyone know how to fix it?
If you use Mocking to test Drift database, then you'll need to mock the method call as well, otherwiser the method will return null which is the default behavior for Mockito. For example.
// return rowid
when(db.insertItem(any)).thenAnswer((_) => 1);
However it is recommended as per Drift documentation to use in-memory sqlite database which doesn't require real device or simulator for testing.
This issue was also has been discussed here
Using in memory database
import 'package:drift/native.dart';
import 'package:test/test.dart';
import 'package:my_app/src/database.dart';
void main() {
MyDatabase database;
MyRepo repo;
setUp(() {
database = MyDatabase(NativeDatabase.memory());
repo = MyRepo(appDatabase: database);
});
tearDown(() async {
await database.close();
});
group('mytest', () {
test('test create', () async {
await repo.create(MyDateCompanion(title: 'some name'));
final list = await repo.getItemList();
expect(list, isA<MyDataObject>())
})
});
}

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 test with mockito and dartz fails

I'm testing a repository implementation but the test fails and I can't find where the error is.
This is the repository:
class ProductRepositoryImpl implements ProductRepository {
final ProductRemoteDataSource remoteDataSource;
final NetworkInfo networkInfo;
ProductRepositoryImpl(
{#required this.remoteDataSource, #required this.networkInfo});
#override
Future<Either<Failure, List<Product>>> getProducts() async {
this.networkInfo.istConnected;
return Right(await this.remoteDataSource.getProducts());
}
}
This is the test implementation:
class MockRemoteDataSource extends Mock implements ProductRemoteDataSource {}
class MockNetworkInfo extends Mock implements NetworkInfo {}
void main() {
ProductRepositoryImpl repository;
MockRemoteDataSource mockRemoteDataSource;
MockNetworkInfo mockNetworkInfo;
final tProductModel = ProductModel(
id: 1,
title: 'Product 1',
description: 'Product description',
oldPrice: '20.99',
discount: 10,
storeName: 'Zé Strore',
remainingOffers: 3,
imagesUrls: ['https://unsplash.com/photos/EF7BVa9BB2M']);
final productModelList = [tProductModel];
final Product tProduct = tProductModel;
final productList = [tProduct];
setUp(() {
mockRemoteDataSource = MockRemoteDataSource();
mockNetworkInfo = MockNetworkInfo();
repository = ProductRepositoryImpl(
remoteDataSource: mockRemoteDataSource, networkInfo: mockNetworkInfo);
});
group('device is online', () {
setUp(() {
when(mockNetworkInfo.istConnected).thenAnswer((_) async => true);
});
test(
'should return remote data when the call to remote data source is successful',
() async {
// arrange
when(mockRemoteDataSource.getProducts())
.thenAnswer((_) async => productModelList);
// act
final result = await repository.getProducts();
// assert
verify(mockRemoteDataSource.getProducts());
expect(result, equals(Right(productList)));
});
});
}
The error reported is:
Expected: Right<dynamic, List<Product>>:<Right([ProductModel(1, Product 1, Product description, 20.99, 10, Zé Strore, 3, [https://unsplash.com/photos/EF7BVa9BB2M])])>
Actual: Right<Failure, List<Product>>:<Right([ProductModel(1, Product 1, Product description, 20.99, 10, Zé Strore, 3, [https://unsplash.com/photos/EF7BVa9BB2M])])>
package:test_api expect
expect
package:flutter_test/src/widget_tester.dart:441
main.<fn>.<fn>
test/…/repositories/product_repository_impl_test.dart:74
I don't know where is the error. The objects looks like the same. I tried to use expect(result, equals(Right<Failure, List<Product>>(productList)));, but the error persists. The dataSource object has one method and was mocked.
I always did this using the fold method:
final Either<Failure, List<Product>> result = await repository.getProducts();
result.fold((left) => fail('test failed'), (right) {
verify(mockRemoteDataSource.getProducts()).called(1);
expect(right, equals(productList));
});
Don't know if that's the correct way to do it, but for me it worked. Couldn't check it. If it didn't work please let me know!

type 'Null' is not a subtype of type 'Future<Response>' when testing mocked http client with Mocktail

I have written a test for a simple HTTP get using Mocktail to mock the HTTP client. When I call the get method in the test I receive "type 'Null' is not a subtype of type 'Future'".
Anyone any idea why this might be?
Here is the test:
class MockHttpClient extends Mock implements http.Client {}
void main() {
late IdRemoteDataSourceImpl dataSource;
late MockHttpClient mockHttpClient;
setUp(
() {
mockHttpClient = MockHttpClient();
dataSource = IdRemoteDataSourceImpl(client: mockHttpClient);
},
);
group('Get id', () {
test(
'when response code is 200',
() async {
final url = Uri.parse('https://api.test.com/');
const tUsername = 'username';
final accountJson = json.decode(
fixture('account.json'),
// ignore: avoid_as
) as Map<String, dynamic>;
final tIdModel = IdModel.fromJson(accountJson);
// arrange
when(() => mockHttpClient.get(url))
.thenAnswer((_) async => http.Response(
fixture('account.json'),
200,
));
// act
final testResult = await dataSource.getId(tUsername);
// assert
// expect(testResult, tIdModel);
},
);
});
}
The error occurs when the following line runs:
final testResult = await dataSource.getId(tUsername);
Code being tested:
import 'dart:convert';
import 'package:http/http.dart' as http;
class IdModel {
IdModel({required this.id});
final String id;
factory IdModel.fromJson(Map<String, dynamic> json) {
return IdModel(id: json['id'].toString());
}
}
abstract class IdRemoteDataSource {
Future<IdModel> getId(String username);
}
class IdRemoteDataSourceImpl implements IdRemoteDataSource {
IdRemoteDataSourceImpl({required this.client});
final http.Client client;
#override
Future<IdModel> getId(String username) async {
final url = Uri.parse('https://api.test.com/query?username=$username');
final response = await client.get(url);
// ignore: avoid_as
final responseJson = json.decode(response.body) as Map<String, dynamic>;
return IdModel.fromJson(responseJson);
}
}
Error type 'Null' is not a subtype of type 'Future'... occurs when you call method that has not been implemented for mock object or there are different parameters passed to it.
In your code you passed different url parameter to get(...) method. Http client mock waiting for 'https://api.test.com/' but actually 'https://api.test.com/query?username=$username' has been passed.
You have two options to solve it.
Pass the same url to mocked method from when(...) that will be passed during test:
const tUsername = 'username';
final url = Uri.parse('https://api.test.com/query?username=$tUsername');
...
// arrange
when(() => mockHttpClient.get(url))
.thenAnswer((_) async => http.Response(
fixture('account.json'),
200,
),
);
Use any matcher (if you don't care which parameter passed):
registerFallbackValue(Uri.parse(''));
...
when(() => mockHttpClient.get(any()))
.thenAnswer((_) async => http.Response(
fixture('account.json'),
200,
),
);