I know when application loads, mirage seeds the database. But I wanted to know is there a way to change the seeded database dynamically later on (for example on some user actions).
So, I have an API which gives me the status of the progress and I am polling that API call. Initially Mirage seeds the database for me but every time I make that API call, same data is returned and I want the data to change so that I can test my UI design. Is there any way to do it?
Yes, in your mirage/config.js:
let pollNum = 0;
this.get('/api/poll', () => {
pollNum++;
if (pollNum > 2) {
return { success: true }; // replace with your success fixture
} else {
return { success: false }; // replace with your in progress fixture
}
});
Related
I am trying to use fixtures with my acceptance tests however its causing tests to fail due to duplicate data since fixtures are re-inserted in the mirage DB every test. Is there a way to get around this or remove the fixtures every test
setupApplicationTest(hooks);
setupMirage(hooks);
hooks.beforeEach(function() {
this.server.loadFixtures();
});
Where is this code that you're showing me above coming from?
In an acceptance test, Mirage automatically starts / stops the server from an initializer it bundles in its addon under addon/instance-initializers/ember-cli-mirage-autostart.js:
let server = startMirage(appInstance);
testContext.server = server;
// To ensure that the server is shut down when the application is
// destroyed, register and create a singleton object that shuts the server
// down in its willDestroy() hook.
appInstance.register('mirage:shutdown', MirageShutdown);
which calls:
willDestroy() {
let testContext = this.get('testContext');
testContext.server.shutdown();
delete testContext.server;
}
Ember starts and stops the app between every test which means that each acceptance test automatically starts with an empty database.
If you are outside the context of an acceptance test, you need to do the start stopping yourself.
// tests/integration/components/your-test.js
import { startMirage } from 'yourapp/initializers/ember-cli-mirage';
moduleForComponent('your-component', 'Integration | Component | your component', {
integration: true,
beforeEach() {
this.server = startMirage();
},
afterEach() {
this.server.shutdown();
}
});
Calling shutdown after each test is vital for clearing the data
Stumped on a couple failures and want to know if I'm understanding Mirage correctly:
1.In ember-cli-mirage, am I correct that the server response I define should reflect what my actual server is returning? For example:
this.get('/athletes', function(db, request) {
let athletes = db.athletes || [];
return {
athletes: athletes,
meta: { count: athletes.length }
}
});
I am using custom serializers and the above matches the format of my server response for a get request on this route, however, on two tests I'm getting two failures with this error: normalizeResponse must return a valid JSON API document: meta must be an object
2.Is mirage enforcing the json:api format, and is it doing so because of the way I'm setting up the tests?
For example, I have several tests that visit the above /athletes route, yet my failures occur when I use an async call like below. I would love to know the appropriate way to correctly overwrite the server response behavior, as well as why the normalizeResponse error appears in the console for 2 tests but only causes the one below to fail.
test('contact params not sent with request after clicking .showglobal', function(assert) {
assert.expect(2);
let done = assert.async();
server.createList('athlete', 10);
//perform a search, which shows all 10 athletes
visit('/athletes');
fillIn('.search-inner input', "c");
andThen(() => {
server.get('/athletes', (db, request) => {
assert.notOk(params.hasOwnProperty("contacts"));
done();
});
//get global athletes, which I thought would now be intercepted by the server.get call defined within the andThen block
click('button.showglobal');
});
});
Result:
✘ Error: Assertion Failed: normalizeResponse must return a valid JSON API document:
* meta must be an object
expected true
I tried changing my server response to a json:api format as suggested in the last example here but this looks nothing like my actual server response and causes my tests to fail since my app doesn't parse a payload with this structure. Any tips or advice must appreciated.
You are correct. Are the failures happening for the mock you've shown above? It looks to me like that would always return meta as an object, so verify the response is what you think it should be by looking in the console after the request is made.
If you'd like to see responses during a test, enter server.logging = true in your test:
test('I can view the photos', function() {
server.logging = true;
server.createList('photo', 10);
visit('/');
andThen(function() {
equal( find('img').length, 10 );
});
});
No, Mirage is agnostic about your particular backend, though it does come with some defaults. Again I would try enabling server.logging here to debug your tests.
Also, when writing asserts against the mock server, define the route handlers at the beginning of the test, as shown in the example from the docs.
I was able to get my second test to pass based on Sam's advice. My confusion was how to assert against the request params for a route that I have to visit and perform actions on. I was having to visit /athletes, click on different buttons, and each of these actions was sending separate requests (and params) to the /athletes route. That's is why I was trying to redefine the route handler within the andThen block (i.e. after I had already visited the route using the route definition in my mirage/config file).
Not in love with my solution, but the way I handled it was to move my assertion out of route handler and instead assign the value of the request to a top-level variable. That way, in my final andThen() block, I was able to assert against the last call to the /athletes route.
assert.expect(1);
//will get assigned the value of 'request' on each server call
let athletesRequest;
//override server response defined in mirage/config in order to
//capture and assert against request/response after user actions
server.get('athletes', (db, request) => {
let athletes = db.athletes || [];
athletesRequest = request;
return {
athletes: athletes,
meta: { count: athletes.length }
};
});
//sends request to /athletes
visit('/athletes');
andThen(() => {
//sends request to /athletes
fillIn('.search-inner input', "ab");
andThen(function() {
//sends (final) request to /athletes
click('button.search');
andThen(function() {
//asserts against /athletes request made on click('button.search') assert.notOk(athletesRequest.queryParams.hasOwnProperty("contact"));
});
});
});
I'm still getting console errors related to meta is not an object, but they are not preventing tests from passing. Using the server.logging = true allowed me to see that meta is indeed an object in all FakeServer responses.
Thanks again to Sam for the advice. server.logging = true and pauseTest() make acceptance tests a lot easier to troubleshoot.
I have been following the Sails.js documentation for testing, here:
http://sailsjs.org/documentation/concepts/testing
I have successfully been able to implement Controller tests that
hit different paths of my app, and check the responses of different Express requests.
My trouble is knowing A) How to instantiate a Model, specifically from my User model B) How I can guarantee that the model is successfully created.
I currently have a test in which, in a before hook, I create new user with all the required attributes:
before(function(){
User.create({firstName:"Bob", lastName: "Balaban", password:"12345", email:"bob#bob.com"})
});
The problem is that, I do not know how to verify if this record has been added to my tests database, or if a validation error or some other error is thrown upon the call to create.
NOTE: I ask this, because a test which is dependent upon the before() hook successfully functioning fails, and the only reason it could possible fail is if the User wasn't actually added to the db
You need to wait for the User to be created in before by using the done callback function argument, and calling it after you're done with setting up the test environment. Also, you're not lifting sails here for some reason despite the docs urging you to do so. I'd also recommend using a test database instead of your normal database so your test data is independent of your production / development data.
Example code below. The addition of done and the exec callback are probably the most vital parts.
var Sails = require('sails'), sails;
// ...
before(function(done) {
// Increase the Mocha timeout so that Sails has enough time to lift.
this.timeout(10000);
Sails.lift({
// If you want to use a different DB for testing, uncomment these and replace with your own DB info.
/*connections: {
// Replace the following with whatever suits you.
testMysql: {
adapter : 'sails-mysql',
host : 'localhost',
port : 3306,
user : 'mySQLUser',
password : 'MyAwesomePassword',
database : 'testDB'
}
},
models: {
connection: 'testMysql',
migrate: 'drop'
}
*/
}, function(err, server) {
sails = server;
if (err) return done(err);
User.create({firstName:"Bob", lastName: "Balaban", password:"12345", email:"bob#bob.com"})
.exec(function(err, createdUser) {
if (err) {
console.log("Failed to create user! Error below:");
console.log(err);
}
else {
console.log("User created successfully:");
console.log(user);
}
done(err, sails);
})
});
});
I am loading a large model (2500 entries) in Ember Data from an API.
It also takes 3 HTTP Requests since the server will only return 1000 results at a time.
My whole web browser freezes for a moment while it is loading, which begs the question:
What is the best way to load large models without blocking the UI?
I tried beginPropertyChanges, endPropertyChanges:
Ember.RSVP.resolve(store.beginPropertyChanges())
.then(getAllTeams) // this loads the model
.then(function() { return Ember.RSVP.resolve(store.endPropertyChanges()); });
var getAllTeams = function(teams, skip) {
if (!teams) {
return store.find('team', {limit:1000, skip: 0}).then(function(foundTeams) {
var teams = foundTeams;
return getAllTeams(foundTeams,teams.get('length'));
});
}
else if (teams.get('length') < 1000) {
return store.find('team');
}
else {
return store.find('team', {limit: 1000, skip:skip}).then(function(foundTeams) {
return getAllTeams(foundTeams,skip+teams.get('length'));
});
}
}
Doing beginPropertyChanges on the store is not going to accomplish anything useful at all.
The default behavior of Ember is that yes, it will block on large downloads. Here is a possible approach.
// route
export default Ember.Route.extend({
model: function() {
var all = return this.store.all('team');
function get_more(n) {
return store.find('team', {limit:1000, skip: n}) .
then(function(teams) {
if (teams.length === 1000) return get_more(n+1000);
})
}(0));
return all;
});
We return a live collection of teams in the store, which initially might be zero. Asynchronously to that, we start a loop which gets items 1000 at a time. As the new items come in, the live collection will be updated and the relevant UI will as well.
Untested.
Ember provides a mechanism for handling long route render times. I believe what you are looking for is Loading / Error Substates.
Check out official EmberJS guide pages.
Happy coding :)
As you know, inside unit tests it's built-in angularjs feature to mock XHR requests with $httpBackend - this is nice and helpful while writing unit tests.
Recently, I met with need of mocking XHR in case of file upload and discovered some problems.
Consider following code:
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", uploadProgress(event), false);
xhr.addEventListener("load", uploadComplete(event), false);
xhr.addEventListener("error", uploadError(event), false);
xhr.addEventListener("abort", uploadAbort(event), false);
xhr.open("POST", 'some url');
xhr.send(someData);
What I want to do is to do unit testing of such a code with mocking of XHR requests, but it's not possible do it because there is no $http service used here.
I tried this (and it's working and could be mocked with $httpBackend):
$http({
method: 'POST',
url: 'some url',
data: someData,
headers: {'Content-Type': undefined},
transformRequest: angular.identity})
.then(successCallback, errorCallback);
But in this case I don't know how to implement 'progress' callback and 'abort' callback (they are essential and required in case I am working on now).
I've seen information that latest Angular supports progress callback for promises (not sure though whether it's integrated with $http service), but what about abort callback?
Any ideas or maybe your met with something similar before?
If the $http service doesn't give you everything you need, you can still unit test the first block of code. First of all, change your code to use Angular's $window service. This is just a wrapper service, but it allows you to mock the object in your tests. So, you'll want to do this:
var xhr = new $window.XMLHttpRequest();
Then in your tests, just mock it and use spies.
$window.XMLHttpRequest= angular.noop;
addEventListenerSpy = jasmine.createSpy("addEventListener");
openSpy = jasmine.createSpy("open");
sendSpy = jasmine.createSpy("send");
xhrObj = {
upload:
{
addEventListener: addEventListenerSpy
},
addEventListener: addEventListenerSpy,
open: openSpy,
send: sendSpy
};
spyOn($window, "XMLHttpRequest").andReturn(xhrObj);
From there, you can make the different spies return whatever you want for the different tests.
You should mock $http and control any deferred, as you want more control over your test. Basically, mock $http provider and serve a custom implementation that exposes its deferred, then play with it.
You should not worry whether $http is working right or not, because it is supposed to, and is already tested. So you have to mock it and only worry testing your part of the code.
You should go something like this:
describe('Testing a Hello World controller', function() {
beforeEach(module(function($provide) {
$provide.provider('$http', function() {
this.$get = function($q) {
return function() {
var deferred = $q.defer(),
promise = deferred.promise;
promise.$$deferred = deferred;
return promise;
}
};
});
}));
it('should answer to fail callback', inject(function(yourService, $rootScope) {
var spyOk = jasmine.createSpy('okListener'),
spyAbort = jasmine.createSpy('abortListener'),
spyProgress = jasmine.createSpy('progressListener');
var promise = yourService.upload('a-file');
promise.then(spyOk, spyAbort, spyProgress);
promise.$$deferred.reject('something went wrong');
$rootScope.$apply();
expect(spyAbort).toHaveBeenCalledWith('something went wrong');
}));
});
And your service is simply:
app.service('yourService', function($http) {
return {
upload: function(file) {
// do something and
return $http({...});
}
};
});
Just note that promises notification is only available in the latest RC release. So, if you can't use it, just elaborate a little more the example and mock the XHR events and so.
Also note that you should preferably have one test case for each of the callbacks (fail, success and progress), in order to follow KISS principle.