I am trying to set up a unit test as following :
import Vue from 'vue'
import ChangeTitleComponent from '#/components/ChangeTitleComponent'
import store from '#/vuex/store'
describe('ChangeTitleComponent.vue', () => {
describe('changeTitle', () => {
var component
beforeEach(() => {
var vm = new Vue({
template: '<div><h2>{{ title }}</h2> <change-title-component :title="title" :id="id"></change-title-component></div>',
components: {
ChangeTitleComponent
},
props: ['title', 'id'],
store
}).$mount()
component = vm.$refs.changetitlecomponent
})
component.title = 'Groceries'
component.id = '1'
it('should log the component', () => {
console.log('CHANGE TITLE COMPONENT: ', component)
})
})
})
but when I run it, I get an error ...
Uncaught TypeError: Cannot set property 'title' of undefined Uncaught TypeError: Cannot set property 'title' of undefined
Why my component is 'undefined ' ?
thanks for feedback
The main problem is that you forgot to add ref attribute to the component in template, so in fact property changetitlecomponent doesn't exist on vm.$refs.
This should fix problem.
<change-title-component ref="changetitlecomponent" :title="title" :id="id"></change-title-component>
Found by author: Setting the properties to the component object should
be moved into the it block of unit test.
Related
I am using jest for unit testing in nuxt js
I have mounted hook like this
async mounted(){
try{
var response = await this.$axios.get("api_url here");
this.result = response.data;
} catch(e){
console.log("Exception: ",e)
}
}
when i do unit test for it my code is . utnit.spec.js
jest.mock("axios", () => ({
get: () => Promise.resolve({ data: [{ val: 1 }] })
}));
import { mount } from '#vue/test-utils';
import file from '../filefile';
import axios from "axios";
describe('file', () => {
test('check comp. working correctly', () => {
var wrapper = mount(file);
afterEach(() => {
wrapper.destroy()
})
})
})
I am getting this warn there and there is no data in the results
Exception: TypeError: Cannot read property 'get' of undefined
at VueComponent.mounted
how do I know what is the problem here, is this I can not access axios in the unit file Is there any specific way to test Axios in mounted hook
The error means that it's this.$axios.get that is not available, not axios.get. The component relies on Axios plugin that is commonly installed in Vue application entry point or Nuxt configuration.
It can be installed for localVue Vue instance in tests, or be supplied directly to the component:
var wrapper = mount(file, { mocks: { $axios: axios } });
Also, the mock will fail if Axios is used as axios() somewhere because default import is expected to be a function:
jest.mock("axios", () => Object.assign(
jest.fn(),
{ get: jest.fn() }
));
axios.get is Jest spy, the implementation is mocked per test depending on the use and isn't limited to hard-coded Promise.resolve({ data: ... }) supplied in the mock.
I want to unit test with Jest and Enzyme if my <Text /> tag correctly receives props.header as text.
Usually I was able to test the content of the <Text /> tag like this:
it("should render a label", () => {
expect(wrapper.find(Text).contains("submit")).toBe(true);
});
But as soon as I pass an object this is no longer possible. Let me show you:
const createTestProps = props => ({
header: SOME_CONSTANT,
...props
});
...
let wrapper;
let props;
beforeEach(() => {
props = createTestProps();
wrapper = shallow(<MyList {...props} loaded={false} />);
});
it("should render a header", () => {
expect(wrapper.find(Text).contains(props.header)).toBe(true);
});
This fails with the following error message:
● MyList › rendering › still loading › should render a header
expect(received).toBe(expected) // Object.is equality
Expected: true
Received: false
How could I test this using Jest and Enzyme?
Edit
I found out that it has something to do passing a constant to the props. If I hardcode the value of props like this:
const createTestProps = props => ({
header: "Some hardcoded value",
...props
});
The test also passes. Is there a way to make this work even with a constant?
I am writing some tests for my Angular2 application according to the documentation but I have run into a problem which I can't seem to fix. I get the following error when trying to launch the spec runner:
Failed: This test module uses the component CategoriesComponent which is using a "templateUrl", but they were never compiled. Please call "TestBed.compileComponents" before your test.
I understand this is happening as I am using a seperate template file for the template within the component but I jhave tried multilpe solutions which don't seem to work.
Here is my component under test:
import { Component } from "#angular/core";
#Component({
selector: 'categories-component',
templateUrl: '/app/views/catalog/categories/categories-dashboard.html',
moduleId: module.id
})
export class CategoriesComponent {
title: 'Categories;
}
The categories-dashboard.html file:
<h1>{{ title }}</h1>
and my testing module for the component:
import {TestBed, ComponentFixture, ComponentFixtureAutoDetect, async} from "#angular/core/testing";
import { By} from "#angular/platform-browser";
import { CategoriesComponent } from "../../../../components/catalog/categories/CategoriesComponent";
import { DebugElement } from "#angular/core";
let comp: CategoriesComponent;
let fixture: ComponentFixture<CategoriesComponent>;
let de: DebugElement;
let el: HTMLElement;
describe('BannerComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CategoriesComponent ],
providers: [
{ provide: ComponentFixtureAutoDetect, useValue: true }
]
});
TestBed.compileComponents();
fixture = TestBed.createComponent(CategoriesComponent);
comp = fixture.componentInstance; // BannerComponent test instance
// query for the title <h1> by CSS element selector
de = fixture.debugElement.query(By.css('h1'));
el = de.nativeElement;
}));
it('should display original title', () => {
expect(el.textContent).toContain(comp.title);
});
});
I have tried to implement TestBed.compileComponents() into into the component but wherever I put it it doesn't seem to work.
Can anyone see why this error is occurring or point me in the directoin of a solution?
Thanks!
compileComponents resolves asynchronously (as it makes an XHR for the template), so it returns a promise. You should handle anything requiring the resolution of the promise, inside of the then callback of the promise
TestBed.compileComponents().then(() =>{
fixture = TestBed.createComponent(CategoriesComponent);
comp = fixture.componentInstance; // BannerComponent test instance
// query for the title <h1> by CSS element selector
de = fixture.debugElement.query(By.css('h1'));
el = de.nativeElement;
});
I am implementing a wizard component in Angular 2 RC4, and now I am trying to write som unit tests. Unit testing in Angular 2 is starting to get well documented, but I simply cannot find out how to mock the result of a content query in the component.
The app has 2 components (in addition to the app component), WizardComponent and WizardStepComponent. The app component (app.ts) defines the wizard and the steps in its template:
<div>
<fa-wizard>
<fa-wizard-step stepTitle="First step">step 1 content</fa-wizard-step>
<fa-wizard-step stepTitle="Second step">step 2 content</fa-wizard-step>
<fa-wizard-step stepTitle="Third step">step 3 content</fa-wizard-step>
</fa-wizard>
</div>
The WizardComponent (wizard-component.ts) gets a reference to the steps by using a ContentChildren query.
#Component({
selector: 'fa-wizard',
template: `<div *ngFor="let step of steps">
<ng-content></ng-content>
</div>
<div><button (click)="cycleSteps()">Cycle steps</button></div>`
})
export class WizardComponent implements AfterContentInit {
#ContentChildren(WizardStepComponent) steps: QueryList<WizardStepComponent>;
....
}
The problem is how to mock the steps variable in the unit test:
describe('Wizard component', () => {
it('should set first step active on init', async(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
return tcb
.createAsync(WizardComponent)
.then( (fixture) =>{
let nativeElement = fixture.nativeElement;
let testComponent: WizardComponent = fixture.componentInstance;
//how to initialize testComponent.steps with mock data?
fixture.detectChanges();
expect(fixture.componentInstance.steps[0].active).toBe(true);
});
})));
});
I have created a plunker implementing a very simple wizard demonstrating the problem. The wizard-component.spec.ts file contains the unit test.
If anyone can point me in the right direction, I would greatly appreciate it.
Thanks to drewmoore's answer in this question, I have been able to get this working.
The key is to create a wrapper component for testing, which specifies the wizard and the wizard steps in it's template. Angular will then do the content query for you and populate the variable.
Edit: Implementation is for Angular 6.0.0-beta.3
My full test implementation looks like this:
//We need to wrap the WizardComponent in this component when testing, to have the wizard steps initialized
#Component({
selector: 'test-cmp',
template: `<fa-wizard>
<fa-wizard-step stepTitle="step1"></fa-wizard-step>
<fa-wizard-step stepTitle="step2"></fa-wizard-step>
</fa-wizard>`,
})
class TestWrapperComponent { }
describe('Wizard component', () => {
let component: WizardComponent;
let fixture: ComponentFixture<TestWrapperComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
schemas: [ NO_ERRORS_SCHEMA ],
declarations: [
TestWrapperComponent,
WizardComponent,
WizardStepComponent
],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TestWrapperComponent);
component = fixture.debugElement.children[0].componentInstance;
});
it('should set first step active on init', () => {
expect(component.steps[0].active).toBe(true);
expect(component.steps.length).toBe(3);
});
});
If you have better/other solutions, you are very welcome to add you answer as well. I'll leave the question open for some time.
For anybody coming to this question recently, things have changed slightly and there is a different way to do this, which I find a bit easier. It is different because it uses a template reference and #ViewChild to access the component under test rather than fixture.debugElement.children[0].componentInstance. Also, the syntax has changed.
Let's say we have a select component that requires an option template to be passed in. And we want to test that our ngAfterContentInit method throws an error if that option template is not provided.
Here is a minimal version of that component:
#Component({
selector: 'my-select',
template: `
<div>
<ng-template
*ngFor="let option of options"
[ngTemplateOutlet]="optionTemplate"
[ngOutletContext]="{$implicit: option}">
</ng-template>
</div>
`
})
export class MySelectComponent<T> implements AfterContentInit {
#Input() options: T[];
#ContentChild('option') optionTemplate: TemplateRef<any>;
ngAfterContentInit() {
if (!this.optionTemplate) {
throw new Error('Missing option template!');
}
}
}
First, create a WrapperComponent which contains the component under test, like so:
#Component({
template: `
<my-select [options]="[1, 2, 3]">
<ng-template #option let-number>
<p>{{ number }}</p>
</ng-template>
</my-select>
`
})
class WrapperComponent {
#ViewChild(MySelectComponent) mySelect: MySelectComponent<number>;
}
Note the use of the #ViewChild decorator in the test component. That gives access to MySelectComponent by name as a property on the TestComponent class. Then in the test setup, declare both the TestComponent and the MySelectComponent.
describe('MySelectComponent', () => {
let component: MySelectComponent<number>;
let fixture: ComponentFixture<WrapperComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
/*
Declare both the TestComponent and the component you want to
test.
*/
declarations: [
TestComponent,
MySelectComponent
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(WrapperComponent);
/*
Access the component you really want to test via the
ElementRef property on the WrapperComponent.
*/
component = fixture.componentInstance.mySelect;
});
/*
Then test the component as normal.
*/
describe('ngAfterContentInit', () => {
component.optionTemplate = undefined;
expect(() => component.ngAfterContentInit())
.toThrowError('Missing option template!');
});
});
#Component({
selector: 'test-cmp',
template: `<wizard>
<wizard-step [title]="'step1'"></wizard-step>
<wizard-step [title]="'step2'"></wizard-step>
<wizard-step [title]="'step3'"></wizard-step>
</wizard>`,
})
class TestWrapperComponent {
}
describe('Wizard Component', () => {
let component: WizardComponent;
let fixture: ComponentFixture<TestWrapperComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [SharedModule],
schemas: [NO_ERRORS_SCHEMA],
declarations: [TestWrapperComponent]
});
}));
beforeEach(() => {
fixture = TestBed.createComponent(TestWrapperComponent);
component = fixture.debugElement.children[0].componentInstance;
fixture.detectChanges();
});
describe('Wizard component', () => {
it('Should create wizard', () => {
expect(component).toBeTruthy();
});
});
});
I have a simple angular2 component as defined below. And I'm looking to create a unit testing with karma, jasmine to run through this component.
#Component({
selector: 'property',
template: require('./property.component.html'),
directives: [Panel],
providers: [ConfigService]});
export class PropertyComponent {
config:any;
constructor(config:ConfigService) {
this.config = config.getConfig();
}
}
This is my testing spec file.
describe('property component', () => {
it('should have property page title', injectAsync([TestComponentBuilder], (tcb) => {
return tcb.createAsync(PropertyComponent).then((fixture) => {
let propertyComp = fixture.componentInstance,
element = fixture.nativeElement;
expect(element.querySelector('h1').innerText).toBe('property page');
});
}));
})
However I got a list of weird errors... I'm guessing this is due to the ConfigService Provider in the PropertyComponent, because when I removed the provider dependency, it went through.
Does anyone know how to deal with the dependency Providers?
Thanks!
errors:
_instantiateProvider#angular2-seed/config/spec-bundle.js:23435:38
_new#angular2-seed/config/spec-bundle.js:23424:42
getObjByKeyId#angular2-seed/config/spec-bundle.js:22937:38
_getByKeyDefault#angular2-seed/config/spec-bundle.js:23641:51
_getByKey#angular2-seed/config/spec-bundle.js:23587:42
_getByDependency#angular2-seed/config/spec-bundle.js:23573:35
_instantiate#angular2-seed/config/spec-bundle.js:23463:53
_instantiateProvider#angular2-seed/config/spec-bundle.js:23435:38
_new#angular2-seed/config/spec-bundle.js:23424:42
instantiateProvider#angular2-seed/config/spec-bundle.js:22924:35
init#angular2-seed/config/spec-bundle.js:34694:44
AppElement#angular2-seed/config/spec-bundle.js:34371:33
viewFactory_HostPropertyComponent0
createRootHostView#angular2-seed/config/spec-bundle.js:35741:48
You need to use beforeEachProviders in this case:
import {beforeEachProviders, describe, it, expect} from 'angular2/testing';
//...other imports...
describe('property component', () => {
beforeEachProviders(()=> [
ConfigService, //if you don't need to mock
provide(ConfigService, {useClass:MockConfigService}) // more typical
]);
it('should have property page title', injectAsync([TestComponentBuilder], (tcb) => {
return tcb.createAsync(PropertyComponent).then((fixture) => {
//expectations...
});
}));
})
Note that you need to import angular's patched describe, it, expect functions along with beforeEachProvidersfrom angular2/testing. I emphasize this because it's easy to forget to do that, and it results in failures with rather unintuitive messages.