The case is: i am going to use MyProvider in the following way SomeProvidersClass.get().myProvider; from this structure:
class SomeProvidersClass {
late final myProvider =
Provider((ref) => MyProvider());
MyProvider get myProvider =>
read(providers.myProvider);
}
But
i heed to MyProvider be initialized via some async function;
so i tried to do it in the following way:
class MyProvider {
late final SomeType _someVariable; // need to init in async way
Future<void> init() async => _someVariable = await SomeType.getInstance();
MyProvider();
// example of content of this provider that i am going to use
dynamic? getSmth() => _someVariable.getSmth();
}
and i got the following error
LateInitializationError: field has not been initialized;
Could you suggest me any approach to do it? i am new in riverpod and providers :)
Try this:
class MyProvider {
SomeType? _someVariable; // need to init in async way
Future<void> init() async => _someVariable = await SomeType.getInstance();
MyProvider(){
init();
}
// example of content of this provider that i am going to use
dynamic? getSmth() => _someVariable?.getSmth();
}
Related
The following code is a simplified example from my code. I have class A which is dependent on class B. I want to test class A, so I mock class B. Then I'm writing a test for a method of class A and inside of that test I write a stub for whenever a method from my mocked class B is called:
fetchData() async {
try {
await b.getData();
} on DioError catch (e) {
switch (e.response!.statusCode) {
case 401:
logout();
throw UnauthorizedException();
default:
throw UnspecifiedDioException(error: e);
}
}
Test written for fetchData() method:
test('check if fetchData calls logout when 401 is returned', () {
when(mockB.getData())
.thenAnswer((_) async =>
throw DioError(
requestOptions: RequestOptions(path: ""),
response: Response(requestOptions: RequestOptions(path: ""), statusCode: 401)));
verify(a.logout()); // doesn't work because A isn't mocked
});
I've read that you can do this very easily with spies but to my surprise spies are available for every language which uses mockito except for dart. It's apparently deprecated but then again how can something be deprecated if there isn't even a newer version to replace it with.
I'd really appreciate it if someone could tell me if there is a convenient workaround for what I'm trying to achieve. Thanks in advance.
Edit: I've changed the question because the former one wasn't making much sense. I just wanna know if there is something like spies in dart or not.
Using mocktail..
You should stub your logout invocation's dependency as well.
class A {
A({required this.api, required this.auth});
// to be mocked
final Api api;
final Auth auth;
Future<void> fetchData() async {
try {
await api.getData();
} catch (e) {
auth.logout();
}
}
}
class Auth {
Future<void> logout() => Future(() {});
}
class Api {
Future<void> getData() => Future(() {});
}
And your test
class MockApi extends Mock implements Api {}
class MockAuth extends Mock implements Auth {}
void main() {
// create mock objects
final mockApi = MockApi();
final mockAuth = MockAuth();
test('when [Api.getData] throws, [Auth.logout] is called', () async {
// create an instance of "A" and use your mock objects
final a = A(api: mockApi, auth: mockAuth);
// use "thenThrow" to throw
when(() => mockApi.getData()).thenThrow('anything');
// use "thenAnswer" for future-returning methods
when(() => mockAuth.logout()).thenAnswer((_) => Future.value(null));
// call the method to "start" the test
await a.fetchData();
// verify logout was called
verify(mockAuth.logout).called(1); // passes
});
}
I'm new to dart. Currently, working on a mobile app through flutter. I have a Helper class which has some common methods which I've planned throughout the app. I've included that Helper class in another class. But, can't able to fig. out how to access its methods.
My commom Helper class code:
import 'dart:async';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
class Helper {
Map userDetails = {};
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
// --- Method for getting user details from shared preference ---
Future<Map>getUserDetailsFromSharedPreference () async {
try {
final SharedPreferences prefs = await _prefs;
if(prefs.getString('user') != null) {
this.userDetails = json.decode(prefs.getString('user'));
} else {
print('Shared preference has no data');
}
} catch (e){
print('Exception caught at getUserDetails method');
print(e.toString());
}
return this.userDetails;
}
}
Here is my main program code where I've included the Helper class & trying to access it's getUserDetailsFromSharedPreference (). In this case, I'm getting an error like Only static memebers can be accessed in initializers. I also tried to extends Helper class in UserProfile class. But, there also I'm getting a different kind of errors. Can't able to identify how to do this thing.
import 'package:flutter/material.dart';
import 'helper.dart';
class UserProfile extends StatefulWidget {
#override
UserProfileState createState() => new UserProfileState();
}
class UserProfileState extends State<UserProfile> {
Helper helper = new Helper();
var userData = helper.getUserDetailsFromSharedPreference();
}
#Günter Zöchbauer I've made my Helper.dart file like this as you've suggested -
import 'dart:async';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
class Helper {
Map userDetails = {};
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
static Helper _instance;
factory Helper() => _instance ??= new Helper._();
Helper._();
// --- Method for getting user details from shared preference ---
Future<Map>getUserDetailsFromSharedPreference () async {
try {
final SharedPreferences prefs = await _prefs;
if(prefs.getString('user') != null) {
this.userDetails = json.decode(prefs.getString('user'));
} else {
print('Shared preference has no data');
}
} catch (e){
print('Exception caught at getUserDetails method');
print(e.toString());
}
return this.userDetails;
}
}
Now, in my tryint to access that getUserDetailsFromSharedPreference() method I'm getting the same error Only static memebers can be accessed in initializers .
You could ensure a singleton instance of the class using a public factory constructor with a private regular constructor:
class Helper {
static Helper _instance;
factory Helper() => _instance ??= new Helper._();
Helper._();
...
}
If you call new Helper(), you'll always get the same instance.
You need to import the file that contains class Helper {} everywhere where you want to use it.
??= means new Helper._() is only executed when _instance is null and if it is executed the result will be assigned to _instance before it is returned to the caller.
update
getUserDetailsFromSharedPreference is async and can therefore not be used in the way you use it, at least it will not lead to the expected result. getUserDetailsFromSharedPreference returns a Future that provides the result when the Future completes.
class UserProfileState extends State<UserProfile> {
Helper helper = new Helper();
Future<Map> _userData; // this with ??= of the next line is to prevent `getUserDetailsFromSharedPreference` to be called more than once
Future<Map> get userData => _userData ??= helper.getUserDetailsFromSharedPreference();
}
If you need to access userData you need to mark the method where you do with async and use await to get the result.
foo() async {
var ud = await userData;
print(ud);
}
To access other class method you can simply put static on the method.
class Helper {
static printing(String someText){
print(someText);
}
}
void main() {
Helper.printing('Hello World!');
}
I think this question is more related to accessing one class data in another class. So I explained on the basis of my understanding of the question but if I'm not correct about it.
but if you want to access data of class A, not directly but through class B.
so first you have to make an object of A in class B but remember one thing you would have to make the object static in order to get access to the data of class A within Class B
If you still are confused about all this, I made a solution video.
Check it out: https://youtu.be/shK7ZraruCI
I want to write some unit tests around an abstract Uploader class that I have written like so:
abstract class Uploader {
Future<StreamSubscription> subscribe(String filename, void onEvent(Event event));
}
class FirebaseUploader implements Uploader {
Future<StreamSubscription> subscribe(String filename, void onEvent(Event event)) async {
String userId = await auth.signInAnonymously();
DatabaseReference databaseReference = _databaseReference(userId, filename);
return databaseReference.onValue.listen(onEvent);
}
}
class UploaderMock implements Uploader {
Future<StreamSubscription> subscribe(String filename, void onEvent(Event event)) async {
Event event = new Event(); // The class 'Event' doesn't have a default constructor.
return Future.value(null);
}
}
The trouble is, I can't work out how to create my own Events in my UploaderMock, so I can call onEvent. If I try to create a new Event(), I get the following error:
The class 'Event' doesn't have a default constructor.
This is because Event has a private constructor:
Event._(this._data) : snapshot = new DataSnapshot._(_data['snapshot']);
This makes sense for production, but it doesn't really work for testing.
Any ideas? How can I test code that uses StreamSubscription?
You can implements Event on a custom class.
class Bar {
Bar._() {}
}
class Foo implements Bar {
Foo();
}
You can't, but you can make them public and annotate it with
#visibleForTesting to get an DartAnalyzer warning when they are
accessed from code that is not in in the same library or in test/
answered here How to test private functions/methods in Flutter?
I have a class that defines a method that returns a Future. The Future contains a list of class that also return a future.
class User{
Future<List<Album>> albums(){
};
}
class Album{
Future<List<Photos>> photos(){
}
};
What is the best way to mock the method in these classes when testing another class?
The class I am trying to test looks a bit like
class Presenter {
Presenter( User user){
user.albums().then( _processAlbums);
}
_processAlbums(List<Album> albums) {
albums.forEach( (album)=>album.photos.then( _processPhotos));
}
_processPhotos(List<Photo> photos) {
....stuff
}
}
I tried writing a unit test like this
class MockUser extends Mock implements User{}
class MockAlbum extends Mock implements Album{}
class MockPhoto extends Mock implements Photo{}
class MockFutureList<T> extends Mock implements Future<T>{
MockFutureList( List<T> items){
when( callsTo( "then")).thenReturn( items);
}
}
void main(){
test("constuctor should request the albums from the user ",(){
MockUser user = new MockUser();
MockAlbum album = new MockAlbum();
List<Album> listOfAlbums = [ album];
MockPhoto photo = new MockPhoto();
List<Album> listOfPhotos = [ album];
user.when( callsTo( "albums")).thenReturn( new MockFutureList(listOfAlbums));
album.when( callsTo( "photos")).thenReturn( new MockFutureList( listOfPhotos));
PicasaPhotoPresentor underTest = new PicasaPhotoPresentor( view, user);
user.getLogs( callsTo( "albums")).verify( happenedOnce);
album.getLogs( callsTo( "photos")).verify( happenedOnce);
});
}
This allowed me to test that the constructor called the user.photos() method, but not that the album.photos() method was called.
I am not sure that mocking a Future is a good idea - Would it not be better to create a 'real' Future that contains a list of Mocks?
Any ideas would be very helpful!
Since you're only interested in verifying that methods in User and Album are called, you won't need to mock the Future.
Verifying the mocks gets a bit tricky here, because you're chaining futures inside the constructor. With a little understanding of how the event loop works in Dart, I recommend using a future and calling expectAsync after you create your presenter.
The expectAsync function tells the unit test library to wait until it's called to verify your tests. Otherwise the test will complete successfully without running your expectations.
With this, here's what your test should would look like:
import 'package:unittest/unittest.dart';
class MockUser extends Mock implements User {}
class MockAlbum extends Mock implements Album {}
void main() {
test("constuctor should request the albums from the user ", () {
var user = new MockUser();
var album = new MockAlbum();
user.when(callsTo("albums")).thenReturn(new Future(() => [album]));
var presenter = new PicasaPhotoPresentor(view, user);
// Verify the mocks on the next event loop.
new Future(expectAsync(() {
album.getLogs(callsTo("photos")).verify(happendOnce);
}));
});
}
Here is how I managed to do it
1) Define FutureCallbackMock
class FutureCallbackMock extends Mock implements Function {
Future<void> call();
}
2) get function from a mock and set it up
FutureCallback onPressed = FutureCallbackMock().call;
completer = Completer<void>();
future = completer.future;
when(onPressed()).thenAnswer((_) => future);
3) Verify like so
verify(onPressed()).called(1);
4) Complete the future if needed:
completer.complete();
NOTE: in flutter tests I had to wrap my test in tester.runAsync like so
testWidgets(
'when tapped disables underlying button until future completes',
(WidgetTester tester) async {
await tester.runAsync(() async {
// test here
});
});
I was able to do this with Mocktail. This is the article that this is from, and explains how to integrate it into your app. This is a full widget test and depends on this gist code.
The crux is that you need to declare a Mock class that has a call method. Then, you can then mock the top-level function that returns a Future. You can use the when and verify methods with this.
//Gist code
import 'package:gist/main.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter/material.dart';
class LaunchMock extends Mock {
Future<bool> call(
Uri url, {
LaunchMode? mode,
WebViewConfiguration? webViewConfiguration,
String? webOnlyWindowName,
});
}
void main() {
testWidgets('Test Url Launch', (tester) async {
//These allow default values
registerFallbackValue(LaunchMode.platformDefault);
registerFallbackValue(const WebViewConfiguration());
//Create the mock
final mock = LaunchMock();
when(() => mock(
flutterDevUri,
mode: any(named: 'mode'),
webViewConfiguration: any(named: 'webViewConfiguration'),
webOnlyWindowName: any(named: 'webOnlyWindowName'),
)).thenAnswer((_) async => true);
final builder = compose()
//Replace the launch function with a mock
..addSingletonService<LaunchUrl>(mock);
await tester.pumpWidget(
builder.toContainer()<MyApp>(),
);
//Tap the icon
await tester.tap(
find.byIcon(Icons.favorite),
);
await tester.pumpAndSettle();
verify(() => mock(flutterDevUri)).called(1);
});
}
I need to "fetch" the methods in a Dart Class.
How can I do this?
And I want to be able to call the methods.
May I see an example?
Here's an easy copy-pasteable code sample:
import 'dart:mirrors';
import 'dart:io';
main() {
var im = reflect(new File('test')); // Retrieve the InstanceMirror of some class instance.
im.type.methods.values.forEach((MethodMirror method) => print(method.simpleName));
}
Output is:
existsSync
_delete
exists
directory
_getDecodedLines
readAsTextSync
readAsBytesSync
readAsLinesSync
_directory
throwIfError
lastModifiedSync
readAsLines
open
_ensureFileService
deleteSync
delete
_exists
length
openInputStream
create
_create
readAsText
_openStdioSync
openOutputStream
_fullPath
_lastModified
fullPathSync
readAsBytes
lastModified
_openStdio
_open
openSync
lengthSync
directorySync
fullPath
createSync
_lengthFromName
Here is a basic example:
(Note: You will want to have a (very) up to date version of the SDK for this, this was done in Dart Editor version 0.2.1_r14167, Dart SDK version 0.2.1.2_r14167 Nov 2, 2012)
My most sincere Thanks to Gilad of the Google Dart Team for providing this example!
#import('dart:mirrors');
class MyClass {
String _test;
String get test => _test;
set test(String paramVal) => _test = paramVal;
void my_method() {
}
void print_test(){
print("test string is: ${_test}");
}
MyClass(String test) {
_test = test;
}
}
main() {
MyClass myClass = new MyClass("Make my day, PUNK.");
myClass.print_test();
//ClassMirror myClassMirror = reflect(myClass).type;
InstanceMirror myClassInstanceMirror = reflect(myClass);
ClassMirror MyClassMirror = myClassInstanceMirror.type;
Map<String, MethodMirror> map = MyClassMirror.methods;
print("map = ${map}");
map.values.forEach( (MethodMirror mm){
myClassInstanceMirror.invoke(mm.simpleName,[]);
});
}
Concerning Reflection I have just written a couple of "Helper Functions" for fetching a LIST of the method names (not a Map) and invoking the method... all I can say for sure is that it works right now. There are likely technical reasons for not doing it this way - but in my case this does not run in a complex environment. However, I do think they nicely mask over a lot of details that not everybody is going to want to deal with.
Here's a functioning demonstration with and without the Helper functions:
#import('dart:mirrors');
class MyClass {
String _test;
String get test => _test;
set test(String paramVal) => _test = paramVal;
void my_method1(){print("in method1");}
void my_method2(){print("in method2");}
void print_test(){
print("test string is: ${_test}");
}
MyClass(String test) {
_test = test;
}
}
//Helper Methods
InstanceMirror hMr;
List REFLECT_methods(Object myClass) {hMr=reflect(myClass);return(hMr.type.methods.values);}
REFLECT_invoke(MethodMirror mm){hMr.invoke(mm.simpleName, []);}
main() {
MyClass myClass = new MyClass("Make my day, PUNK.");
print("\n=======Using Raw Reflection================");
InstanceMirror myClassInstanceMirror = reflect(myClass);
ClassMirror MyClassMirror = myClassInstanceMirror.type;
Map<String, MethodMirror> map1 = MyClassMirror.methods;
map1.values.forEach( (MethodMirror mm){
myClassInstanceMirror.invoke(mm.simpleName,[]);
});
print("\n==========Using Helper functions=============");
List list2 = REFLECT_methods(myClass);
list2.forEach((method){
REFLECT_invoke(method);
});
}