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!
Related
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>()));
});
});
}
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>())
})
});
}
I'm getting the below error while I'm trying to implement bloc testing in my flutter project
type 'Null' is not a subtype of type 'Future<bool>'
package:mynovatium/features/signup/repositories/signup_repository.dart 10:16 MockRepository.createAccountsignup
Following are the corresponding files that might help identify the cause of the error
signup_bloc_test.dart
class MockRepository extends Mock implements SignUpRepository {}
void main() async {
await configureInjection(inj.Environment.test);
group('SignupBloc', () {
late SignUpBloc signUpBloc;
late SignUpRepository signupRepositoryMock;
setUp(() {
signupRepositoryMock = MockRepository();
signUpBloc = SignUpBloc(signUpRepository: signupRepositoryMock);
});
test('initial state of the bloc is [AuthenticationInitial]', () {
expect(SignUpBloc(signUpRepository: signupRepositoryMock).state,
SignupInitial(),);
});
group('SignUpCreateAccount', () {
blocTest<SignUpBloc, SignUpState>(
'emits [SignUpCreateAccountLoading, SignupInitial] '
'state when successfully Signed up',
setUp: () {
when(signupRepositoryMock.createAccount(
'Nevil',
'abcd',
'nikunj#gmail.com',
'english',
),).thenAnswer((_) async => Future<bool>.value(true));
},
build: () => SignUpBloc(signUpRepository: signupRepositoryMock),
act: (SignUpBloc bloc) => bloc.add(
const SignUpCreateAccount(
'Nevil',
'abcd',
'nikunj#gmail.com',
'english',
),
),
expect: () => [
SignUpCreateAccountLoading(),
SignupInitial(),
],
);
});
});
}
signup_repository.dart
This is the code for the signup repository.
class SignUpRepository {
Future<bool> createAccount(String _firstName, String _lastName, String _eMailAddress, String _language) async {
final Response _response;
try {
_response = await CEApiRequest().post(
Endpoints.createCustomerAPI,
jsonData: <String, dynamic>{
'firstName': _firstName,
'lastName': _lastName,
'email': _eMailAddress,
'language': _language,
'responseUrl': Endpoints.flutterAddress,
},
);
final Map<String, dynamic> _customerMap = jsonDecode(_response.body);
final CustomerModel _clients = CustomerModel.fromJson(_customerMap['data']);
if (_clients.id != null) {
return true;
} else {
return false;
}
} on KBMException catch (e) {
final KBMException _exception = e;
throw _exception;
}
}
}
If anyone has any ideas on what might be the issue here, please help!!
Okay so in the above code you need to stub the methods within the mock repository as well and override it to have it return something incase null is being returned.
class MockRepository extends Mock implements SignUpRepository {
#override
Future<bool> createAccount(String? _firstName, String? _lastName, String? _eMailAddress, String? _language) =>
super.noSuchMethod(Invocation.method(#createAccount, [_firstName, _lastName, _eMailAddress, _language]),
returnValue: Future<bool>.value(false),);
}
Doing something like that done in the above code works well.
I,m unit testing flutter class using mockito. I stubbed one method using 'when()' but it returns null. is there something wrong in my testing?. I have used freezed union to generate Result class. My Repository is an abstract class
this is my test class
class MockRepository extends Mock implements Repository {}
void main() {
late CryptoUseCases useCases;
late MockRepository repository;
final Result<List<Crypto>, Exception> data = const Result.success([
Crypto(
id: 1,
name: 'Bitcoin',
symbol: 'BTC',
priceUsd: 4000,
availableSupply: 879787908,
logo: '',
volumeUsd24h: 786876,
totalSupply: 876587,
marketCapUsd: 698,
maxSupply: 867987,
rank: 1,
),
Crypto(
id: 3,
name: 'Euthereum',
symbol: 'ETH',
priceUsd: 134,
availableSupply: 879787908,
logo: '',
volumeUsd24h: 786876,
totalSupply: 876587,
marketCapUsd: 698,
maxSupply: 867987,
rank: 2,
),
]);
setUp(() {
repository = MockRepository();
useCases = CryptoUseCases(repository);
});
test('getCryptos return a list of crypto', () async {
//arrange
when(repository.getCryptos()).thenAnswer((_) async => data);
//act
final result = await useCases.getCryptos();
//assert
expect(result, equals(data));
verify(repository.getCryptos()).called(1);
verifyNoMoreInteractions(repository);
});
}
this is my class
class CryptoUseCases {
final Repository repository;
CryptoUseCases(this.repository);
Future<Result<List<Crypto>, Exception>> getCryptos() async {
final result = await repository.getCryptos();
result.when(success: (data) {
return Result.success(data);
}, error: (e) {
return Result.error(e);
});
return Result.error(Exception());
}
}
i'm using freezed here. is that a problem?
this is the error
type 'Null' is not a subtype of type 'Future<Result<List<Crypto>, Exception>>'
package:crypto_app/src/domain/repositories/repository.dart 6:43 MockRepository.getCryptos
test\use_cases_test.dart 51:21 main.<fn>
test\use_cases_test.dart 49:46
found the answer after some tries.
I believe the null error is caused due to null safe dart feature.
So to create a mock for null safe dart, we need to anotate #GenerateMocks() above the main method of test.
In my case
#GenerateMocks([Repository])
void main(){}
and use buildrunner to generate code.
after this, this code worked fine
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,
),
);