I am very new to RhinoMocks (first time).
This is my method declaration:
void SetXmlValue(ref string path, ref string Value);
I need to verify that for the given input, when SetXmlValue is called, it gets called with particular values for path and value
I have tried a few options including:
_mockClass.Expect(o => o.SetXmlValue(ref Arg<string>.Ref(Rhino.Mocks.Constraints.Is.Equal(path), "somepath").Dummy ,ref Arg<string>.Ref(Rhino.Mocks.Constraints.Is.Equal(value), "somevalue").Dummy));
_mockClass.Expect(o => o.SetXmlValue(ref Arg<string>.Out(path).Dummy, ref Arg<string>.Out(value).Dummy))
However, I am not able to confirm that the SetXmlValue method was called with the parameters I am expecting it to call.
Any inputs?
This verifies that the method gets called with the correct params:
_mockClass.Stub(x => x.SetXmlValue(null, null))
.IgnoreArguments()
.WhenCalled(invocation => Assert.AreEqual("somepath", invocation.Arguments[0]));
.WhenCalled(invocation => Assert.AreEqual("somevalue", invocation.Arguments[1]));
_mockClass.VerifyAllExpectations();
EDIT
Just found another way of doing this:
_mockClass.GetArgumentsForCallsMadeOn(x => x.SetXmlValue(null, null));
Assert.AreEqual("somepath", arguments[0][0]);
Assert.AreEqual("somevalue", arguments[0][1]);
The second example was useful, when I used Gherkin scenarios and the step definition of result had to be the last one (which is why I could not set the expectations beforehand).
In this case arguments[0] contains the parameters when SetXmlValue() was called first time, arguments[1] contains the parameters when SetXmlValue() was called second time, and so on.
Related
I try to read data from Firebase and it doesn't work with a large number of nodes,
Here is what I tried :
Future<int> test() async {
final response = await FirebaseDatabase.instance.reference().child('...').once();
var urls=[];
response.value.forEach((v) => urls.add(v));
print(urls);
return urls.length;
}
Result :
It works well with some test data. Example:
but if I try to read 90 nodes, example :
Result :
(dynamic) => void' is not a subtype of type '(dynamic, dynamic) =>
void' of 'f
Any idea?
NEW
I found that if the key is for example :
-LU645_UgPbGZhBsneOq
(auto generated by a cloud function using .push().set() )
then it doesn't work.
More details : if the key is not numeric, then it doesn't work
Why?
If you use the node names given by push, then the value returned by once().value is a Map. Thus to iterate over it with foreach needs a function taking two arguments instead of one (which is exactly what the errormessage is telling you):
response.value.forEach((dynamic key, dynamic v) => urls.add(v))
this should give you in urls a list of maps with a "name" and an "url" key. The parameter "key" in the foreach function would be the nodename containing the name and url.
Why does a single-valued foreach function work with numerical node names? If you assign numerical node names firebase will return those nodes as an array. As your sample trees lack the "0"-Node you get "null" as the first element of the array.
I am trying to grab information from my firebase database after a particular intent is invoked in my conversation flow.
I am trying to make a function which takes a parameter of user ID, which will then get the highscore for that user, and then say that users highscore back to them.
app.intent('get-highscore', (conv) => {
var thisUsersHighestscore = fetchHighscoreByUserId(conv.user.id);
conv.ask('your highest score is ${thisUsersHighestScore}, say continue to keep playing.');
});
function fetchHighscoreByUserId(userId){
var highscoresRef = database.ref("highscores");
var thisUsersHighscore;
highscoresRef.on('value',function(snap){
var allHighscores= snap.val();
thisUsersHighscore = allHighscores.users.userId.highscore;
});
return thisUsersHighscore;
}
An example of the data in the database:
"highscores" : {
"users" : {
"1539261356999999924819020" : {
"highscore" : 2,
"nickname" : "default"
},
"15393362381293223232222738" : {
"highscore" : 78,
"nickname" : "quiz master"
},
"15393365724084067696560" : {
"highscore" : "32",
"nickname" : "cutie pie"
},
"45343453535534534353" : {
"highscore" : 1,
"nickname" : "friendly man"
}
}
}
It seems like it is never setting any value to thisUsersHighScore in my function.
You have a number of issues going on here - both with how you're using Firebase, how you're using Actions on Google, and how you're using Javascript. Some of these issues are just that you could be doing things better and more efficiently, while others are causing actual problems.
Accessing values in a structure in JavaScript
The first problem is that allHighscores.users.userId.highscore means "In an object named 'allHighscores', get the property named 'users', from the result of that, get the property named 'userId'". But there is no property named "userId" - there are just a bunch of properties named after a number.
You probably wanted something more like allHighscores.users[userId].highscore, which means "In an object named 'allHighscores', get the property named 'users', fromt he result of that, get the property named by the value of 'userId'".
But if this has thousands or hundreds of thousands of records, this will take up a lot of memory. And will take a lot of time to fetch from Firebase. Wouldn't it be better if you just fetched that one record directly from Firebase?
Two Firebase Issues
From above, you should probably just be fetching one record from Firebase, rather than the whole table and then searching for the one record you want. In firebase, this means you get a reference to the path of the data you want, and then request the value.
To specify the path you want, you might do something like
var userRef = database.ref("highscores/users").child(userId);
var userScoreRef = userRef.child( "highscore" );
(You can, of course, put these in one statement. I broke them up like this for clarity.)
Once you have the reference, however, you want to read the data that is at that reference. You have two issues here.
You're using the on() method, which fetches the value once, but then also sets up a callback to be called every time the score updates. You probably don't need the latter, so you can use the once() method to get the value once.
You have a callback function setup to get the value (which is good, since this is an async operation, and this is the traditional way to handle async operations in Javascript), but you're returning a value outside of that callback. So you're always returning an empty value.
These suggest that you need to make fetchHighScoreByUserId() an asynchronous function as well, and the way we have to do this now is to return a Promise. This Promise will then resolve to an actual value when the async function completes. Fortunately, the Firebase library can return a Promise, and we can get its value as part of the .then() clause in the response, so we can simplify things a lot. (I strongly suggest you read up on Promises in Javascript and how to use them.) It might look something like this:
return userScoreRef.once("value")
.then( function(scoreSnapshot){
var score = scoreSnapshot.val();
return score;
} );
Async functions and Actions on Google
In the Intent Handler, you have a similar problem as above. The call to fetchHighScoreByUserId() is async, so it doesn't finish running (or returning a value) by the time you call conv.ask() or return from the function. AoG needs to know to wait for an async call to finish. How can it do that? Promises again!
AoG Intent Handlers must return a Promise if there is an asyc call involved.
Since the modified fetchHighScoreByUserId() returns a Promise, we will leverage that. We'll also set our response in the .then() part of the Promise chain. It might look something like this:
app.intent('get-highscore', (conv) => {
return fetchHighscoreByUserId(conv.user.id)
.then( function(highScore){
conv.ask(`Your highest score is ${highScore}. Do you want to play again?`);
} );
});
Two asides here:
You need to use backticks "`" to define the string if you're trying to use ${highScore} like that.
The phrase "Say continue if you want to play again." is a very poor Voice User Interface. Better is directly asking if they want to play again.
I'm working on a Firebase Cloud Function. When I log the value of change.after.val() I get a printout of a key-value pair
{ DCBPUTBPT5haNaMvMZRNEpOAWXf3: 'https://m.youtube.com/watch?v=t-7mQhSZRgM' }
rather than simply the value (the URL). Here's my code. What am I not understanding about .val() ? Shouldn't "updated" simply contain the URL?
exports.fanOutLink = functions.database.ref('/userLink').onWrite((change, context) => {
const updated = change.after.val();
console.log(updated);
return null
});
If you want only the URL value, you should include a wildcard in your trigger path for the URL key:
exports.fanOutLink = functions.database.ref('/userLink/{keyId}').onWrite((change, context) => {
console.log('keyId=', context.params.keyId);
const updated = change.after.val();
console.log(updated);
return null
});
In the Realtime Database, data is modeled as a JSON tree. The path specified in an event trigger identifies a node in the tree. The value of the node, being JSON, includes all child nodes. The change parameter for the trigger event refers to the value of the entire node.
I indicated above that you can change the trigger path to refer one level down. An alternative is to access the children of the node using the child() method of DataSnapshot.
Without knowing your use-case, it's hard to be more specific about the trigger event path you should use. Keep in mind that the event fires when any element of the node value changes, whether it be a simple value at the root level, or a value of a child node. It is often the case that you want the trigger to be as specific as possible, to better identify what changed. That's where wildcards in the path are useful. As I showed in the code I posted, the string value of a wildcard is available from the context parameter.
Is there a possibility to specify whether the action has its error field set to true?
const response = function*() {
yield takeEvery("CLIENT_RESPONSE", handleResponse);
}
However, we don't know whether the action with type CLIENT_RESPONSE has its error field set to true or not.
I know I can check this in the handleResponse but that seems to be more work than it should. For instance, the handleResponse might get complex because for both the non-error and error case I need to write a lot of code (i.e. I want to have different handlers for both cases).
So is there a way to specify to only take that action when error is set to true?
According to Saga API reference, the pattern (first argument) of takeEvery can be String, Array or Function.
You can achieve what you want by passing a function:
const response = function*() {
yield takeEvery(action => (action.type === "CLIENT_RESPONSE" && !action.error), handleResponse);
}
I have an object which might contain a promise property declared thus:
type PromiseAction = {
+type: string,
promise: ?Promise<any>,
};
The action argument to a function is declared to be of type PromiseAction:
(action: PromiseAction) =>
Later on I check whether the received action object does have a promise property and if action.promise has a then:
if (action.promise && typeof action.promise.then === 'function') {
If it does then I hook onto the promise chain:
return promise.then(
At which point I get the error: "type parameter U of call of method then. Missing annotation"
I can see in the source for flow that the then property of a Promise has a U parameter which, I assume, is the one being asked for.
How can an provide that U annotation if I only have only one parameter Promise<+R> in the type declaration?
You do not need to define the value of U.
The flow source you linked to means, essentially, "Promises returned by then fulfill with a value that is the same as either the return value of the handlers, or the fulfilled value of the returned Promise of those handlers." That sounds confusing (because Promises can be very confusing) but the bottom line is that it's not something you "fill out". It creates a relationship between the types that then returns and the types of the return values of onFulfill and onReject passed to then.
The error you're getting means that Flow can't figure out what that relationship is because it doesn't have enough information. Annotate the then callbacks with types:
return promise.then((a:string)=>...)
That will either fix the error, or at least disambiguate U enough to give you a more specific error.