how to check if methods have been called jasmine unit test - javascript

I'm new to unit testing, and after reading the docs I'm still confused on how to do checks on asynchronous methods, as far as i understand I will need to use runs() and wait(). However, instead of doing that, I could use spyOn to check if the method has been called instead.
this is the code i want to test
$scope.createMedicalServices = function(){
if($scope.newTreatment.async_selected_treatment.id == null){
$scope.newTreatment.description = $scope.newTreatment.description_no_id;
}
else{
$scope.newTreatment.description = $scope.newTreatment.description;
}
$scope.newTreatment.no_cpt_code = $scope.newTreatment.no_cpt_code;
$scope.newTreatment.cash_price = $scope.newTreatment.cash_price_input;
$scope.newTreatment.average_price = $scope.newTreatment.average_price_input;
$scope.newTreatment.service = $scope.newTreatment.async_selected_treatment.service;
var returnPromiseObject;
if ($scope.newTreatment.no_cpt_code) {
returnPromiseObject = ProviderMedicalService.createNew($scope.newTreatment);
}
else if($scope.newTreatment.async_selected_treatment.id == null){
returnPromiseObject = ProviderMedicalService.createNewCPT($scope.newTreatment);
}
else{
returnPromiseObject = ProviderMedicalService.createExisting($scope.newTreatment);
}
returnPromiseObject.then(
function (value){
$scope.newTreatment.id = value.id;
$scope.provider_medical_services.push($scope.newTreatment);
},
function (error){
console.log(error);
});
};
$scope.searchTreatments = function(value){
return Treatment.find(value).then(function(values){
return values;
});
};
and this is what i wrote so far, but its not much as i have no idea how to test it.
it('search treament should have been called and return a value'),function(){
scope.searchTreatments();
}
it('it should create medical service after submitting the form'),function(){
scope.createMedicalServices();
runs
}
Basically, I am content if the method just gets called properly, as the createMedicalService function is actually executed the moment the user clicks on the submit form, as its part of ng-submit on the html file.
Help and suggestions greatly appreciated

Unit testing can be a little confusing at first. There are a few principles that help with unit testing.
Singleness of Purpose. A component should just do one thing. Consider breaking your code into smaller parts. Move logic out of controllers into factories or services. These can be tested individually.
Inversion of Control. Angularjs does a great job at providing IoC built in. Pass in your dependencies as stubs, fakes or mocks into your unit.
Only test the interface. It is a bad practice to test the internal call structure of a unit. This ends up not testing the functionality, but rather the implimentation. Instead think about this example.
You have a controller that gets a rest service passed in called nameService. This controller has a method called submit(first, last) that when called, should called the method post(args) on the nameService.
This would allow you to create a stub of the nameService, create a spy on the post() method and pass in the stub to the controller we are testing. Then you can simply call submit() on the controller and test that it called the service.
In essence, you test that if you input something into your unit, your unit will output it as expected.

Related

How do I access information about the currently running test case from the beforeEach function?

Using Protractor 5.1.2 and Jasmine2 for describing test cases, how does one get the current testcase/spec being run in the beforeEach method?
I would like to do some different setup based on which test case I'm running. I do not want to put these tests in different spec files with repeating code except for the little bit I want to change in the setup.
Example of what I'm looking for:
...
beforeEach(() => {
if(currentSpec/TestCase.name == "thisName") {
// Do a particular login specific to testcase.name
} else {
// Do a default login
}
});
...
My research into this brought up much older solutions (2+ years) that are very out of date and seem to keep saying that accessing the currently running testcase/spec is something they (protractor) try to keep hidden. I feel like wanting to do particular setup for a particular test case in a suite of test cases is not a unique thing. I could just be using the wrong search terms.
I am not sure how to do what you want with beforeEach(). But, I think you can get the same effect by using a helper file. This will allow you to setup a common file that any spec can reference so you can use a common set of functions. To set this up, you will:
Create a central file (I call mine util.js)
const helper = function(){
this.exampleFunction = function(num){
return num; //insert function here
}
this.exampleFunction2 = function(elem){
elem.click() //insert function here
}
}
Inside your spec.js file you will do:
const help = require('path/to/util.js');
const util = new help();
describe('Example with util',function(){
it('Should use util to click an element',function(){
let elem = $('div.yourItem');
util.exampleFunction2(elem);
});
});
You can then call these functions from any spec file. You would then be able to seperate your tests into seperate spec files, but have a common set of functions for the parts that are the same.
Another way to do this, without creating separate files is to just use a local function.
Example spec.js file:
describe('Should use functions',function(){
afterEach(function(){
$('button.logout').click();
)};
it('Should run test as user 1',function(){
$('#Username').sendKeys('User1');
$('#Password').sendKeys('Password1');
$('button.login).click();
doStuff();
)};
it('Should run test as user 2',function(){
$('#Username').sendKeys('User2');
$('#Password').sendKeys('Password2');
$('button.login').click();
doStuff();
)};
function doStuff(){
$('div.thing1').click();
$('div.thing2').click();
)};
)};
As per comments for multiple describes:
describe('Test with user 1',function(){
beforeEach(function(){
//login as user 1
});
it('Should do a thing',function(){
//does the thing as user 1
});
});
describe('Test with user 2',function(){
beforeEach(function(){
//login as user 2
});
it('Should do another thing',function(){
//does the other thing as user 2
});
});
The whole point of beforeEach is that it is the same for each test.
If you want to do different things, then they belong in the specific test.
Write a helper function and call it from the specific test if you want to have common functionality that does slightly different things depending on an argument.

