In a React application I want to test an ajax call
A click event is handled by a method:
_search = () => {
AppActions.search({
url: this.props.url,
queryValue: query
})
}
and this invokes a method on a store which makes an Ajax call:
onSearch (req) {
$.get(url)
.done((data) => {
// set state...
})
}
I was trying to use Sinon to stub the jquery ajax call:
describe('Search', function () {
describe('Rendering', function () {
beforeEach(() => {
sinon.stub($, 'ajax')
let component = TestUtils.renderIntoDocument(<MockComponent />)
this.renderedDOM = ReactDOM.findDOMNode(component)
})
afterEach(() => {
$.ajax.restore()
})
it('Should make an ajax request', () => {
TestUtils.Simulate.click(myButton)
// expect stubbed ajax call to have been made with args etc
})
})
})
Anyone know how I should do this so that I can:
stub the ajax call
invoke a callback or
inspect the stubbed ajax call
Thanks in advance
I think that you should use sinon's fakeServer api.
for example:
const server = sinon.fakeServer.create();
server.respondWith("GET", "/rest/api/yourURL", [200, {"Content-Type": "application/json"}, '{"response": "whatever"}']);
// TODO: test code
server.respond();
server.restore();
It can stub the ajax call and invoke a callback
Related
I have the following code
cy.intercept('GET', Cypress.env('activationCode')).as('getActivationCode')
let validationCode;
cy.request('GET', Cypress.env('activationCode'))
.then( ({ body }) => {
validationCode = body
console.log(body);
// this have the value
})
cy.wait('#getActivationCode')
console.log(validationCode)
// this is undefined
I need to receive a variable from a get request to fill a form but I don't know how to expect it to receive the value so that the execution can continue.
I don't want to code inside the then of the request.
console.log(validationCode) This is coming as undefined because of non-cypress commands are run before the cypress commands. So before validationCode is updated with any value, it is being printed. To avoid this us cy.log(). Also the the way cypress recommends to use variables is by using alias.
cy.intercept('GET', Cypress.env('activationCode')).as('getActivationCode')
cy.request('GET', Cypress.env('activationCode')).then(({body}) => {
cy.wrap(body).as('responseBody') //save response body using alias
console.log(body)
// this have the value
})
cy.wait('#getActivationCode')
cy.get('#responseBody').then((responseBody) => {
cy.log(responseBody) //prints the response body
})
If you want to use console.log you can do this:
let validationCode
cy.request('GET', Cypress.env('activationCode'))
.then(({body}) => {
validationCode = body
console.log(body)
// this have the value
})
.then(() => {
cy.wait('#getActivationCode')
console.log(body)
})
Cypress commands are async, so you should be careful when you mix async and sync code.
You can easily accessa certain property from a request by using the .its() command.
cy.intercept('GET', Cypress.env('activationCode'))
.its('response.body.variableIWant') // you'll need drill down to your specific variable you want
.as('variableIWant')
// some other code
cy.get('#variableIWant')
Another way using .then()
cy.intercept('GET', Cypress.env('activationCode'))
.its('response')
.then( resp = >{
// some code to get variable you want
return variableIWant //this will become new subject for cy commands
})
.as('variableIWant')
// some other code
cy.get('#variableIWant')
Another alternative, use combination before(), function() and .this
before(function() {
cy.request('GET', Cypress.env('activationCode'))
.then( ({ body }) => {
return body
})
.as('validationCode') // puts body into this.validationCode
})
it('tests the validationCode', function() {
console.log(this.validationCode)
})
it('another test of validationCode', function() {
console.log(this.validationCode)
})
You should look at Verifying the request modification
cy.intercept() cannot be debugged using cy.request()! Cypress only intercepts requests made by your front-end application.
Which means the cy.request() will not fire the cy.intercept()
Looking at the code generated by angular cli, the beforeEach method uses async but doesn't call done(). How does it work then? Isn't calling done mandatory when using async?
beforeEach(async(() => { //async
TestBed.configureTestingModule({
imports: [AppModule,RouterTestingModule.withRoutes(routes), ReactiveFormsModule]
})
.compileComponents();
}));//done not called
beforeEach(() => {
fixture = TestBed.createComponent(PageNotFoundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
The done() is the last callback an async function should call, calling it between will throw an error.
Have a look at this guide https://jasmine.github.io/tutorials/async , it describes about done() and it should be handled.
I'm testing an Angular App with Cypress.
I'm running my test with the Cypress dashboard, that I open using this command:
$(npm bin)/cypress open
I'm calling an API with my test: it works.
But when I change my code, Cypress will rerun the code which will cause my first (and only my first test) to fail. The request calling the API is aborted.
The only way to make it work again is to manually end the process, then start it again.
Has anyone got an idea what is causing this strange behaviour?
Here is my test code:
beforeEach(() => {
cy.visit('/');
cy.server();
cy.route('POST', `myUrl`).as('apiCall');
});
it('should find a doctor when user searches doctor with firstName', () => {
cy.get('#myInput').type('foo');
cy.get('#submitButton]').click();
cy.wait('#apiCall').then((xhr) => {
expect(xhr.status).to.eq(200);
});
});
You can prepare XHR stub like this:
describe('', () => {
let requests = {}; // for store sent request
beforeEach(() => {
cy.server({ delay: 500 }); // cypress will answer for mocked xhr after 0.5s
cy.route({
url: '<URL>',
method: 'POST',
response: 'fixture:response',
onRequest: ({ request }) => {
Object.assign(requests, { someRequest: request.body }); // it has to be mutated
},
});
});
And then in test:
it('', () => {
cy
.doSomeSteps()
.assertEqual(requests, 'someRequest', { data: 42 })
});
There is 2 advantages of this solution: first 0.5s delay make test more realistic because real backend doesn't answer immediately. Second is you can verify if application will send proper payload after doSomeActions() step.
assertEqual is just util to make assertion more readable
Cypress.Commands.add('assertEqual', (obj, key, value) =>
cy
.wrap(obj)
.its(key)
.should('deep.equal', value)
);
I'm trying to test my asyn thunk middleware function using mocha, chai and sinon (my first time!).
Please consider my files:
ayncActionCreators.js
export const fetchCurrentUser = () => {
return (dispatch) => {
setTimeout(dispatch, 100);
}
};
ayncActionCreators.spec.js
//...
it('Should work', () => {
const dispatch = sinon.spy();
const action = fetchCurrentUser();
action(dispatch);
expect(dispatch.called).to.be.true;
});
I did not yet implement the fetchCurrentUser function - just assumed it will take some "server" time and then it will call 'dispatch()'.
The spec fails, due to the async flow. If I add a setTimeout of 101 ms before the expect - it passes.
My code will use some DB API that returns promise, so the async function will eventually look like:
//...
return (dispatch) => {
return dbAPI.fetchUser().then(dispatch(....));
}
So I tried to require dbAPI and create a sinon.stub().returns(Promise.resolve()) inside the test and it didn't work as well (I thought that since the stub returns a resolved promise - the async function will act like a synchronous function).
Any ideas how should I test async functions like that?
Thank,
Amit.
Don't mock dispatch with sinon, write your own and call Mocha's done() in that when it's done.
it('Should work', (done) => {
const dispatch = () => {
// Do your tests here
done();
};
const action = fetchCurrentUser();
action(dispatch)
// Also allow quick failures if your promise fails
.catch(done);
})
If you're just wanting to ensure that the dispatch is called, then mocha will time out. The catch on the returned promise from your async action creator allows errors to be shown in the right place and for the test to fail rather than time out.
Well, I think I've found a solution:
Assuming my async function looks like this:
//...
return (dispatch) => {
return dbAPI.fetchUser().then(dispatch(....));
}
Then I can write the spec as follows:
it('Should work', () => {
dbAPI.fetchUser = sinon.stub().returns(Promise.resolve({username: 'John'}));
const dispatch = sinon.spy();
const action = fetchCurrentUser();
action(dispatch).then(() => {
expect(dispatch.called).to.be.true;
});
});
I don't know if this is a workaround or not, but it works. I would appreciate your opinions of a better way of doing this...
Thanks,
Amit.
I'm trying to send data to my view from an AJAX call to my API. I am able to successfully hit my API and get data, but I was having problems with the view rendering before the AJAX call came back.
I'm trying to wrap my AJAX call in a Promise but it's not working. Here's my layout
Controller
.controller('DashCtrl', function($scope, Tweets) {
$scope.tweets = Tweets.all()
})
Factory doing ajax call
.factory('Tweets', function($http) {
$http.get('http://localhost:3000/tweets')
.success(function(data) {
var tweets = data
debugger
})
return {
all: function() {
//should return the results of the AJAX call when it's complete
}
}
});
I've tried making wrapping the ajax call into a function and using .then(function(payload){ return payload.data }) - Payload.data has my data but its never returned when I call the function. I'm new to angular, so I would appreciate any help or insight.
You should define your factory as
.factory('Tweets', function($http) {
return {
all: function() {
return $http.get('http://localhost:3000/tweets')
.then(function(response) {
return reponse.data;
})
}
}
});
Then change your controller to
.controller('DashCtrl', function($scope, Tweets) {
Tweets.all().then(function(data) {
$scope.tweets = data;
});
})
Use the $resource service. The docs don't mention it, but comments in the source do.
$resolved: true after first server interaction is completed (either with success or rejection), false before that.
So in the controller:
$scope.tweets = $resource('/tweets').query()
And in the view:
<div ng-if="tweets.$resolved">
Loading data with ngResource or from factory promise callback are viable options, but there's one more way nobody mentioned yet: resolve data to controller via route definition. This approach allows to write simplistic controllers that don't know how to load data at all. In most cases it will be more than enough if you don't need to load data dynamically, like pagination or infinite scroll.
You will need to define route and resolve function:
angular
.module('app', ['ngRoute'])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
controller: 'ctrl',
controllerAs: 'view',
templateUrl: 'view.html',
resolve: {
tweets: function (Tweets) {
return Tweets.all();
}
}
})
})
The tweets property on resolve will inject loaded data into controller as tweets, all you have to do is just assign received data:
.controller('ctrl', function (tweets) {
this.tweets = tweets;
});
In addition, here's how Tweets service might look like:
.factory('Tweets', function ($timeout) {
function all () {
return $timeout(function () {
return ["hey", "there"];
});
}
return {
all: all
};
})
Basically, it exposes methods that return promise, returning some data ($timeout returns promise too, so I've used it instead of $http for example purpose).
Full example on JS Bin.