I have this action
Future<void> signUpAction(Store<AppState> store) async {
try {
// ...
} catch (e) {
// ..
}
}
And I dispatch it like this
store.dispatch(signUpAction);
Now, if I want to pass two paramters, how would I do that? Since there is already one parameter there.
I tried this
Future<void> signUpAction(Store<AppState> store, email, password) async {
try {
// ...
} catch (e) {
// ..
}
}
but then on dispatching, if I do
store.dispatch(signUpAction("some#email.com", "somEPa55word!"));
it says the singUpAction expects 3 parameters, so I don't know very well how to pass only these two
Thank you
The dispatch method expects a specific signature. If your method does not have exactly that signature, you can make an anonymous function on the fly that matches the signature.
In this case, since your method takes not only the store, but also the email and password:
store.dispatch((x) => signUpAction(x, "some#email.com", "somEPa55word!"));
Related
How to test Function passed as an argument in Flutter?
code:
Future<User> execute({
required String username,
required String password,
required void Function(AuthFailure fail) onFailure,
required void Function(User user) onSuccess,
}) async {
if (username.isNonValid || password.isNonValid) {
onFailure(const AuthFailure.wrongCredentials()); // I want to test this line
return const User.anonymous();
}
...
}
test:
test('use case - failure execution for incorrect credentials', () async {
// GIVEN
// WHEN
final user = await useCase.execute(
username: "noname",
password: "password",
onFailure: (fail) {},
onSuccess: (user) {},
);
// THEN
// TODO how to verify onFailure call inside useCase?
expect(user, const User.anonymous());
});
Or maybe testing this way is not the idiomatic way, because the test becomes more white-box instead black-box? Should I perceive passing functions as arguments to use cases as anti-pattern? I can change it then. The proposition is to return sth like Either from useCase.execute():
Future<Either<Failure, Success>> execute({
required String username,
required String password,
}) async {
if (username.isEmpty || password.isEmpty) {
// return wrapper around AuthFailure.wrongCredentials()) of Either left subtype (Either has two subtypes)
}
...
}
This way I only verify return type, and all the lines are covered this way. It's gonna work, but I feel better with the simplest, not the smartest solution.
PS I use Mocktail for mocking, but using Mockito in solution is also warmly welcomed.
If you just want to verify that the callback is triggered, I personally would just make your callback set a flag and then test that flag afterward, which I think is straightforward, simple, and easy to understand with no magic:
test('use case - failure execution for incorrect credentials', () async {
var failureCalled = false;
final user = await useCase.execute(
username: "noname",
password: "password",
onFailure: (fail) => failureCalled = true,
onSuccess: (user) {},
);
expect(user, const User.anonymous());
expect(failureCalled, true);
});
But if you really want to use Mocks, you will need some Mock object to use and to call instance methods on that in callbacks. With Mockito you could do:
test('use case - failure execution for incorrect credentials', () async {
dynamic mock = Mock();
final user = await useCase.execute(
username: "noname",
password: "password",
onFailure: (fail) => mock.fail(fail),
onSuccess: (user) {},
);
expect(user, const User.anonymous());
verify(mock.fail(any)).called(1);
});
Some things to note:
To avoid declaring a class with the expected instance methods and then code-generating stubs, create a raw Mock instance but declare it as dynamic to disable static type-checking. This will then take advantage the Mock.noSuchMethod implementation.
You can't use onFailure: mock.fail directly since the Mock has no generated stubs, and mock.fail will just be null instead of a Function.
I am not experienced with Mocktail, but I imagine that you could do something similar.
var a = await () async {try{return await ... }}.call();
I was thinking that way to write is not convenient, they are so verbose.
"await () async {...}" is a very ugly style.
What I'm wrong? There is another way to do this?
var a = await () async
{
try {
return await method();
} catch (e) {
return anotherMethod();
}
}.call();
EDIT:
The return inside the try block send the value to a variable "a". Otherwise, I had to: First: declare a variable Then: set the value inside the try-catch block.
This kind of structure can easily drive you to fail, once that you don't have any guarantee that the variable are true set.
By the other hand: with an inline function you have to return any value otherwise the compile alert by the ERROR. And you get a more refactorable code;
I am trying to get Contact using the function getContactsForPhone
getName()async{
number = '123-456-7890'
return await ContactsService.getContactsForPhone(number).then((value) => value.elementAt(0).displayName.toString());
}
but I am getting Future<dynmaic> instead of .displayName which is supposed to be String
You are mixing it up two way's to use Futures:
You can use await keyword to await for the conclusion.
You can use the then method to have a callback when the Future ends.
You need to choose one and stick with it. Using the await is always preferable because it makes the code more readable and avoids some callback hells.
In your case:
Future<String> getName() async {
number = '123-456-7890'
Iterable<Contact> myIterable = await ContactsService.getContactsForPhone(number);
List<Contact> myList = myIterable.toList();
return myList[0].displayName.toString()
}
Which should return the DisplayName you wanted.
Remember to also use the await keyword from the outside, wherever you call this function.
You can read more about Future and Asynchronous code here.
I have a firebase cloud function to create a user document with user data whenever a user registers. How would I return an error when the set() fails? Since this is not an http request (an I don't want to use an http request in this case) I have no response. So how would I catch errors?
export const onUserCreated = functions.region('europe-west1').auth.user().onCreate(async user => {
const privateUserData = {
phoneNumber: user.phoneNumber
}
const publicUserData = {
name: 'Nameless'
}
try
{
await firestore.doc('users').collection('private').doc('data').set(privateUserData);
}catch(error)
{
//What do I put here?
}
try
{
await firestore.doc('users').collection('public').doc('data').set(publicUserData);
}catch(error)
{
//What do I put here?
}
});
You can't "return" an error, since the client doesn't even "know" about this function running, there is nobody to respond to.
You can make a registration collection, and in your function make a document there for the current user (using the uid as the document id). In that document, you can put any information you'd like your user to know (status, errors, etc).
So your clients would have to add a listener to this document to learn about their registration.
In your particular code, I think the error is in doc('users'). I guess you meant doc('users/'+user.uid).
Your catch -block will receive errors that occur on your set -call:
try {
await firestore.doc('users').collection('public').doc('data').set(publicUserData);
} catch (error) {
// here you have the error info.
}
Can anybody tell me if it is possible to create a deferred completable in a concat operator.
I want to fetch a session, and after this load a user with the corresponding session id.
SessionAPI.post(email: email, password: password)
UserAPI.get(id: Session.load()!.userId)
Until now I used observables with the flatMap operator.
I will now try to reproduce the same behaviour with the completables, which doesn't have flatMap operator.
Working code with observables:
SessionAPI.post(email: email, password: password)
.flatMap { (_) -> Single<Any> in
return UserAPI.get(id: Session.load()!.userId)
}
New working code with completables
SessionAPI.post(email: email, password: password)
.concat(Completable.deferred { UserAPI.get(id: Session.load()!.userId) } )
I now want to create an extension for this deferred completable, like:
SessionAPI.post(email: email, password: password)
.concatDeferred(UserAPI.get(id: Session.load()!.userId))
Current extension:
extension PrimitiveSequenceType where Self.Element == Never, Self.Trait == RxSwift.CompletableTrait {
func concatDeferred(_ second: RxSwift.Completable) -> RxSwift.Completable {
return Completable.deferred { () -> PrimitiveSequence<CompletableTrait, Never> in
return second
}
}
}
Issue: The Session.load()! in UserAPI.get is loaded and crashing before SessionAPI.post finished.
Does someone got an idea to get this extension up running?
Thanks!
I'm going to assume that the reason you want to defer your UserAPI.get(id:) is because some "magic" is happening in the background where SessionAPI.post(email:password:) is making it so Session.load() is valid.
What this tells me is that the post(email:password:) should not be completable in the first place. Rather it should return an Observable<T> where T is whatever Session.load() returns.
You can't make the code work like you want:
SessionAPI.post(email: email, password: password)
.concatDeferred(UserAPI.get(id: Session.load()!.userId))
With the above code, Session.load() will get called before SessionAPI.post(email:password:) is even called no matter what code you put in concatDeferred.
The Session.load() function must be called before concatDeferred is so that the former can pass its result into the latter.