While writing unit tests for a function that handles a angular material 2 dialog using the example code from material 2 i run into problems.
I'm a Jasmine newbie but I didn't had problems to write unit test before.
I have to test the result of the afterClose function but i can't get the handle to dialogRef.
Could it be a problem how the material2 dialog API is engineered?
let dialogRef = this.dialog.open(ExtractPageDialog, {
width: this.EXPORT_DIALOG_WIDTH,
data: {
document: this.document
}
});
dialogRef.afterClosed().subscribe((result: any) => {
if (result) {
let fileId = this.document.fileId;
this.docProvider.extractPage(this.document.fileId, result.fromPage, result.toPage).subscribe(() => {
() => { //totest },
(error) => { //totest }
});
} else {
//totest
}
});
DOCS:
https://material.angular.io/components/component/dialog
one solution could be to split the subscribed function in multiple functions and test them
dialogRef.afterClosed().subscribe(this.functionName);
functionName(result: any) {
if (result) {
let fileId = this.document.fileId;
this.docProvider.extractPage(this.document.fileId, result.fromPage, result.toPage).subscribe(() => {
() => { //totest },
(error) => { //totest }
});
} else {
//totest
}
}
}
Related
caps.component.ts:->
if (Acc.accOpen()) {
this.keyboardShowListener = Keyboard.addListener('keyboardDidShow', () => {
this.ngZone.run(() => {
this.isKeyboardVisible = true;
})
});
}
how can I write unit test case for this?
I was struggling with a test issue for my custom useLazyQuery hook. My first test is passing but the second one is failing. What am doing wrong for the second test?
Here is the useLazyFetchCoin.tsx
export const useLazyFetchCoin = () => {
const [coins, setCoins] = useState<ICoin[]>([]);
useEffect(() => {
const coins = localStorage.getItem('coinsInfo');
if (coins) {
setCoins(JSON.parse(coins));
}
}, []);
const [getData, { loading, error }] = useLazyQuery(GET_COIN_PRICE_QUERY, {
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true,
onCompleted: (data) => {
const hasSameCoin = coins.some((f) => f.id === data.markets[0]?.id);
if (data.markets.length && !hasSameCoin) {
const allCoins = [...coins, data.markets[0]];
setCoins(allCoins);
localStorage.setItem('coinsInfo', JSON.stringify(allCoins));
} else if (data.markets.length <= 0) {
alertNotification('Coin not found !', Notification.ERROR);
}
if (hasSameCoin) {
alertNotification('This coin already exists on your list !', Notification.WARNING);
}
}
});
return { coins, setCoins, getData, loading, error };
};
Here is the test file
describe('useLazyFetchCoin custom hook', () => {
const QueryMock = [
{
request: {
query: GET_COIN_PRICE_QUERY,
variables: { code: 'BNB' }
},
result: {
data: {
markets: [
{
id: 'binance_bnb_eur',
baseSymbol: 'BNB',
ticker: {
lastPrice: '414.90000000'
}
}
]
}
}
}
];
const QueryWrongCodeMock = [
{
request: {
query: GET_COIN_PRICE_QUERY,
variables: { code: 'asd' }
},
result: {
data: {
markets: []
}
}
}
];
function getHookWrapper(mocks: any, code: string) {
const wrapper = ({ children }: any) => (
<MockedProvider mocks={mocks} addTypename={false}>
{children}
</MockedProvider>
);
const { result, waitForNextUpdate } = renderHook(() => useLazyFetchCoin(), {
wrapper
});
expect(typeof result.current.coins).toBe('object');
expect(result.current.loading).toBeFalsy();
expect(result.current.error).toBeUndefined();
// call the lazy function
act(() => {
result.current.getData({
variables: { code }
});
});
return { result, waitForNextUpdate };
}
it('should return an array of coins', async () => {
// Working correctly
const { result, waitForNextUpdate } = getHookWrapper(QueryMock, 'BNB');
await waitForNextUpdate();
expect(result.current.loading).toBeFalsy();
expect(result.current.coins[0]).toEqual({
id: 'binance_bnb_eur',
baseSymbol: 'BNB',
ticker: {
lastPrice: '414.90000000'
}
});
});
it('should return an empty array when requesting a wrong code', async () => {
// Not working
const { result, waitForNextUpdate } = getHookWrapper(QueryWrongCodeMock, 'asd');
await waitForNextUpdate();
expect(result.current.loading).toBeFalsy();
expect(result.current.coins[0]).toEqual([]);
});
});
I got this error message for the second test.
Expected: []
Received: {"baseSymbol": "BNB", "id": "binance_bnb_eur", "ticker": {"lastPrice": "414.90000000"}}
I don't get it because I'm using different queries for each test.
Also, the second test should receive an empty array when you pass a wrong code such as 'asd'.
How can write a proper test for it?
I fixed the problem. When I was changing the order test, It worked correctly.
I added a clear mock function for it.
clear mock
I have a function that returns and treats a promise, I need to cover the return that is inside then but I don't know how I can do this, I'm currently trying as follows:
confirmRemoveUser(user: IUser) {
this.modalService
.open('Confirma a exclusão do usuário selecionado?', {
titleText: 'Confirmando exclusão',
confirmButtonText: 'Sim',
cancelButtonText: 'Cancelar',
closeButtonText: 'Fechar',
buttonType: 'danger'
})
.result.then(
(result: BentoModalConfirmationCloseReason) => {
if (result === BentoModalConfirmationCloseReason.Confirm) {
if (this.removeUser(user)) {
this.toastService.open('Usuário excluído com sucesso!', { type: 'success', close: true });
} else {
this.toastService.open('Falha ao excluir o usuário!', { type: 'warning', close: true, duration: 0 });
}
}
}
);
}
I'm currently using callthrough () and imagine that with some parameter I can get the promise but I don't know how:
it('Given_ConfirmRemoveUser_When_UserStepIsCalled_Then_UserIsRemoved', (done) => {
component.selectedJob = {
};
component.selectedArea = {
};
component.users = [{
}];
spyOn(modalService, 'open').withArgs('This is modal msg').and.callThrough();
component.confirmRemoveUser(component.users[0]);
expect(modalService.open).toHaveBeenCalled();
done();
});
And my coverage is like the image below:
Image here!
UPDATE
New Error
Your test should work when it is rewritten as follows:
it('Given_ConfirmRemoveUser_When_UserStepIsCalled_Then_UserIsRemoved', (done) => {
spyOn(modalService, 'open').and.returnValue(Promise.resolve(BentoModalConfirmationCloseReason.Confirm));
spyOn(toastService, 'open').and.stub();
component.confirmRemoveUser(component.users[0])
.then(r => {
expect(toastService.open).toHaveBeenCalled();
done();
})
.catch(e => fail(e));
});
You probably also want to know what will be displayed in the toast. Therefore it makes sense to rather use expect(toastService.open).toHaveBeenCalledWith(?);.
UPDATE
Above solution only works if confirmRemoveUser would return a Promise.
confirmRemoveUser(user: IUser) {
return this.modalService
...
In your case, the use of the done function does not make sense. You need to use async and await.
it('Given_ConfirmRemoveUser_When_UserStepIsCalled_Then_UserIsRemoved', async () => {
spyOn(modalService, 'open').and.returnValue(Promise.resolve(BentoModalConfirmationCloseReason.Confirm));
spyOn(toastService, 'open').and.stub();
await component.confirmRemoveUser(component.users[0]);
expect(toastService.open).toHaveBeenCalled();
});
The same can be achieved with fakeAsync and flush.
import { fakeAsync, flush } from '#angular/core/testing';
...
it('Given_ConfirmRemoveUser_When_UserStepIsCalled_Then_UserIsRemoved', fakeAsync(() => {
spyOn(modalService, 'open').and.returnValue(Promise.resolve(BentoModalConfirmationCloseReason.Confirm));
spyOn(toastService, 'open').and.stub();
component.confirmRemoveUser(component.users[0]);
flush();
expect(toastService.open).toHaveBeenCalled();
}));
unit testing code in spec.ts
it('Should test LoadMenus method', () => {
let arrays = [{ "Id": 1, "MenuType": "A00", "MenuName": "Search", "UserDetail": { "UserName": "rajendranra" }
alert(11);// executed
this.modalNota.LoadMenus(arrays, 'A00', 'A', '0');
alert(12);// does not execute
expect(this.modalNota.cocmenuitems[0].MenuName).toBe('Search');
});
Component method which am testing
LoadMenus(lstMenu, menustring, submenustartstring, submenuendstring) {
alert(menustring);
this.subitems = [];
let menu = lstMenu.find(myObj => myObj.MenuType === menustring);
if (menu !== null) {
this.menufilter = lstMenu.filter(myObj => myObj.MenuType.startsWith(submenustartstring))
.filter(x => x.MenuType.endsWith(submenuendstring));
if (this.menufilter.length > 1) {
for (let menuType = 0; menuType < this.menufilter.length; menuType++) {
if (this.menufilter[menuType].MenuType !== menustring) {
this.subitems.push({
label: this.menufilter[menuType].MenuName,
routerLink: [this.GetLink(this.menufilter[menuType].MenuName)]
});
}
}
alert(menu.MenuName);
this.cocmenuitems.push({ label: menu.MenuName, routerLink: [this.GetLink(menu.MenuName)], items: this.subitems });
} else {
alert(menu.MenuName);
this.cocmenuitems.push({ label: menu.MenuName, routerLink: [this.GetLink(menu.MenuName)] });
}
}
}
This unit test showing the result is failed. the main problem is Alert alert(12); does not fire. Why my alert(12) does not hit after this this.modalNota.LoadMenus(arrays, 'A00', 'A', '0'); component code is executed??
Am struggling a whole day for this. kindly give me the solution.
I am testing a angular component and the code is
ngOnInit(): void {
this.getNar();
}
getNar(): void {
let self = this;
this.dashboardService.getNar().subscribe(
res => self.narIds = res.narIds,
error => self.error = error,
function () {
self.narIds.forEach(element => {
// Some Code
});
}
);
}
The Service provider for this i.e Dashboard Service is
getNar(): Observable<any> {
return this.http.get(Config.Api.GetNar + '1/nar').map((res: Response) => res.json());
}
And my Test cases are:
let res = '"narIds":[{"id":1,"narId":"104034-1","narName":"SDLC Platform"},{"id":2,"narId":"64829-1","narName":"EMS-EMS"}]';
describe('Application Health Component', () => {
beforeEach( async(() => {
TestBed.configureTestingModule({
providers: [MockBackend, DashboardService],
imports: [ChartsModule, SlimScrollModule, HttpModule],
declarations: [CompletedFilterPipe, ApplicationHealthComponent]
})
.compileComponents()
.then(createComponent);
}));
it('should call the getNar when ngOnInit is called', async(() => {
spyOn(dashboardService, 'getNar').and.returnValue(Observable.of(res));
comp.ngOnInit();
expect(dashboardService.getNar).toHaveBeenCalled();
}));
});
function createComponent() {
fixture = TestBed.createComponent(ApplicationHealthComponent);
comp = fixture.componentInstance;
dashboardService = fixture.debugElement.injector.get(DashboardService);
};
The problem I am getting is the test case gives an error that forEach is undefined.
The error message is not that forEach function is not defined, it's that your object "self.narIds" is undefined. Fairly sure this is due to the way you declared your onComplete function in Observable.subscribe
related to this Rx Subscribe OnComplete fires but cannot use the data
change your
function () {
self.narIds.forEach(element => {
// Some Code
});
code to
() => {
self.narIds.forEach(element => {
// Some Code
});