Jasmine spyOn and how to use spies generally

I am starting out in JS unit testing and having a hard time getting my head around how to create meaningful tests with Jasmine spies.
it('should take an array of shopping items', function() {
spyOn(checkObj, 'getTotal');
checkObj.getTotal([1, 2, 3]);
expect(checkObj.getTotal).toHaveBeenCalledWith(jasmine.any(Array));
});
Using the above excerpt of test I created as an example I cant see how this is a useful test as my call to getTotal is hard coded inside the spec. But at the same time I would like to ensure the param passed is an array and not some other type...its the hard coding that I am sure is wrong somehow.
Could someone offer a bit of guidance on how I should think/approach this type of testing scenario
Well, spies are useful for a bit different scenario. Much depends on how you yourself define the scope of your unit test as well. If you do the minimal possible unit (i.e. method) then lets imagine the following:
var x = function() { }
x.prototype.f1 = function() {
//do something
},
x.prototype.f2 = function(){
// do something else
this.f1();
}
Now, in your unit test for f2 you are not interested in how f1 works inside. so, you make a spy on it:
var a = new x();
a.f1 = jasmine.createSpy("spy-on-f1");
expect(a.f1).not.toHaveBeenCalled();
a.f2();
expect(a.f1).toHaveBeenCalled();
For example, for angularjs applications, I often mock whole services with spies, just to isolate the algorithm in testing.
As a bonus, you can actually replace the real call with some fake function like this:
a.f1 = jasmine.createSpy("fake-spy").and.callFake(function(){
// do something predictible or return global variable that can be set externaly
});

node, unit-testing and mocking with sinon

