I am unit testing a method where I do two different things with same parameter.I want to check which parameters are passed in both the statements. and either they are same.
Method I am testing
doWork(value: boolean) {
if (value) {
add(value);
} else {
delete(value);
}
}
I know I can test with "toHaveBeenCalledWith" for a specific value.
But here I would like to test where it's the same for add and for delete.
I think you can take advantage of callFake on Jasmine. callFake calls the function you supply every time that function is called.
Something like this.
it('should call add and delete with the same value', () => {
const addParameter;
const deleteParameter;
spyOn(component, 'add').and.callFake(value => addParameter = value);
spyOn(component, 'delete').and.callFake(value => deleteParamter = value);
component.add(5);
component.delete(5);
expect(addParameter).toBe(deleteParameter);
});
Of course that is a contrived example but something like that should get you going. I also wrote it as if you were using Angular 2+.
Related
I'm trying to mock CSV Stringify with AngularJS...but I just want to grab the two parameters passed in and use them later in the test to test if some options were correct.
it("converts latitude and longitude correctly", function() {
this.CSV._stringifyPromise._setResolveMode("instant");
var csvFeatures, csvOptions;
this.CSV.stringify.and.callFake(function(csvFeaturesInStringify,
csvOptionsInStringify) {
csvFeatures = csvFeaturesInStringify;
csvOptions = csvOptionsInStringify;
});
this.featureExportCSVService.buildCsvFile(this.features, "Test");
expect(this.csvOptions[0].LONG).toEqual(this.features[0].geometry.coordinates[
0].toString());
expect(this.csvOptions[0].LAT).toEqual(this.features[0].geometry.coordinates[
1].toString());
});
I believe CSV stringify returns a promise in the non-test code as it's called...
CSV.stringify(csvObject.features, csvObject.options)
.then(function(result) { ... }
I assumed it would just be return the result in my call fake but that doesn't seem to work.
this.CSV.stringify.and.callFake(function(csvFeaturesInStringify,
csvOptionsInStringify) {
expect(...);
expect(...);
return "test,blah,blah";
});
I tried use $q.defer().resolve(); but $q was undefined. Doing a return it just says undefined is not a constructor
How would I just resolve this function to just return a dummy String or even just end the test here and evaluate my expect() calls?
Need to call the return like this with a then function and a successCallback....
this.CSV.stringify.and.callFake(function(csvFeaturesInStringify, csvOptionsInStringify) {
csvFeatures = csvFeaturesInStringify;
return {
then: function(successCallback) {
successCallback("geometry,LAT,LONG,name,marker-color");
}
};
Then after the function call that eventually gets to that method, you need to put the expect blocks there not in here.
this.featureExportCSVService.buildCsvFile(this.features, "Test");
expect(this.csvOptions[0].LONG).toEqual(this.features[0].geometry.coordinates[
0].toString());
expect(this.csvOptions[0].LAT).toEqual(this.features[0].geometry.coordinates[
1].toString());
I am writing tests using Jasmine for my angular application. All the tests are passing. My class looks like follows:
class xyz implements ng.IComponentController {
private myList: ng.IPromise<MyList[]> ;
//declare necessary variables
/* #ngInject */
constructor(private ListService: ListService,
) {
this.myList = this.ListService.getList();
}
public onChange(): void {
this.isNameUnique(this.name).then(function(unique){
scope.isUnique = unique;
scope.errorNameInput = !reg.test(scope.name) || !scope.isUnique;
scope.myFunction({
//do something
});
});
}
public isNameUnique(name: string): ng.IPromise<boolean> {
return this.myList
.then(
(names) => {
_.mapValues(names, function(name){
return name.uuid.toLowerCase();
});
return (_.findIndex(names, { uuid : uuid.toLowerCase() }) === -1) ? true : false;
});
}
}
Here, I am using ListService to pre-populate my list in the constructor itself (so it calls the service only once). Then, in my onChange method, I am checking
if a name is unique or not. The isNameUnique is returning a boolean promise.
Now, I'm trying to get 100% coverage for my test. I'm getting confused about testing isNameUnique method here. My first test is:
(Assuming myList is a json similar to response I will get from service)
this.$scope.myFunction = jasmine.createSpy('myFunction');
it('should ...', function() {
this.view.find(NAME_INPUT).val('blue').change(); // my view element.
this.getList.resolve(myList);
this.controller.isNameUnique('blue').then(function (unique) {
expect(unique).toEqual(false); //since blue is already in my json
expect(this.controller.errorNameInput).toEqual(true); //since its not unique, errornameinput will be set to true
expect(this.$scope.myFunction).toHaveBeenCalled();
});
});
I would expect this test to cover the line: scope.errorNameInput = !reg.test(scope.name) || !scope.isUnique and invocation of myFunction() but it still shows uncovered. Not sure why.
Please let me know if you see anything else wrong since I'm quite new to Angular and Jasmine. Thanks.
You need to call $scope.$digest() to cause your promise to resolve in your test. There is a handy tutorial that discusses this in depth here
Hope that helps!
export interface IFooModel {
foo:string;
fooFunction(fooProp:string):void;
}
export class FooCtrl implements IFooModel {
constructor(public foo:string){
}
fooFunction(fooProp:string):void{
}
}
The code above is fairly standard. My question is , when i want to access foo:string in the function i have to do this
fooFunction(fooProp:string):void{
var fooAgain = this.foo;
// Pretend i set it up properly for $mdDialog to work
this.$mdDialog.show(options).then(function(answer: boolean) {
if (answer) {
// fooAgain works
// this.foo does not work
}
}
Why do i have to set this.foo to a variable in order to access it inside another function , instead of just writing this.foo ? In some functions i end up for about 4 variables declarations that are already declared in the constructor. Is there maybe a better way to this? I get the feels that there is too much repeat code in the controller.
Yes this is a problem in Javascript, but thankfully in TypeScript this problem is no more thanks to fat arrows! Yay!
Fat arrows are like anonymous functions but handle the this variable for you.
Let me show you:
fooFunction(fooProp:string):void {
// Pretend i set it up properly for $mdDialog to work
this.$mdDialog.show(options).then((answer: boolean) => {
if (answer) {
this.foo = "";
}
});
}
Using fat arrows this will now compile to this in JS:
FooCtrl.prototype.fooFunction = function (fooProp) {
var _this = this;
// Pretend i set it up properly for $mdDialog to work
this.$mdDialog.show(options).then(function (answer) {
if (answer) {
_this.foo = "";
}
});
};
So Typescript automatically creates a _this variable for you, so that you no longer have the problem. Pretty neat if you ask me.
Here's the documentation for Arrow functions:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions
The only reason to place the value in a local variable prior to calling a function is because you expect the class to go out of scope (i.e. something will happen to change the scope, such as a callback or asynchronous operation).
By putting the value in a local variable it becomes part of the closure for the function and the value is stored alongside the function itself.
This is one of the trickiest aspects of JavaScript - scope is kinda funky.
There are other options to solve this problem, including fat arrows...
() => {
// this.foo is now preserved
}
Or call / apply which allow you to set the scope.
I'm trying to return a value from a WebDriver promise within a Protractor solution using TypeScript, but I'm getting undefined as response.
get nameInput(): string {
var value: string;
this.nameElement.getAttribute('value').then(v => value = v);
return value;
}
In the above case it seems like the function is not waiting for the promise to return, therefore I tried moving away from the getter style and declared the return type as WebDriver's promise:
getNameInput(): webdriver.promise.Promise<string> {
var nameElement = element(by.id('name'));
return nameElement.getText().then(v => { return v });
}
But I'm getting Function as the return instead of the value of v. Seems like the promise is not being unwrapped by Jasmine's expect, as it happens when running it in JS.
I know I can run the promise directly within the expect, but ideally I would create all the function's logic outside of any expectations, so that I can then feed the expectation the function call with any (if any) parameters only - instead of polluting the test case with promise logic.
Any ideas?
You don't need to resolve the promises, just return them:
getNameInput(): webdriver.promise.Promise<string> {
var nameElement = element(by.id('name'));
return nameElement.getText();
}
Then you need to have a real value returned from the getNameInput() - resolve it in your test:
getNameInput().then(v => { console.log(v) });
Note that, you also let expect() resolve it implicitly:
expect(getNameInput()).toEqual("Expected Value");
I'm writing an application using angular and typescript.
I'm using ng-grid and I have to handle the afterSelectionChange event.
I tried to set the event handler in two ways
this.$scope.settoriGridOptions.afterSelectionChange = this.afterSelectionChange;
where this.afterSelectionChange is a method of the controller class,
and
this.$scope.settoriGridOptions.afterSelectionChange = (... ) => {};
including the code inside, but in both cases the this pointer is incorrect and I cannot access to the services of the controller.
how can I fix this?
after a more tests and reading a few articles I see that the problem is the implicit passing of the this pointer as parameter in the function call.
if I write
$scope.filtroSoluzione = this.filtroSoluzione;
when called the this pointer is set to null, but if I write
$scope.filtroSoluzione = () => { return this.filtroSoluzione() };
or
$scope.filtroSoluzione = () => { .. inline code ... };
the this pointer I set correctly.
How can I have a more consistent behavior? I don't like to write always the code inside because this makes the class harder to read and navigate
thanks,
Luca
Thanks for the extra information in your edits, I now see the problem.
class foo {
public afterSelectionChange = () => {
console.log(this);
}
}
When you declare your function like I did above, your this is the instance instead of what you are seeing know because it captures the this variable. It comes with a cost though, because now it creates a new afterSelectionChange function for every instance of your class. In this case I think it is still what you want though.
var foo = (function () {
function foo() {
var _this = this;
this.afterSelectionChange = function () {
console.log(_this);
};
}
foo.prototype.baz = function () {
console.log(this);
};
return foo;
})();
In the above code-gen you can see the difference when declaring the function with name = () => {} and the normal way.
Another solutions might be this:
this.$scope.settoriGridOptions.afterSelectionChange = this.afterSelectionChange.bind(this);
But I don't find that really nice either... (this should work with the normal public afterSelectionChange() {} declaration you are used to.