Testing asynchronously returned data - unit-testing

I have the following example:
<!DOCTYPE html>
<html>
<head>
<title>Mocha</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="mocha.css" />
</head>
<body>
<div id="mocha"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.1.2/mocha.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.min.js"></script>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-resource/1.0.3/vue-resource.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sinon.js/1.15.4/sinon.js"></script>
<script>mocha.setup('bdd');</script>
<script>
"use strict";
var assert = chai.assert;
var should = chai.should();
var vm = new Vue({
data: {
message: "Hello"
},
methods: {
loadMessage: function() {
this.$http.get("/get").then(
function(value) {
this.message = value.body.message;
});
},
}
});
describe('getMessage', function() {
let server;
beforeEach(function () {
server = sinon.fakeServer.create();
});
it("should get the message", function(done) {
server.respondWith([200, { 'Content-Type': 'application/json' },
JSON.stringify({message: "Test"})]);
vm.message.should.equal("Hello");
vm.loadMessage();
server.respond();
setTimeout(function() {
// This one works, but it's quirky and a possible error is not well represented in the HTML output.
vm.message.should.equal("Test");
done();
}, 100);
// This one doesn't work
//vm.message.should.equal("Test");
});
});
</script>
<script>
mocha.run();
</script>
</body>
</html>
I want to test that Vue asynchronously gets data from the server. Though, I mock out the actual HTTP request with Sinon FakeServer.
Naturally, directly after the call to loadMessage, the message is not yet set. I could use a timeout function for the test, but I believe there should be a better method. I've looked into respondImmediately, but it did not change. Also, there is the possibility to call a done() function. However, as I understand this, this done would need to be called within the loadMessage function, hence modifying the code under test.
What is the correct approach to handle this problem?
Edit: I have found at least a partial solution, but it seems to be still messy: call the done() function in the mocha unit test. When the assertion fails, it is at least shown in the HTML output. However, the assertion message is not as clear as in a normal test. Also, the technique still seems messy to me.

Since updating of vue component is done asynchronously you would need to use
// Inspect the generated HTML after a state update
it('updates the rendered message when vm.message updates', done => {
const vm = new Vue(MyComponent).$mount()
vm.message = 'foo'
// wait a "tick" after state change before asserting DOM updates
Vue.nextTick(() => {
expect(vm.$el.textContent).toBe('foo')
done()
})
})
Taken from official docs.

Related

Polymer not Defined in Web Component Tester

I am trying to run two tests with web-component-tester interactively on a Polymer 2 project in Chrome v69, web-component-test v 6.5.0, and webcomponentsjs v 2.1.3. The first super basic test passes, and the second fails with the following error:
Error: Polymer is not defined flush at shop-home.test.html:36
<html>
<head>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>shop-home</title>
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<script>void(0)</script>
<script src="/bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="/bower_components/test-fixture/test-fixture.html">
<link rel="import" href="../shop-home.html">
</head>
<body>
<test-fixture id="basic">
<template>
<shop-home></shop-home>
</template>
</test-fixture>
<script>
suite('shop-home tests', () => {
var home;
setup(() => {
home = fixture('basic');
});
test('super basic test', (done) => {
flush(() => {
console.log('what a great test')
done();
});
});
test('load mission statement', (done) => {
flush(() => {
let ms = Polymer.dom(home.root).querySelector('mission-statement');
assert.exists(ms, 'mission statement is neither `null` nor `undefined`');
done();
});
});
});
When running via the command line the error gives more detail:
Error: Error thrown outside of test function: document.getElementById(fixtureId).create is not a function. (In 'document.getElementById(fixtureId).create(model)', 'document.getElementById(fixtureId).create' is undefined)
at shop-home.html:25, 1 failed tests, Error thrown outside of test function: document.getElementById(...).create is not a function
at shop-home.html:25
How can I prevent this and run a proper unit test?

Jasmine breaks when instantiating class

