test of angularjs controller: unsatistifed post request - unit-testing

I'm testing an angularjs controller, using also mocks, but it raises the error 'Error: Unsatisfied requests: POST /myurl
My file for test contains a beforeEach method like this
httpBackend.whenPOST('/myurl')
.respond( 200,obj1 );
httpBackend.expectPOST('/myurl')
scope = $rootScope.$new();
MainCtrl = $controller('MyCtrl', {
$scope:scope
});
and my test case is:
it('scope.mymethod should work fine', function(){
httpBackend.flush()
// verify size of array before calling the method
expect(scope.myobjs.length).toEqual(2)
// call the method
scope.saveNewPage(myobj)
// verify size of array after calling the method
expect(scope.myobjs.length).toEqual(3)
})
The method saveNewPage looks like:
function saveNewPage(p){
console.log('Hello')
$http.post('/myurl', {
e:p.e, url:p.url, name:p.name
}).then(function (response) {
otherMethod(new Page(response.data.page))
}, handleError);
}
Note that console.log('Hello') is never executed (in karma console it's never printed).
EDIT:
In the meanwhile I'm studying the doc about httpBackend, I tried to change the position of httpBackend.flush(). Basically, i'm executing a first flush(), to initialize data in the scope, then I execute the method, and then I execute an other flush() for the pending request. Specifically, in this situation the test case look like:
it('scope.saveNewPage should work fine', function(){
var p=new Object(pages[0])
httpBackend.flush()
httpBackend.whenPOST('/myurl',{
url:pages[0].url,
existingPage:new Object(pages[0]),
name:pages[0].name
}).respond(200,{data:pages[0]})
httpBackend.expectPOST('/myurl')
scope.saveNewPage(p)
httpBackend.flush()
expect(scope.pages.length).toBe(3)
})
But now it raises Error: No response defined !, like if I didn't specified the mock for that url

I solved in this way:
I put the calls of whenPOST and expectPOST before calling the method to test
I put httpBackend.flush() after calling the method to test, such that, invoking the method it generates pending request, and by httpBackend.flush() it satisfies the pending requests
I adjusted the parameter of respond method. Basically it didn't need to associate the response to a data key of the response

Assuming the POST is supposed to come from saveNewPage, you will need to call httpBackend.flush() between saveNewPage and the line where you inspect the result. flush only flushes the responses that have already been requested by your code.
it('scope.mymethod should work fine', function(){
expect(scope.myobjs.length).toEqual(2)
scope.saveNewPage(myobj)
expect(scope.myobjs.length).toEqual(2)
httpBackend.flush()
expect(scope.myobjs.length).toEqual(3)
})

Related

Baqend onUpdate Handler

Will doing partialupdate() cause code in a data class' onUpdate Handler to run?
I have this setup in the data class:
exports.onUpdate = function(db, obj) {
DB.log.info(obj.ShiftID);
db.Shifts.load(obj.ShiftID)
.then((Shift) => {
DB.log.info(Shift);
if (Shift.User == db.User.me) {
Shift.User = null;
Shift.status = 0;
return Shift.update();
}
})
};
(yes, role 2 for node has permissions to query and update the Shifts data class)
But I am getting zero logs when I make a partialupdate(). Do I need to do a real update query...load the object, modify the data, update()?
Also it seems that this code causes the partialupdate() to not run at all, but when I delete the handler, it starts working again.
Yes, that is currently an unimplemented feature since a partial update can't execute an onUpdate handler since there is no object which can be passed to the update handler.
On the other hand, a partial update can't be executed directly since that will result in a security issue (since your onUpdate handler can contain validation code etc.)
So we currently reject any partial update on a class which has an onUpdate handler because there doesn't exist a way how we can actually validate the partial update against your onUpdate code.
We have planned that you can define an extra onPartial handler where you can take some extra steps before the partialUpdate is executed. But that handler will only get the partial update and not the object itself.
I'm pretty sure that partialupdate() will not cause the onUpdate Handler to run.
When I put the log line in and edit the records using website data manager it does log as expected. Not a big deal, I can just rewrite the query to be a full update.
BUT having any code in there does break partialupdate() which is not good.
Here is the code I'm using that works as long as there is nothing in the onUpdateHandler:
requestShift(shiftID) {
db.ready().then((db) => {
db.Applicants.find()
.where({
"shiftID": { "$in": [shiftID] },
})
.singleResult((applicants) => {
return applicants.partialUpdate()
.add("applicants", db.User.me.id)
.add("photos", this.props.UserData.photo)
.execute()
})
Alert.alert(
'Confirmation',
'Shift has been requested.',
)
this.props.navigation.dispatch(goToFindShifts)
})
}

<query>.loading will not change to true

What are the possible reasons for query being stuck on loading = true (networkStatus = 1)?
I cannot get a query result on refetch and cannot log 'called2'
graphql(_stepQuery, {
name: 'stepQuery',
options: ({goalDocId}) => ({
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true,
variables: {
goalDocId
}
})
}
)
componentWillReceiveProps(nextProps) {
let stepIdsFromServer
if (nextProps.currentGoalSteps.length > this.props.currentGoalSteps.length) {
console.log('called')
this.props.stepQuery.refetch()
console.log('this.props', this.props)
console.log('nextProps',nextProps)
if (!nextProps.stepQuery.loading) {
// console.log('nextProps.stepQuery.allSteps', nextProps.stepQuery.allSteps)
console.log('called2')
}
This looks quite dangerous for a infinite loop.
First the refetch function is a Promise, so you will not be able to know the correct query state right after the call for refetching. You would need to go on in the .then function. See refetch Api.
Second the query in the end is executed inside the graphql wrapper Component. So you should not check the loading state and refetch in the componentWillReceiveProps function, Because when the query is executed again the whole component is instantiated again and will enter the componentWillReceiveProps function with resetted states and so on.
If you need some kind of search, i suggest you use a mutation as a workaround (using withApollo wrapper and in the componentWillReceiveProps you call this.props.client("MUTATION")), because this will not render the whole component.

Qunit assert throws not working

I am trying to assert that a few steps in the code (visiting page, providing wrong card-number, password combination and clicking submit)should generate an error from the backend service - I have referred to this already..
and tried the suggestion of using Error Object as the second argument to assert.throws but that doesn't work for me.
i did see this link as well before posting my question,
My problem is - I don't have control over the code that throws the exception/error in this case. (I cannot change it to say Ember.assert etc) I just want to be able to catch an erroneous case.
Secondly, I don't have a component in this case. Its a straight forward API call that's made when click on submit is done, basically an action submitAuthForm is called in controller which calls ember cli mirage scenario that returns the following object problems object.
return new Response(401,{'X-Auth-Token': 'wrong-username-password-combination'},failResponse401);
and the returned object looks like
var failResponse401 = {
problems: [ {
field: null,
index: null,
value: null,
code: '0006',
subCode: '',
details: {},
_type: 'error'
} ]
};
We have a node_module dependency on an in-house exceptions kit that throws an Error object based on this.
Here's my Qunit test
test('ERROR_CASE_visiting /signon, provide cardNumber(2342314) and ' +
'password, submit and expect to see invalid cardnumber/password error',
function (assert) {
assert.expect(2);
assert.throws(
function () {
visit('/signon');
fillIn('#a8n-signon-card-number input', '2342314');
fillIn('#a8n-signon-password input', 'password');
click('#a8n-signon-submit-button button');
},
Error,
"Error Thrown"
);
});
I keep getting this error from Qunit
Error Thrown# 110 ms
Expected:
function Error( a ){
[code]
}
Result:
undefined
Diff:
function Error( a ){
[code]
}defined
Source:
at Object.<anonymous> (http://localhost:7357/assets/tests.js:175:12)
at runTest (http://localhost:7357/assets/test-support.js:3884:30)
at Test.run (http://localhost:7357/assets/test-support.js:3870:6)
at http://localhost:7357/assets/test-support.js:4076:12
at Object.advance (http://localhost:7357/assets/test-support.js:3529:26)
at begin (http://localhost:7357/assets/test-support.js:5341:20)
API rejected the request because of : []# 1634 ms
Expected:
true
Result:
false
Source:
Error: API rejected the request because of : []
at Class.init (http://localhost:7357/assets/vendor.js:172237:14)
at Class.superWrapper [as init] (http://localhost:7357/assets/vendor.js:55946:22)
at new Class (http://localhost:7357/assets/vendor.js:51657:19)
at Function._ClassMixinProps.create (http://localhost:7357/assets/vendor.js:51849:12)
at Function.createException (http://localhost:7357/assets/vendor.js:172664:16)
at Class.<anonymous> (http://localhost:7357/assets/vendor.js:133592:72)
at ComputedPropertyPrototype.get (http://localhost:7357/assets/vendor.js:32450:27)
at Object.get (http://localhost:7357/assets/vendor.js:37456:19)
at Class.get (http://localhost:7357/assets/vendor.js:50194:26)
at http://localhost:7357/assets/vendor.js:133645:30
Is there anything else that i can try to get it to work.
Is there someway i could somehow pass this test, by wrapping the returned response somehow in a way that it doesn't break my test altogether.
I found a workaround following this link
the user pablobm has posted a link to a helper
I used that to workaround this Qunit issue.

How to make a unit test in meteor that adds a document to a collection? Mocking userId

Thank you for your help-
I'd like to know if my app successfully adds a document to the database using a unit test in Meteor. I'm using practicalmeteor:mocha and chai. The issue I'm running into is that I don't know how to mock a this.userId, it keeps telling me I'm not logged in.
it('inserts the draft agenda document into the collection', function() {
// TODO: mock document to insert into collection
// TODO: mock userId and Agenda.insert
this.userId = "Not an empty string";
console.log("before spec, changing this.userId: " + this.userId) //is "Not an empty string"
Meteor.call('createAgenda', mockAgenda, function(res) {
console.log("callback with response: " + res); //You're not logged-in. [not-logged-in]
console.log("this.userId: " + this.userId) //is undefined
}
}
see https://docs.meteor.com/api/methods.html#DDPCommon-MethodInvocation-userId for more info on user id
test runner fails to import files in client directory
MochaRunner.runServerTests: failures: 1 when meteor methods are called
I have to call the server side meteor methods that have been declared in the testing context as if I am on the client, but I can't import the client files or operate as if I'm a client
MochaRunner.runServerTests: Starting server side tests with run id R7ocZh3Qva3rExTL9 runs basically every time
This seems useful but hasn't worked for me yet https://forums.meteor.com/t/testing-methods-which-use-this-userid/2292/8
Thank you for your help, any code examples would be great.
Remarks
I wanted to post a comment but do not have enough reputation. So here are some remarks. Since you are testing on the server, you can call a Meteor method without a callback. This will result in a synchronous execution and simplify your test. Otherwise you will have to let the test know it is finished by calling the done function in your callback, see mocha docs.
Using mdg:validated-method
You can call a valited method and provide the context in which they execute using the _execute function. Below is an example taken from the todos sample project. For more examples you can take a look at their Lists and Todos tests.
it('makes a list private and updates the todos', function() {
// Check initial state is public
assert.isFalse(Lists.findOne(listId).isPrivate());
// Set up method arguments and context
const methodInvocation = {
userId
};
const args = {
listId
};
// Making the list private adds userId to the todo
makePrivate._execute(methodInvocation, args);
assertListAndTodoArePrivate();
// Making the list public removes it
makePublic._execute(methodInvocation, args);
assert.isUndefined(Todos.findOne(todoId).userId);
assert.isTrue(Todos.findOne(todoId).editableBy(userId));
});
Using standard methods
Another possiblity would be to bind the standard call function to the correct context. Note that this is just a thought and not tested.
var methodInvocation = {
userId: "some user id"
};
Meteor.call.bind(methodInvocation)('createAgenda', mockAgenda);

Laravel Response::download() test

I have the following code in one of my routes:
return Response::download('cv.pdf');
Any idea how to test this? I've tried to use shouldReceive() but that doesn't seem to work ('shouldReceive() undefined function....').
$response->assertDownload() was added in Laravel 8.45.0:
Assert that the response is a "download". Typically, this means the invoked route that returned the response returned a Response::download response, BinaryFileResponse, or Storage::download response:
$response->assertDownload();
Learn More:
https://laravel.com/docs/8.x/http-tests#assert-download
EDIT: As pointed by #DavidBarker in his comment to the OP question
The Illuminate\Support\Facades\Response class doesn't actually extend
Illuminate\Support\Facades\Facade so doesnt have the shouldRecieve()
method. You need to test the response of this route after calling it
in a test.
So if you want to test your download functionality, you can try checking the response for errors with:
$this->assertTrue(preg_match('/(error|notice)/i', $response) === false);
You can assert that the status code is 200
$this->assertEquals($response->getStatusCode(), 200);
because sometimes you might have some data returned that match "error" or "notice" and that would be misleading.
I additionally assert that there's an attachment in the response headers:
$this->assertContains('attachment', (string)$response);
You can use Mockery to mock the download method, for this you will need to mock ResponseFactory.
public function testDownloadCsv()
{
$this->instance(
ResponseFactory::class, Mockery::mock(ResponseFactory::class, function ($mock) {
$mock->shouldReceive('download')
->once()
->andReturn(['header' => 'data']);
}));
$response = $this->get('/dowload-csv');
$response->assertStatus(Response::HTTP_OK);
$response->assertJson(['header' => 'data']); // Response
}