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
Related
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 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!
I have generated mock class for my store and repository class using #GenerateMocks as below::
#GenerateMocks([],customMocks: [
MockSpec<MyStore>(as: #MockMyStore, returnNullOnMissingStub: true),
MockSpec<MyRepository>(as: #MockMyRepository, returnNullOnMissingStub: true),
])
this has automatically created a test.mocks file for both store and repository class but while writing unit testing I am getting error: 'Null' is not a subtype of type 'Future' in type cast.
The test cases I have written as below:
test('call api successfully', () async {
when(mockStore.calApi()).thenAnswer((realInvocation) => Future.value());
expect(await store.calApi(), true);
});
//MyStore class
class MyStore = _MyStore with _$MyStore;
abstract class _MyStore with Store {
late MyRepository _repository;
_MyStore(MyStore repository)
: this._repository = repository;
Future<bool> callApi() async {
final isInternet = await Util.checkInternetAndShowDialog(context);
if (isInternet) {
final future = _repository.callApi();
var res = await future;
return true;
}
return false;
}
}
Can any one help me to resolve this issue, why I am receiving 'Null' subtype error and pass the test cases in flutter. I shall be thankful for this.
I want to test my GraphQL Query. I have my GraphQL client, and I use a remote datasource to do my requests.
class MockGraphQLClient extends Mock implements GraphQLClient {}
void main() {
RemoteDataSource RemoteDataSource;
MockGraphQLClient mockClient;
setUp(() {
mockClient = MockGraphQLClient();
RemoteDataSource = RemoteDataSource(client: mockClient);
});
group('RemoteDataSource', () {
group('getDetails', () {
test(
'should preform a query with get details with id variable',
() async {
final id = "id";
when(
mockClient.query(
QueryOptions(
documentNode: gql(Queries.getDetailsQuery),
variables: {
'id': id,
},
),
),
).thenAnswer((_) async => QueryResult(
data: json.decode(fixture('details.json'))['data'])));
await RemoteDataSource.getDetailsQuery(id);
verify(mockClient.query(
QueryOptions(
documentNode: gql(Queries.getDetailsQuery),
variables: {
'id': id,
},
),
));
});
});
});
}
I would like to know how to mock the response of my query. Currently it does not return a result, it returns null
But I don't understand why my query returns null, although I have mocked my client, and in my "when" method I use a "thenAnwser" to return the desired value
final GraphQLClient client;
ChatroomRemoteDataSource({this.client});
#override
Future<Model> getDetails(String id) async {
try {
final result = await client.query(QueryOptions(
documentNode: gql(Queries.getDetailsQuery),
variables: {
'id': id,
},
)); // return => null ????
if (result.data == null) {
return [];
}
return result.data['details']
} on Exception catch (exception) {
throw ServerException();
}
}
The argument on which when should mock an answer for is quite complex. You might be easier to just use any in your test case.
when(mockClient.query(any)).thenAnswer((_) async => QueryResult(
data: json.decode(fixture('details.json'))['data'])));
any is provided by Mockito to match any argument.
In the
graphql_flutter: ^5.0.0
you need the add source as null or QueryResultSource.network, when call method when can you pass any so you don't need to pass QueryOptions( documentNode: gql(Queries.getDetailsQuery), variables: { 'id': id, }, ),
here is final code:
when(mockClient.query(any)).thenAnswer((_) async => QueryResult( data: json.decode(fixture('details.json'))['data'], ,source: null)));
any is not accepted with graphQLClient.query(any)) as it accepts non nullable QueryOptions<dynamic>
Using mockito: ^5.1.0 , you will get the warning: The argument type 'Null' can't be assigned to the parameter type 'QueryOptions<dynamic>'
I solved it by creating the mocked QueryOptions as:
class SutQueryOption extends Mock implements QueryOptions {}
void main() {
SutQueryOption _mockedQueryOption;
....
setUp(() {
SutQueryOption _mockedQueryOption = MockedQueryOptions();
....
});
when(mockClient.query(_mockedQueryOption)).thenAnswer((_) async => ....
can You please help me to understand how to test the BehaviorSubject with my custom class(TestClass).
the test returns Instance of 'BehaviorSubject<TestClass>' instead of Instance of 'TestClass.
but with primitive types it work fine
Full error:
ERROR: Expected: should emit an event that <Instance of 'TestClass'>
Actual: <Instance of 'BehaviorSubject<TestClass>'>
Which: emitted • Instance of 'TestClass'
bloc example:
class TestDataBloc {
final testSubject = BehaviorSubject<TestClass>();
Observable<TestClass> get paymentStream => testSubject.stream;
createOrder() {
final TestClass _testData = TestClass(
id: 100,
data: "xxx",
);
testSubject.sink.add(_testData);
}
dispose() async {
await testSubject.drain();
testSubject.close();
}
}
test:
test('_testData', () async {
TestDataBloc _testDataBloc = TestDataBloc();
final TestClass _testData = TestClass(
id: 100,
data: "xxx",
);
expect(
_testDataBloc.testSubject.stream,
emitsInOrder(
<TestClass>[
_testData,
],
),
);
_testDataBloc.createOrder();
});
Thanks
Make sure you have an equality operator defined for TestClass