I'm trying to write tests for our Angular 2 app with Jasmine. Followed a few tutorials, tried a lot. It works with basic tests, but once I make an instance of a component or try to mock it I just get no testresults. According to Angular Doc it's 'That's Jasmine saying "things are so bad that I'm not running any tests."'
Strangely enough, BlobViewModel does work. Whenever I comment or delete the 'this.const = new Constants();' it works again. Tried with multiple classes, always get the same results.. No logs/errors in chrome.
We're using Angular RC4 with Jasmine 2.4.1.
This is my .spec file:
import {Component, OnInit, OnDestroy } from "#angular/core";
import {Router} from '#angular/router';
import { Constants } from './shared/app.constants';
describe('component test', () => {
beforeEach(function () {
this.const = new Constants(); // THIS BREAKS IT
});
it('Tests', () => {
//Tests come here
//this.const.Signalr();
});
});
describe('1st tests', () => {
it('true is true', () => expect(true).toEqual(true));});
describe('BlobViewModel', () => {
var id = 1;
var localhost = "http//localhost";
var fullpath = "http//fullpathtoapplication.com";
var printername = "Printy print";
var papersize = "A4";
var blobmodel = new BlobViewModel(id, localhost, fullpath, printername, papersize);
it('BlobviewModel aanmaken', () => {
expect(blobmodel.ID).toEqual(id);
expect(blobmodel.FullLocalUrl).toEqual(localhost);
expect(blobmodel.FullPath).toEqual(fullpath);
expect(blobmodel.PrinterName).toEqual(printername);
expect(blobmodel.PaperSize).toEqual(papersize);
});
});
HTML file for the .spec runner:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>Ng App Unit Tests</title>
<link rel="stylesheet" href="../js/jasmine-core/lib/jasmine-core/jasmine.css">
<script src="../js/jasmine-core/lib/jasmine-core/jasmine.js"></script>
<script src="../js/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
<script src="../js/jasmine-core/lib/jasmine-core/boot.js"></script>
</head>
<body>
<!-- #1. add the system.js library -->
<script src="../js/systemjs/dist/system.src.js"></script>
<script src="../app/Systemjs.config.js"></script>
<script>
// #2. Configure systemjs to use the .js extension
// for imports from the app folder
System.config({
packages: {
'../app': { defaultExtension: 'js' }
}
});
// #3. Import the spec file explicitly
System.import('../app/file.spec.js')
// #4. wait for all imports to load ...
// then re-execute `window.onload` which
// triggers the Jasmine test-runner start
// or explain what went wrong.
.then(window.onload)
.catch(console.error.bind(console));
</script>
</body>
</html>
In the end I figured it out, had to import the "Reflect-metadata" package in the html file:
<script src="../js/reflect-metadata/Reflect.js"></script>

Using Mocha, Chai and Sinon to test a delayed action

