Im really new to Testing. I set up TedBed. Now i try to test my first Component "MyApp" which looks like this:
#Component({
templateUrl: 'app.html'
})
export class MyApp implements OnInit {
rootPage:any = HomePage;
constructor(
private app:App,
) {
}
pageTwo() {
let navs = this.app.getActiveNavs();
let nav = navs[0]
nav.push(AnotherPage);
}
ngOnInit() {
}
}
Template:
<ion-menu [content]="content" persistent="true">
<ion-header>
<ion-navbar>
<ion-title>
MyApp
</ion-title>
</ion-navbar>
</ion-header>
<ion-content>
<ion-list>
<button menuClose (click)="pageTwo()" ion-item>
<ion-label>
AnotherPage
</ion-label>
</button>
</ion-list>
</ion-content>
</ion-menu>
<ion-nav [root]="rootPage" #content swipeBackEnabled="false"></ion-nav>
As you can see i, i have a mehtod that navigate to "AnotherPage" if the menu-button is clicked. This is working - Now i try to test this behavior:
describe('MyApp Component', () => {
let fixture;
let component;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MyApp],
imports: [
IonicModule.forRoot(MyApp),
],
providers: [
{provide: NavController, useFactory: () => NavControllerMock.instance()},
{provide: App, useFactory: () => AppMock.instance()},
]
})
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();
component = fixture.componentInstance;
});
it('should be created', () => {
expect(component instanceof MyApp).toBe(true);
});
it('should navigate to Antoherpage if pageTwo() is called', () => {
component.pageTwo()
// ...
});
});
I have no clue what i have to do, to test if the "AnotherPage" is pushed to the Navstack after method "pageTwo" is called.
Thanks in advance
So, after some research i got it working. It was easier than i thought:
it('should navigate to Antoherpage if pageTwo() is called', () => {
component.pageTwo()
expect(nav.push).toHaveBeenCalledWith(AnotherPage);
});
since i use the ionic-mocks package (https://github.com/stonelasley/ionic-mocks). I dont have to spy on the nav.push manually.
Related
What I want to do
I wanna make test that is redirect by click.
if possible, I wanna make test not only method called assertion but redirect "URL" assertion.
Test Target Code
<p class="target" v-on:click="redirect">Click!</p>
<script>
export default {
methods: {
redirect() {
window.location.href = '/home'
},
}
}
Test Code
import { shallowMount } from '#vue/test-utils'
import redirectComponent from '../components/RedirectComponent.vue'
describe('Redirect component', () => {
const wrapper = shallowMount(redirectComponent)
it('Redirect By Click Test', () => {
wrapper.find('.target').trigger('click');
expect(window.location.href).toEqual('/home');
})
})
result
Expected: "/home"
Received: "http://localhost/"
How can I make redirect test?
window.location.href gives you the URL of the current page;
window.location.pathname will give you the path name of the current page, which I believe, is what you are looking for.
This is my Solution!
import { shallowMount } from '#vue/test-utils'
import redirectComponent from '../components/RedirectComponent.vue'
describe('Redirect component', () => {
const wrapper = shallowMount(redirectComponent)
Object.defineProperty(window, 'location', {
value: {
href: 'http://localhost',
},
configurable: true,
});
it('Redirect By Click Test', () => {
wrapper.find('.target').trigger('click');
expect(window.location.href).toEqual('/home');
})
})
I have a simple component (HelloComponent) and a couple of tests. First test shallow mounts the component, prints it (wrapper of the component) on the console, and finally calls destroy() api on it. And the second test just prints it without mounting it. I was expecting the second test to print undefined but it prints the same thing (full component markup) as first test. Is my expectation incorrect ?
<!-- HelloComponent.vue -->
<template>
<div>
Hello {{name}}!
</div>
</template>
<script lang="ts">
export default {
name: 'Hello',
data() {
return {
name: ''
};
},
methods: {
setName(name) {
this.name = name;
}
}
}
</script>
import { shallowMount } from '#vue/test-utils';
import HelloComponent from '#/HelloComponent.vue';
describe('Hello component unit tests', () => {
let wrapper;
describe('Set 1', () => {
it('should load component', () => {
wrapper = shallowMount(HelloComponent, {});
expect(wrapper.exists()).toBe(true);
wrapper.vm.setName('oomer');
console.log(wrapper.html());
wrapper.destroy();
});
});
describe('Set 2', () => {
it('should log empty component', () => {
expect(wrapper.vm.name).toEqual('oomer');
console.log(wrapper.html());
});
});
});
I am writing the angular (Karma-Jasmine) test cases and I want to navigate between the pages. How to navigate between pages using karma-Jasmine.
1) Test a component in which navigation is used: navigate method should be called when you do an action (assertion like toHaveBeenCalled OR toHaveBeenCalledWith)
it('should redirect the user to the users page after saving', () => {
let router = TestBed.get(Router);
let spy = spyOn(router, 'navigate');
component.save();
expect(spy).toHaveBeenCalledWith(['users'])
});
2) Also test your routes that proper component will be used
app.routes.spec.ts
import { routes } from './app.routes'
it('should contain a route for users', () => {
expect(routes).toContain({path: 'users', component: UserComponent})
});
3) You can use useValue for testing different activatedRouteParams (just configure then and pass to providers, see example).
Component ts file example:
ngOnInit() {
this.contextType = this.route.snapshot.paramMap.get('contextType');
this.contextId = this.route.snapshot.paramMap.get('contextId');
this.contextFriendlyId = this.route.snapshot.paramMap.get('contextFriendlyId');
}
Spec file (configureTestData is a function that allows you to pass different configurable values, in my case activatedRouteParams)
export function configureTestData(activatedRouteParams) {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SetUpComponent],
imports: [RouterTestingModule],
providers: [
{
provide: ActivatedRoute, useValue: activatedRouteParams
}
]
});
}));
}
describe('SetUp Component:Folder ', () => {
let component: SetUpComponent;
let fixture: ComponentFixture<SetUpComponent>;
configureTestData({
snapshot: {
paramMap: convertToParamMap({
contextType: 'Folder',
contextId: 'FX6C3F2EDE-DB25-BC3D-0F16-D984753C9D2E',
contextFriendlyId: 'FX17373'
})
}
});
beforeEach(() => {
fixture = TestBed.createComponent(SetUpComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create set up component for folder', () => {
expect(component).toBeTruthy();
});
it('should create alert with required properties', () => {
expect(component.contextType).toEqual('Folder);
.... etc
});
});
4) router-outlet and routerLink
Template file:
<nav>
<a routerLink="todos"></a>
</nav>
<router-outlet></router-outlet>
beforeEach(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes([])],
declarations: [AppComponent]
});
});
it('should have router outlet', () => {
let de = fixture.debugElement.query(By.directive(RouterOutlet));
expect(de).not.toBeNull();
});
it('should have a link to todos page', () => {
let debugElements = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref));
let index = debugElements.findIndex(de => de.properties['href'] === '/todos');
expect(index).toBeGreaterThan(-1);
});
5) Stub for ActivatedRoute where we can push params
component.ts
ngOnInit() {
this.route.params.subscribe(p => {
if (p['id'] === 0) {
this.router.navigate(['not-found']);
}
});
}
Spec file:
class RouterStub {
navigate(params) {}
}
class ActivatedRouteStub {
private subject = new Subject();
get params () {
return this.subject.asObservable();
}
push(value) {
this.subject.next(value);
}
}
describe('Navigation Testing', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [],
providers: [
{provide: Router, useClass: RouterStub},
{provide: ActivatedRoute, useClass: ActivatedRouteStub}
]
});
});
it('should navigate to invalid page when invalid params passed', () => {
let router = TestBed.get(Router);
let spy = spyOn(router, 'navigate');
let route: ActivatedRouteStub = TestBed.get(ActivatedRoute);
route.push({id: 0});
expect(spy).toHaveBeenCalledWith(['not-found'])
});
});
Trying to test whether two methods are called on a click event on two different buttons. The result returns that the mock method on the second event is not called.
The template is the following (JobsSearch.vue):
<template>
<b-input-group class="sm-2 mb-2 mt-2">
<b-form-input
:value="this.searchConfig.Keyword"
#input="this.updateJobsSearchConfig"
class="mr-2 rounded-0"
placeholder="Enter Search term..."
/>
<b-button
#click="searchJobs"
class="rounded-0 search-button"
variant="primary"
>
Search
</b-button>
<b-button
#click="resetFilter"
class="rounded-0 ml-2 reset-button"
variant="primary"
>
Reset
</b-button>
</b-input-group>
</template>
This is my JobsSearch.spec.js
import { shallowMount, createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
// BootstrapVue is necessary to recognize the custom HTML elements
import BootstrapVue from 'bootstrap-vue'
import JobsSearch from '#/components/jobs/JobsSearch.vue'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(BootstrapVue)
describe('JobsSearch.vue', () => {
let state
let store
let wrapper
let searchJobs
let resetFilter
let emitEvents
beforeEach(() => {
state = {
jobs: {
paged: {
size: 100,
page: 1
},
search: {
Keyword: '',
status: [],
ucode: []
}
}
}
store = new Vuex.Store({
state
})
searchJobs = jest.fn()
resetFilter = jest.fn()
emitEvents = jest.fn()
wrapper = shallowMount(JobsSearch, {
methods: {
searchJobs,
resetFilter,
emitEvents
},
localVue,
store })
})
afterEach(() => {
wrapper.destroy()
})
// START: Method tests
it('should call jobsSearch method on search button click event', () => {
wrapper.find('.search-button').trigger('click')
expect(searchJobs).toHaveBeenCalled()
})
it('should call resetFilter method on reset button click event', () => {
wrapper.find('.reset-button').trigger('click')
expect(resetFilter).toHaveBeenCalled()
})
// END: Method tests
})
I expect both searchJobs and resetFilter to be called, however, only the first test passes
Any ideas, please?
It seems like that the resetFilter() method is not stubbed correctly.
Adapt the second test as follows:
it('should call resetFilter method on reset button click event', () => {
const resetFilterStub = jest.fn();
wrapper.setMethods({ resetFilter: resetFilterStub });
wrapper.find('.reset-button').trigger('click')
expect(resetFilter).toHaveBeenCalled()
})
I want to test my RolesComponent listenGlobal Renderer method. RolesComponent code below.
import { Component, OnInit, Renderer, AfterViewInit } from '#angular/core';
import { Router } from '#angular/router';
import { AhanaService } from '../../services/ahana.service';
#Component({
selector: 'app-roles',
template: '<div><button roleId="1">test click</button></div>',
styleUrls: ['./roles.component.css']
})
export class RolesComponent implements AfterViewInit {
constructor(public router: Router, private ahanaService: AhanaService, private renderer: Renderer) {}
ngAfterViewInit(): void {
this.renderer.listenGlobal('document', 'click', (event) => {
if (event.target.hasAttribute("roleId")) {
var roleId = event.target.getAttribute('roleId')
// console.log('/configuration/update-role/' + roleId)
this.router.navigate(['/configuration/update-role/' + roleId]);
}
});
}
}
how to convert RolesComponent.spec.ts and ngAfterViewInit(): void { this.renderer.listenGlobal('document', 'click', (event) => { method call.
If you want to test that your click listener is behaving as expected, then you can manually call the ngAfterViewInit lifecycle hook from your test and assert that router.navigate() is called as expected. In order to do this, you can mock and spy on the router.navigate method and expect it to have been called with a particular url route. The result is very similar to this stack overflow answer.
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '#angular/core/testing';
import { Router } from '#angular/router';
import { RolesComponent } from './roles.component';
describe('RolesComponent', () => {
let component: RolesComponent;
let fixture: ComponentFixture<RolesComponent>;
const routerSpy = { navigate: jasmine.createSpy('navigate') }; // Create our router mock, which we will spy on
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RolesComponent ],
providers: [{ provide: Router, useValue: routerSpy }], // Register our mock router as a provider
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RolesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should register click listener', fakeAsync(() => {
component.ngAfterViewInit(); // Call the lifecycle hook
const button = fixture.debugElement.nativeElement.querySelector('button');
button.click(); // "Click" the button
tick();
expect(routerSpy.navigate).toHaveBeenCalledWith(['/configuration/update-role/1']); // Assert that our mock was called
}));
});