So I am using a test suite of Chai, rewire, sinon, and sinon-chai to test some node javascript. This is my first time trying to set this up so I could use some pointers. The function I am trying to test looks like so :
UserRoles.get = function(ccUrl, namespace, environment, ccToken, authPath) {
var crowdControl = new CrowdControl(ccUrl, namespace, environment, ccToken, authPath);
return q.Promise(function(resolve, reject) {
crowdControl.get().then(resolve).fail(reject).done();
});
};
Inside a document that exports as UserRoles. So I have the initial set up working fine, where I am having troubles is mocking to test this function. I'm trying to mock the new CrowdContol part so my attempt to do that looks like so : https://jsfiddle.net/d5dczyuk/ .
so I'm trying out the
testHelpers.sinon.stub(CrowdControl, "UserRoles");
to intercept and stub
var CrowdControl = require('./crowdcontrol');
then just running
userRoles.get;
console.log(CrowdControl);
And it seems the stub is not being called ( it logs it's a stub but not that it has been called). I will also need to stub the crowdControl.get() hopefully too, however I was trying to get this simple part working first. Not sure what I need to be doing differently to get this to work here. This is my first time unit testing in node, I've done a bunch in angular where I could just "mock" the CrowdControl, but I'm not sure how it works in node.
Just to clarify I am just checking if CrowControl will be called with those vars passing in, should I just stub it? but I also want to mock the crowdControl so I can force what the get returns.
Edit: here is my second attempt : https://jsfiddle.net/5m5jwk5q/
I like to use proxyquire for this kind of testing. With proxyquire you can stub out require'd dependencies from the modules you're trying to test. So in your case you could do:
var crowdControlSpy = sinon.spy();
// Makes sure that when ./user-roles tries to require ./crowdcontrol
// our controlled spy is passed, instead of the actual module.
var UserRoles = proxyquire('./user-roles', {
'./crowdcontrol': crowdControlSpy
});
UserRoles.get(...);
expect(crowdControlSpy).to.have.been.called;

How to properly test an AngularJS Controller Function

We just started implementing jasmine tests in our AngularJS project and I have a question:
We want to test this controller function:
$scope.deleteClick = function () {
$scope.processing = true;
peopleNotesSrv.deleteNote($scope.currentOperator.operatorId, $scope.noteId, $scope.deleteSuccessCallback, $scope.deleteErrorCallback);
};
We wrote this following test:
it('deleteClick should pass proper parameters to peopleNoteSrv', function () {
$controllerConstructor('PeopleNoteEditCtrl', { $scope: $scope });
$scope.noteId = 5;
expect(function () { $scope.deleteClick(); }).not.toThrow();
});
This test makes sure that when we call the $scope.deleteClick() function that $scope.processing is set to true and that the call to peopleNotesSrv doesn't throw any errors because of invalid arguments. We are testing the two callback functions in separate tests.
Should we be testing that the peopleNotesSrv.deleteNote function was called so the test is more explicit? The way this test is written right now it doesn't really tell someone what the deleteClick() function does under the hood and that seems to be incorrect.
Ask yourself what you'd do if you had developed it using TDD. It pretty much goes the direction Sam pointed out, but here are some examples:
Controller Tests
start writing a test which would expect a deleteClick to exist.
Expect deleteClick to setup the loading state (check for processing = true)
Test whether a service is injected into the controller (peopleNotesSrv)
Check whether deleteClick calls the service (as already mentioned via spies)
Verify that $scope.noteId and the other $scope.params are present and set
This is as far as it relates to the Controller. All the criteria whether it fails or throws errors etc. should be tested in a Service.spec. Since I don't know your service in detail here some examples
Service Tests
Ensure deleteNote exists
Check what happens if wrong number of arguments (less or more) are supplied
Make some positive tests (like your noteId = 5)
Make some negative tests
Ensure callbacks are properly called
... and so on.
Testing for validity in controllers doesn't make a lot of sense because than you'd need to do it for every Controller you have out there. By isolating the Service as a separate Unit of Test and ensure that it fulfills all the requirements you can just use it without testing. It's kinda the same as you never would test jQuery features or in case of Angular jQLite, since you simply expect them to do what they should :)
EDIT:
Make controller tests fail on service call
Pretty easy lets take this example. First we create our Service Test to ensure that the call fails if not the proper number of arguments is supplied:
describe('Service: peopleNoteSrv', function () {
// load the service's module
beforeEach(module('angularControllerServicecallApp'));
// instantiate service
var peopleNoteSrv;
beforeEach(inject(function (_peopleNoteSrv_) {
peopleNoteSrv = _peopleNoteSrv_;
}));
it('should throw error on false number of arguments', function () {
expect(function() { peopleNoteSrv.deleteNote('justOneParameter'); }).toThrow();
});
});
Now to ensure that the test passes lets create the error throwing part in our service method
angular.module('angularControllerServicecallApp')
.service('peopleNoteSrv', function peopleNoteSrv() {
this.deleteNote = function(param1, param2, param3) {
if(arguments.length !== 3)
throw Error('Invalid number of arguments supplied');
return "OK";
};
});
Now lets create 2 demo controllers, FirstCtrl will do it properly, but SecondCtrl should fail
angular.module('angularControllerServicecallApp')
.controller('FirstCtrl', function ($scope, peopleNoteSrv) {
$scope.doIt = function() {
return peopleNoteSrv.deleteNote('param1', 'param2', 'param3');
}
});
angular.module('angularControllerServicecallApp')
.controller('SecondCtrl', function ($scope, peopleNoteSrv) {
$scope.doIt = function() {
return peopleNoteSrv.deleteNote('onlyOneParameter');
}
});
And both controller as a demo have following test:
it('should call Service properly', function () {
expect(scope.doIt()).toBe("OK");
});
Karma now spits out something like this:
Error: Invalid number of arguments supplied
at [PATH]/app/scripts/services/peoplenotesrv.js:15
at [PATH]/app/scripts/controllers/second.js:13
at [PATH]/test/spec/controllers/second.js:20
Thus you exactly know that you missed to update SecondCtrl. Of course this should work for any of your tests consuming the Service method.
Hope that's what you meant.
I think the answer is that it depends.
There are two cases:
1 - You also have a suite of tests for the peopleNotesSrv service.
In this case I would leave this test as-is or check a few more things around the specific functionality of $scope.deleteClick(), such as if there are any watchers on $scope.processing that do anything specific regarding a .deleteClick() call.
2 - You do not have any tests for all the possible functionality for the peopleNotesSrv service.
In this case I would write a more explicit test that does check that the .deleteNote() actually performed it's job.
In my opinion you should really build tests up and try to not test the same thing in more than one place, as this adds extra work and could produce holes in the tests if you think, "Well I can just test this specific case when it gets called from a specific function that calls it."
What if you ever want to reuse that deletNote() as part of a bigger function in a different place?Then you need to write another test for the same code because it is being called from a different function.
So I would aim for case 1, this way you can write all your tests for that service and then trust that those tests cover the rest of this particular test. If you throw errors on bad input or for failures to actually delete a note, you should trust that other code to test what it was designed to test. This will greatly speed up your test-writing time and increase the chance that your tests cover all the cases. It also keeps all the tests for that service in the same place in your test code.
I think also a good question to start with is what kind of test is this? Unit Test or End-to-End test?
I was assuming it was a Unit Test for my answer, if it was an End-to-End test, then you might want to keep following the function calls to verify everything is happening as you expect.
Here are some links on Unit Tests, End-to-End tests, and a pretty good article about both and Angular.
What's the difference between unit, functional, acceptance, and integration tests? (End-to-End tests can also be called Integration test)
http://www.sitepoint.com/unit-and-e2e-testing-in-angularjs/

Revealing Module Pattern - Unit Testing with Jasmine

After a brief romance with the revealing module pattern I've come to realise a set-back when it comes to unit-testing modules. I cannot however decide if it is my approach to testing a module or whether there is some form of work-around.
Consider the following code:
var myWonderfulModule = (function () {
function publicMethodA (condition) {
if(condition === 'b') {
publicMethodB();
}
}
function publicMethodB () {
// ...
}
return {
methodA : publicMethodA,
methodB : publicMethodB
}
}());
If I wanted to test (using Jasmine) the various paths leading through publicMethodA to publicMethodB. I might write a small test like so:
it("should make a call to publicMethodB when condition is 'b'", function() {
spyOn(myWonderfulModule , 'publicMethodB');
myWonderfulModule.publicMethodA('b');
expect(myWonderfulModule.publicMethodB).toHaveBeenCalled();
});
If I understand correctly, there's a copy of publicMethodB within the closure that cannot be changed. Even if I change myWonderfulModule.publicMethodB afterwards:
myWonderfulModule.publicMethodB = undefined;
calling myWonderfulModule.publicMethodA will still run the original version of B.
The example above is of course simplified but there are plenty of scenarios I can think of where it would be convenient to unit test conditional paths through a method.
Is this a limitation of the revealing module pattern or simply a misuse of unit testing? If not what work-arounds are available to me? I'm considering moving to something like RequireJS or reverting back to non-modular code.
Any advice appreciated!
You cant test the intern methodes of a closure. And you also shouldn't spy on it. Think about about your module as a black box. You put something in and you get something out. All you should test is that the thing you get out of your module is the one that you expect.
Spying on methodes in your module makes not much sense. Think about it. You spy on it, the test passes. Now you change the functionality so it creates a bug, the test still passes cause the function is still called but you never mention the bug. If you just test the thing that cames out you dont need to spy on internal methodes cause, that they are called is implicite when the outcome of the module is what you expect.
So in your case there is no thing that goes in and nothing comes out. This makes not much sense but I believe that your module interacts with DOM or makes an ajax call. This are things that you can test (DOM) or you should spy on (ajax).
You should also make you self familiar with Inversion of Control and Dependency Injection. These are patterns that will make your modules much more easier to test.
If you use the keyword "this" when you call publicMethodB() from publicMethodA() it will work. For example:
var myWonderfulModule = (function () {
function publicMethodA (condition) {
if(condition === 'b') {
this.publicMethodB();
}
}
function publicMethodB () {
// ...
}
return {
methodA : publicMethodA,
methodB : publicMethodB
}
}());

Categories

Resources