I want to create a unit test for an asynchronous process. I have created a bare-bones mock up of the situation, using a timeout to delay an alert. (The real process will be the loading of a JS file on the fly).
I'm just beginning with Mocha, Chai and Sinon. I've created a folder called vendor alongside my HTML file. This contains the latest versions of mocha.css, mocha.js, chai.js and sinon.js.
The code below works fine if I comment out the setTimeout() call. How should I alter it so that the sinon.assert... calls will wait for the delayed action to occur?
<!DOCTYPE html>
<head>
<title>Delayed alert test</title>
</head>
<body>
<div id="mocha"><p>Index</p></div>
<div id="messages"></div>
<div id="fixtures"></div>
<link rel="stylesheet" type="text/css" href="vendor/mocha.css" />
<script src="vendor/mocha.js"></script>
<script src="vendor/chai.js"></script>
<script src="vendor/sinon.js"></script>
<script>
mocha.setup('bdd')
var spy = sinon.spy(window, 'alert')
describe("Test", function() {
describe("#alert", function() {
it("should show an alert", function(done) {
this.timeout(5000)
setTimeout(function () { // Works if these 2 lines...
alert ("Once")
alert ("Twice")
}, 2000) // are commented out
sinon.assert.called(spy)
sinon.assert.calledTwice(spy)
sinon.assert.calledWithExactly(spy, "Once")
sinon.assert.calledWithExactly(spy, "Twice")
done()
});
});
})
mocha.run();
</script>
</body>
</html>
Your assertions and done() (if the test ever got there) are being called as soon as the timeout is setup.
The test sets a timeout
this.timeout(5000)
Schedules your test to run in 2 seconds, and moves on immediately.
setTimeout(...
Checks an assertion that fails
sinon.assert.called(spy)
Then exits before the setTimeout has a chance to run.
The assertions need to run after the setTimeout has completed and as we are in a browser, the asynchronous assertions need to be captured in a try/catch block so the actual exception can be passed back to mocha via done().
Most asynchronous API's allow you to pass a "callback" function in, which is called after the asynchronous tasks completion which is normally the function you put your assertions/done in.
In your case it's a little more literal...
describe("Test", function() {
describe("#alert", function() {
it("should show an alert", function(done) {
this.timeout(5000)
var stub = sinon.stub(window, 'alert')
var assertion = function(){
try {
sinon.assert.called(stub)
sinon.assert.calledTwice(stub)
sinon.assert.calledWithExactly(stub, "Oce")
sinon.assert.calledWithExactly(stub, "Twice")
done()
} catch ( e ) {
done( e )
}
}
setTimeout(function () {
alert ("Once")
alert ("Twice")
assertion()
}, 2000)
});
});
})
https://jsfiddle.net/6w4p7rxz/
Note, I changed the spy to stub for less clicking. Also use chai! Maybe sinon-chai as well to make things easier to read.

Polymer unit test: dom-repeat is not rendered when "ready" is called

I have the following unit test for my custom polymer component:
<head>
<meta charset="UTF-8">
<title>survey</title>
<script src="../bower_components/webcomponentsjs/webcomponents.js"></script>
<script src="/web-component-tester/browser.js"></script>
<script src="../bower_components/test-fixture/test-fixture-mocha.js"></script>
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/test-fixture/test-fixture.html">
<link rel="import" href="../bower_components/iron-test-helpers/iron-test-helpers.html">
<link rel="import" href="../views/components/survey.html">
</head>
<body>
<test-fixture id="Network">
<template>
<survey></survey>
</template>
</test-fixture>
<script>
describe('<survey>', function() {
var survey;
describe('network', function() {
beforeEach(function(done) {
survey = fixture('Network');
})
it('should work', function() {
expect(survey.$.dog).to.exist;
});
});
});
</script>
And the following custom polymer survey component:
<link rel="import" href="../../bower_components/paper-checkbox/paper-checkbox.html">
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="survey">
<template>
<h3 class="text-center">Tell us about yourself!</h3>
<div class="form-group">
<label>I'm a...</label>
<array-selector id="imaSelector" items="{{ima}}" selected="{{imaSelected}}" multi toggle></array-selector>
<template is="dom-repeat" id="imaList" items="{{ima}}">
<div class="checkbox">
<paper-checkbox id="{{item.id}}" on-iron-change="toggleIma">{{item.name}}</paper-checkbox>
</div>
</template>
</div>
</template>
</dom-module>
<script>
Polymer({
is: 'survey',
properties: {
ima: {
type: Array,
value: function() {
return [ {
name: 'House Cat',
id: 'houseCat'
}, {
name: 'Basic Dog',
id: 'dog'
}, {
name: 'Swimming Fish',
id: 'fish'
}];
}
},
},
toggleIma: function(e) {
var item = this.$.imaList.itemForElement(e.target);
if (item) {
this.$.imaSelector.select(item.id);
}
}
})
</script>
This test will fail, because the local dom is not initialized, due to the fact that I'm using a dom-repeat element.
How do I want until the local dom is stamped?
There are two parts to this. Waiting for the async render, and finding the node.
For the render: Either listen for the dom-change event from the dom-repeat template, or call the render() method on the dom-repeat to force a synchronous render.
In unit tests, you probably just want to call render().
For finding the node--this.$ is only populated with statically created elements (not, for example, elements from a dom-if or dom-repeat template), as described in the docs. This is a frequent source of confusion.
You can use the this.$$ convenience method to query a local DOM element by selector, so you could do something like this:
survey.$.imaList.render();
expect(survey.$$(#dog)).to.exist;
You could return a Promise instead of expecting something immediately:
it('should work', function() {
return expect(survey.$.dog).should.eventually.exist();
});
See http://mochajs.org/#asynchronous-code for more information.
This seems to be a Polymer issue. The issue is I was trying to use the this.$ selector to reference dynamically created nodes. However, the polymer documentation explicitly states that the this.$ will only include statically created nodes, not dynamically created nodes.
See the note in this link. This is for the 0.5 version, but I am assuming this is the same in the 1.0 version. If there are any other know solutions than those mentioned in the link, I would love to hear them.
https://www.polymer-project.org/0.5/docs/polymer/polymer.html#automatic-node-finding
Note the final solution, looks something like this:
describe('network', function() {
beforeEach(function(done) {
survey = fixture('Network');
flush(function(){
done()
});
})
it('should work', function() {
expect(survey.querySelector('#dog')).to.exist;
});
});
Note that flush() is needed to ensure the dom is loaded.
https://www.polymer-project.org/0.5/articles/unit-testing-elements.html#wct-specific-helpers

Testing Angular With Mocha and Chai

I want to test my angular app with Yeoman which use Mocha with Phantom and Chai for assertion.
But when i run any sample test case the test case do not run properly it shows PhantomJs timed out due to missing Mocha run() call.Non angular Cases are working fine in test case.
<!doctype html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Mocha Spec Runner</title>
<link rel="stylesheet" href="lib/mocha/mocha.css">
</head>
<body>
<div id="mocha"></div>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>
<script src="lib/mocha/mocha.js"></script>
<script>mocha.setup('bdd')</script>
<script src="lib/chai.js"></script>
<script>
expect = chai.expect;
assert = chai.assert;
</script>
<script>
function addSum(num1, num2) {
return num1 + num2;
}
</script>
<script>
(function() {
describe('Give it some context', function() {
it('should simulate promise', inject(function ($q, $rootScope) {
assert.notStrictEqual(3, '3', 'no coercion for strict equality');
/* var deferred = $q.defer();
var promise = deferred.promise;
var resolvedValue;
promise.then(function(value) { resolvedValue = value; });
expect(resolvedValue).to.be.undefined;
// Simulate resolving of promise
deferred.resolve(123);
// Note that the 'then' function does not get called synchronously.
// This is because we want the promise API to always be async, whether or not
// it got called synchronously or asynchronously.
expect(resolvedValue).to.be.undefined
// Propagate promise resolution to 'then' functions using $apply().
$rootScope.$apply();
expect(resolvedValue).to.equal(123);*/
}));
});
})();
</script>
<!-- trigger the mocha runner -->
<script src="runner/mocha.js"></script>
</body>
</html>
Have you tried using protractor? It has been developed specifically for testing end to end angularjs apps (by the angularjs team). https://github.com/angular/protractor
It has it's own runner, which you install with:
npm install protractor -g
and then the runner is executed with:
protractor /configfile.cfg
No need for an HTML page to run the tests.
The config file is quite simple (you can see the options in the source code).
With that, you'll have the spec defined as:
// myTest.js
describe('angularjs homepage', function() {
it('should greet the named user', function() {
browser.get('http://www.angularjs.org');
element(by.model('yourName')).sendKeys('Julie');
var greeting = element(by.binding('yourName'));
expect(greeting.getText()).toEqual('Hello Julie!');
});
});