How to test StreamSubscription and create Events in Dart? - dart

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?

Related

Dart create class instance by string with class name

I want to invoke functions of a class by their names inside a string. I know my best option are Mirrors.
var ref = reflect(new TestClass());
ref.invoke(Symbol("test"), []);
It works fine, I can call the function test by a string. But I also want to put "TestClass" inside a string. Is it possible somehow ?
var ref = reflect("TestClass");
ref.invoke(Symbol("test"), []);
Jonas
You can do something like this:
import 'dart:mirrors';
class MyClass {
static void myMethod() {
print('Hello World');
}
}
void main() {
callStaticMethodOnClass('MyClass', 'myMethod'); // Hello World
}
void callStaticMethodOnClass(String className, String methodName) {
final classSymbol = Symbol(className);
final methodSymbol = Symbol(methodName);
(currentMirrorSystem().isolate.rootLibrary.declarations[classSymbol]
as ClassMirror)
.invoke(methodSymbol, <dynamic>[]);
}
Note, that this implementation does require that myMethod is static since we are never creating any object but only operate directly on the class itself. You can create new objects from the class by calling newInstance on the ClassMirror but you will then need to call the constructor.
But I hope this is enough. If not, please ask and I can try add some more examples.

How to get the instance of an injected dependency, by its type using Umbraco.Core.Composing (Umbraco 8)

I need to find a way to get an instance of DataProcessingEngine without calling it's constractor.
I am trying to find a way to do so using the registered DataProcessingEngine in composition object (please see the following code). But I could not find a way to do so.
Anyone have a suggestion? Thanks in advance.
public class Composer : IUserComposer
{
public void Compose(Composition composition)
{
composition.Register<IDataProcessingEngine, DataProcessingEngine>(Lifetime.Singleton);
//DataProcessingEngine dataProcessing = compostion.Resolve<IDataProcessingEngine>()??//no resolve function exists in Umbraco.Core.Composing
SaveImagesThread(dataProcessingEngine);
}
public Task SaveImagesThread(IDataProcessingEngine dataProcessingEngine)//TODO - decide async
{
string dataTimerTime = WebConfig.SaveProductsDataTimer;
double time = GetTimeForTimer(dataTimerTime);
if (time > 0)
{
var aTimer = new System.Timers.Timer(time);
aTimer.Elapsed += new ElapsedEventHandler(dataProcessingEngine.SaveImages);
aTimer.Start();
}
return default;
}
}
For all of you who are looking for a way to call a function (that's defined in another class in your code, an Engine or ...) from the composer(where the app starts) and want to avoid calling this function's class' constractor. I've found another way to do so:
public class QueuePollingHandler
{
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public class SubscribeToQueuePollingHandlerComponentComposer :
ComponentComposer<SubscribeToQueuePollingHandler>
{ }
public class SubscribeToQueuePollingHandler : IComponent
{
private readonly IDataProcessingEngine _dataProcessingEngine;
public SubscribeToQueuePollingHandler(IDataProcessingEngine
dataProcessingEngine)
{
_dataProcessingEngine = dataProcessingEngine;
SaveImagesThread(_dataProcessingEngine);
}
public void SaveImagesThread(IDataProcessingEngine
dataProcessingEngine)
{
....
}
}
And the logic explenation: You create a class (SubscribeToQueuePollingHandlerComponentComposer from the example) and define its base class to be ComponentComposer<Class_that_inherits_IComponent>.
And when you start the application you could see that it gets to the registered class' constractor (SubscribeToQueuePollingHandler constructor).
That's the way that I found to be able to call a function right when the application starts without needing to call its class constractor and actualy use dependency injection.

How can i pass an instance's constructor as a type to a function

I am trying to create a base class for my models but I am struggling with the error The name 'cls' isn't a type so it can't be used as a type argument.. So, how can I pass the object's constructor to the Hive.box method?
import 'package:hive/hive.dart';
class AppModel {
#HiveField(0)
int id;
#HiveField(1)
DateTime createdAt;
#HiveField(2)
DateTime updatedAt;
save() async {
final Type cls = this.runtimeType;
// The name 'cls' isn't a type so it can't be used as a type argument.
final Box box = await Hive.openBox<cls>(cls.toString());
await box.put(this.id, this);
return this;
}
}
#HiveType(typeId: 0)
class UserModel extends AppModel {
#HiveField(3)
String email;
#HiveField(4)
String displayName;
}
void main() {
final UserModel user = UserModel()
..email = 'user#domain.com'
..displayName = 'john doe';
user.save().then(() {
print('saved');
});
}
Dart does not have a way to refer to the dynamic type of this (a "self type").
The way such things are often handled is to have a self-type as type argument, so:
class AppModel<T extends AppModel> {
save() async {
final Box box = await Hive.openBox<T>(T.toString());
await box.put(this.id, this as T);
return this;
}
...
and then ensure that each subclass tells the superclass what type it is:
class UserModel extends AppModel<UserModel> {
...
}
(or, if you expect to subclass UserModel eventually:
class UserModel<T extends UserModel> extends AppModel<T> {
...
}
so that a subclass can still pass its type through).
You are also talking about constructors, and for that there is no easy solution.
Dart's type parameters are types, not classes. You cannot access static members or constructors from a type variable, and there is also no other way to pass a class around.
The only way you can have something call a constructor that it doesn't refer to statically, is to wrap the constructor call in a function and pass that function.
(I can't see how you need the constructor here).

How to access one class method from another class in dart?

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

Dart - how to mock a method that returns a future

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);
});
}

Resources