I have a component that emit a value when call to a function,
how can I test if the #output emit a value ?
this is my function
#Output() emitDetail = new EventEmitter();
emitDetailFn() {
this.emitDetail .emit(false);
}
and this is my test
it('Detailfn should be called',()=>{
let emitted: boolean;
component.emitDetail .subscribe(value => {
emitted = value
})
component.emitDetailFn();
expect(emitted).toEqual(false)
})
but the coverage is red still in
In your unit test, you invoke the function emitDetailFn, the reported code coverage however relates to the function emitDetailFnl.
Nevertheless, your unit test could be written as follows:
it('should emit detail false',() => {
spyOn(component.emitDetail, 'emit');
component.emitDetailFn();
expect(component.emitDetail.emit).toHaveBeenCalledWith(false);
});
For further details, please consult spyOn and matchers from the Jasmine documentation.
Related
I am attempting to unit test a custom RxJS operator. The operator is very simple, it uses RetryWhen to retry a failed HTTP request, but has a delay and will only retry when the HTTP Error is in the 500 range. Using jasmine, and this is in an Angular application.
I've looked at this:
rxjs unit test with retryWhen
Unfortunately, updating the SpyOn call doesn't seem to change the returned observable on successive retries. Each time it retries it is retrying with the original spyon Value.
I have also looked at a bunch of rxjs marble examples, none of which seem to work. I am not sure it is possible to use rxjs marbles here, because (AFAIK) there is no way to simulate a situation where you first submit an errored observable, then submit a successful observable on subsequent tries.
The code is basically a clone of this:
https://blog.angularindepth.com/retry-failed-http-requests-in-angular-f5959d486294
export function delayedRetry(delayMS: number, maxRetry) {
let retries = maxRetry;
return (src: Observable<any>) =>
src.pipe(
retryWhen((errors: Observable<any>) => errors.pipe(
delay(delayMS),
mergeMap(error =>
(retries-- > 0 && error.status >= 500) ? of(error) : throwError(error))
))
);
}
I would like to be able to demonstrate that it can subscribe to an observable that returns an error on the first attempt, but then returns a successful response. The end subscription should show whatever success value the observable emits.
Thank you in advance for any insights.
try use this observable as source observable to test
const source = (called,successAt)=>{
return defer(()=>{
if(called<successAt){
called++
return throwError({status:500})
}
else return of(true)
})
}
test
this.delayedRetry(1000,3)(source(0,3)).subscribe()
To test the retry functionality, you need a observable which emits different events each time you call it. For example:
let alreadyCalled = false;
const spy = spyOn<any>(TestBed.inject(MyService), 'getObservable').and.returnValue(
new Observable((observer) => {
if (alreadyCalled) {
observer.next(message);
}
alreadyCalled = true;
observer.error('error message');
})
);
This observable will emit an error first and after that a next event.
You can check, if your observable got the message like this:
it('should retry on error', async(done) => {
let alreadyCalled = false;
const spy = spyOn<any>(TestBed.inject(MyDependencyService), 'getObservable').and.returnValue(
new Observable((observer) => {
if (alreadyCalled) {
observer.next(message);
}
alreadyCalled = true;
observer.error('error message');
})
);
const observer = {
next: (result) => {
expect(result.value).toBe(expectedResult);
done();
}
}
subscription = service.methodUnderTest(observer);
expect(spy).toHaveBeenCalledTimes(1);
}
Building on a previous answer I have been using this, which gives you more control over what's returned.
const source = (observables) => {
let count = 0;
return defer(() => {
return observables[count++];
});
};
Which can then be used like this
const obsA = source([
throwError({status: 500}),
of(1),
]);
Or it can then be used with rxjs marbles like
const obsA = source([
cold('--#', null, { status: 500 }),
cold('--(a|)', { a: 1 }),
]);
I'm trying to write a basic unit test to work on the function below, but can't get it to work. How do I test that something like a proper npm-express response is returned?
I already looked at Using Sinon to stub chained Mongoose calls, https://codeutopia.net/blog/2016/06/10/mongoose-models-and-unit-tests-the-definitive-guide/, and Unit Test with Mongoose, but still can't figure it out. My current best guess, and the resulting error, is below the function to be tested. If possible, I don't want to use anything but Mocha, Sinon, and Chai.expect (i.e. not sinon-mongoose, chai-as-expected, etc.). Any other advice, like what else I can/should test here, is welcome. Thank you!
The function to be tested:
function testGetOneProfile(user_id, res) {
Profiles
.findOne(user_id)
.exec()
.then( (profile) => {
let name = profile.user_name,
skills = profile.skills.join('\n'),
data = { 'name': name, 'skills': skills };
return res
.status(200)
.send(data);
})
.catch( (err) => console.log('Error:', err));
}
My current best-guess unit test:
const mongoose = require('mongoose'),
sinon = require('sinon'),
chai = require('chai'),
expect = chai.expect,
Profile = require('../models/profileModel'),
foo = require('../bin/foo');
mongoose.Promise = global.Promise;
describe('testGetOneProfile', function() {
beforeEach( function() {
sinon.stub(Profile, 'findOne');
});
afterEach( function() {
Profile.findOne.restore();
});
it('should send a response', function() {
let mock_user_id = 'U5YEHNYBS';
let expectedModel = {
user_id: 'U5YEHNYBS',
user_name: 'gus',
skills: [ 'JavaScript', 'Node.js', 'Java', 'Fitness', 'Riding', 'backend']
};
let expectedResponse = {
'name': 'gus',
'skills': 'JavaScript, Node.js, Java, Fitness, Riding, backend'
};
let res = {
send: sinon.stub(),
status: sinon.stub()
};
sinon.stub(mongoose.Query.prototype, 'exec').yields(null, expectedResponse);
Profile.findOne.returns(expectedModel);
foo.testGetOneProfile(mock_user_id, res);
sinon.assert.calledWith(res.send, expectedResponse);
});
});
The test message:
1) testGetOneProfile should send a response:
TypeError: Profiles.findOne(...).exec is not a function
at Object.testGetOneProfile (bin\foo.js:187:10)
at Context.<anonymous> (test\foo.test.js:99:12)
This is a bit of a tricky scenario. The problem here is that the findOne stub in your test returns the model object - instead, it needs to return an object which contains a property exec which in turn is a promise-returning function that finally resolves into the model value... yeah, as mentioned, it's a bit tricky :)
Something like this:
const findOneResult = {
exec: sinon.stub().resolves(expectedModel)
}
Profile.findOne.returns(findOneResult);
You also need to have the status function on the response object return an object containing a send function
//if we set up the stub to return the res object
//it returns the necessary func
res.status.returns(res);
I think you shouldn't need to change anything else in the test and it might work like that. Note that you sinon 2.0 or newer for the resolves function to exist on the stub (or you can use sinon-as-promised with sinon 1.x)
This post goes into a bit more detail on how you can deal with complex objects like that:
https://codeutopia.net/blog/2016/05/23/sinon-js-quick-tip-how-to-stubmock-complex-objects-such-as-dom-objects/
Simple question:
I've followed the tutorial: here where I have the following code:
this.term.valueChanges
.debounceTime(400)
.distinctUntilChanged()
.switchMap(term => this.wikipediaService.search(term));
Where term is a Control inheriting from AbstractControl, how can I trigger the valueChanges observable property, so that I can perform unit tests?
I think I was experiencing the same issue.
I was trying to test that heroService.searchHeroes was called:
ngOnInit() {
this.heroes$ = this.searchTerms
.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap((term: string) => this.heroService.searchHeroes(term))
);
}
I was able to fix it by only calling tick(500) once
it('should call HeroService.searchHeroes', fakeAsync(() => {
spyOn(heroService, 'searchHeroes').and.returnValue(component.heroes$);
let searchField: DebugElement = fixture.debugElement.query(By.css('#search-box'));
searchField.nativeElement.value = 'i';
searchField.nativeElement.dispatchEvent(new Event('keyup'));
tick(500);
expect(heroService.searchHeroes).toHaveBeenCalled();
}));
I found a work around in the following issue:
https://github.com/angular/angular/issues/8251
Essentially there's buggy behavior, but you can get a basic test working with code like this:
it("filter input changed should update filter string",
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
let fixture = tcb.createFakeAsync(MyComponent);
let component = fixture.componentInstance as MyComponent;
fixture.detectChanges();
// Trigger a new value to emit on the valueChanges observable
//
component.filter.updateValue("testing");
// There's buggy behavior with multiple async events so we
// need to run tick() twice to get our desired behavior
// https://github.com/angular/angular/issues/8251
//
tick();
tick(500);
// Now we can inspect the results of the subscription in the
// original component
//
expect(component.filterString).toBe("testing");
})));
...and this is with a component that looks like this:
export class MyComponent {
filter = new Control();
/** The current filter string */
filterString: string;
constructor() {
// Subscribe to changes in the filter string
//
this.filter.valueChanges
.debounceTime(200)
.distinctUntilChanged()
.subscribe((filterString: string) => {
this.filterString = filterString;
});
}
}
I am working with "Smart Table" and will be using their example plugin where a checkbox selects a row in a table: http://lorenzofox3.github.io/smart-table-website/#section-custom
I am writing a unit test for this directive, code below, this is failing. Has anyone written a unit test for this code or could help direct me as to where I am going wrong and if I am actually testing the correct logic?
Directive:
myApp.directive('csSelect', function () {
return {
require: '^stTable',
template: '',
scope: {
row: '=csSelect'
},
link: function (scope, element, attr, ctrl) {
element.bind('change', function (evt) {
scope.$apply(function () {
ctrl.select(scope.row, 'multiple');
});
});
scope.$watch('row.isSelected', function (newValue, oldValue) {
if (newValue === true) {
element.parent().addClass('st-selected');
} else {
element.parent().removeClass('st-selected');
}
});
}
};
});
Unit test:
describe('csSelect',function(){
var scope, element, attr, ctrl;
beforeEach(module('myApp.selectorresult'));
beforeEach(inject(function($rootScope, $compile) {
elm = angular.element(
'<td cs-select="row" class="ng-isolate-scope">' +
'<input type="checkbox">' +
'</td>');
scope = $rootScope;
$compile(elm)(scope);
scope.$digest();
}));
it('should create selectable input',function(){
console.log(elm.find('input'));
var checkbox = elm.find('input');
expect(checkbox.length).toBe(1);
});
});
You need to mock out the stTableController with $controllerProvider before you set up beforeEach(inject...
Check out the test spec for the pagination directive (https://github.com/lorenzofox3/Smart-Table/blob/master/test/spec/stPagination.spec.js), which also requires 'stTable'. It's a good example of how to provide the 'stTableController' with the functions you need from it.
For anyone still having this issue. I hope this helps.
I was struggling with this for ages. I tried mocking the stTableController, I tried adding the vendor files to the karma.conf.js files among other things but could not get any tests to pass.
It seemed that when I removed the require: '^stTable' the tests would pass no problem, but with it in, all tests would fail. I couldn't remove this as this would break my code.
So what I finally found was that all I had to do was add st-table to my element in the spec.js file.
So if my element was
var element = angular.element('<my-component></my-component');
I had to make it
var element = angular.element('<my-component st-table></my-component>');
After that, all tests were passing.
I am having problems with testing sometimes. The test is executed, i can even get the results of the action but the test doesn't show its green or red message telling me if the test run successfully or not. (28/28 test methods complete: 28 passes, 0 fails, 95 assertions and 0 exceptions...), the time, peak memory... etc.
For example. Only if i try to execute this test it crashes:
public function testGetHotVideo() {
$result = $this->testAction("/posts/getHotVideo/");
$this->assertEquals($result, 'GoG_Tv5G17M');
}
It calls this method:
public function getHotVideo(){
$video = $this->Post->getHotVideo();
return $video[0][0]['video'];
}
And it returns the video string correctly. So it can even be printed on the test variable $result.
What's going on here?
UPDATE
I have also notice it occurs the same when i call a method which redirects to some view.
In this case, with the default delete created with Cake Bake:
public function delete($id = null){
$this->Comment->id = $id;
if (!$this->Comment->exists()) {
throw new NotFoundException(__('Invalid comment'));
}
if ($this->Comment->delete()) {
$this->Session->setFlash(__('Comment deleted'));
return $this->redirect(array('controller' => 'posts', 'action' => 'index'));
}
$this->Session->setFlash(__('Comment was not deleted'));
return $this->redirect(array('controller' => 'posts', 'action' => 'index'));
}
Is this SimpleTest (cakephp <= 1.3) or PHPUnit (>1.3)?
In case its SimpleTest you have to handle the redirects in your controller tests (read Mark Storys article on controller testing). Basically you build a test class which inherits from the controller class you are testing and than you overwrite the redirect and some other methods (here some from the article):
class TestPostsController extends PostsController {
...
function redirect($url, $status = null, $exit = true) {
$this->redirectUrl = $url;
}
...
}
than you use this modified controller in your test case.
With CakePHP 2 and PHPUnit I even had some issues with calling the header() function in some of the layouts, which broke the html structure and the css of the test-sides.