How to vary spy results between each tests - javascript

I'm writing some tests for an Angular component that has a dependency on a service.
This service has a stageData public member and I want to test that the component behaves correctly depending on the content of stageData.
Since I need to mock the service I created a spy and defined what it would return for stageData but I can't change this in each tests.
describe('ProgressComponent', () => {
let component: ProgressComponent;
let fixture: ComponentFixture<ProgressComponent>;
let progressStageService: any;
const signStage = new StageCompletionModel();
const inkStage = new StageCompletionModel();
beforeEach(async(() => {
signStage.stage = Stage.Sign;
inkStage.stage = Stage.Ink;
progressStageService = jasmine.createSpyObj('ProgressStageService', ['getStageProgress']);
progressStageService.stageData = [signStage];
TestBed.configureTestingModule({
declarations: [ ProgressComponent ],
providers: [ { provide: ProgressStageService, useValue: progressStageService } ],
schemas: [ NO_ERRORS_SCHEMA ],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProgressComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should not display Ink stage when it is not in stageData', () => {
const callToAction: HTMLElement = fixture.nativeElement;
const stageLabels = callToAction.querySelectorAll('span.stage-label');
expect(Array.from(stageLabels).some(sl => sl.textContent.includes('Ink'))).toBeFalsy();
});
it('should display Ink stage when it is in stageData', () => {
const callToAction: HTMLElement = fixture.nativeElement;
const stageLabels = callToAction.querySelectorAll('span.stage-label');
progressStageService.stageData = [signStage, inkStage];
fixture.detectChanges();
expect(Array.from(stageLabels).some(sl => sl.textContent.includes('Ink'))).toBeTruthy();
});
});
I've tried many variations in my second test case but stageData always contains the data that it is set up with in the beforeEach.

So you have to split it between more describes when mocking the data. The first fixture.detectChanges in the 2nd beforeEach is what "bundles" the component and calls ngOnInit.
Try:
describe('ProgressComponent', () => {
let component: ProgressComponent;
let fixture: ComponentFixture<ProgressComponent>;
let progressStageService: any;
const signStage = new StageCompletionModel();
const inkStage = new StageCompletionModel();
beforeEach(async(() => {
signStage.stage = Stage.Sign;
inkStage.stage = Stage.Ink;
progressStageService = jasmine.createSpyObj('ProgressStageService', ['getStageProgress']);
TestBed.configureTestingModule({
declarations: [ ProgressComponent ],
providers: [ { provide: ProgressStageService, useValue: progressStageService } ],
schemas: [ NO_ERRORS_SCHEMA ],
})
.compileComponents();
}));
beforeEach(() => {
console.log('getting a handle on the fixture');
fixture = TestBed.createComponent(ProgressComponent);
console.log(fixture);
component = fixture.componentInstance;
// re-assign progressStageService to what was injected in the configureTestingModule
// If you're using Angular 9, this should be .inject and not .get
progressStageService = TestBed.get(ProgressStageService);
// fixture.detectChanges(); => Remove fixture.detectChanges here
});
describe('stageData is [signStage]', () => {
beforeEach(() => {
// assign stageData to the service that was injected
progressStageService.stageData = [signStage];
fixture.detectChanges();
});
it('should not display Ink stage when it is not in stageData', () => {
const callToAction: HTMLElement = fixture.nativeElement;
const stageLabels = callToAction.querySelectorAll('span.stage-label');
expect(Array.from(stageLabels).some(sl => sl.textContent.includes('Ink'))).toBeFalsy();
});
});
describe('stageData is [signStage, inkStage]', () => {
beforeEach(() => {
// assign stageData to the service that was injected
progressStageService.stageData = [signStage, inkStage];
fixture.detectChanges();
});
it('should display Ink stage when it is in stageData', () => {
const callToAction: HTMLElement = fixture.nativeElement;
const stageLabels = callToAction.querySelectorAll('span.stage-label');
expect(Array.from(stageLabels).some(sl =>
sl.textContent.includes('Ink'))).toBeTruthy();
});
});
});

Related

How to create a test for the below controller?

do I need to write a test for the below code?
#Post()
update(#Body() updateUserDto: UpdateUserDto, #Request() req) {
return this.userService.update(req.user.user, updateUserDto);
}
I'm going to make some assumptions about injected properties of the controller class. For full setups, you can view my testing example repo here. This should be essentially what you're looking for though
describe('UserController (Unit)', () => {
let controller: UserController;
let service: MockedClass<UserService>;
beforeAll(async () => {
const modRef = await Test.createTestingModule({
controllers: [UserController],
providers: [
{
provide: UserService,
useValue: {
update: jest.fn().mockReturnValue({ hello: 'world' }),
},
}
],
}).compile();
controller = modRef.get(UserController);
service = modRef.get(UserService);
});
it('should call update and return the value', () => {
const bodyVal = objectThatMatchesUpdateUserDto;
const reqVal = {
user: {
user: valueThatMatchesReqUserUser
}
};
const res = controller.update(bodyVal, reqVal);
expect(res).toEqual({ hello: 'world' });
expect(service.update).toBeCalledWith(reqVal.user.user, bodyVal)
});
});

Mock Injectable model in nestjs with Jest

I want to unit test my service. Inside my service I have a constructor that is:
contractService.ts:
export class ContractService {
private logger = new Logger("ContractService");
constructor(
#InjectModel(Contract)
private contractModel: typeof Contract
) {}
async getContracts(query: PaginationInterface): Promise<FetchContract> {
const { limit, page, skip } = paginationParseParams(query);
const { sortBy, direction } = sortParseParams(query, ColumnDetails);
const { count, rows } = await this.contractModel.findAndCountAll({
where: {},
offset: skip,
limit: limit,
order: [[sortBy, direction]],
});
const pages = Math.ceil(count / limit);
const meta = {
limit,
skip,
page,
count,
pages,
sortBy,
direction,
};
return { meta, data: rows };
}
}
My model looks like this: (Model is a class from sequelize-typescript)
export class Contract extends Model<Contract> {
....
}
So I want to create my unit test with jest. When I try to mock the contractModel, it does not find the method, eventhough I am trying to mock it.
const mockContractModel = () => ({
findAndCountAll: jest.fn(),
});
describe("ContractService", () => {
let contractService: ContractService;
let contractModel: Contract;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
ContractService,
{
provide: Contract,
useFactory: mockContractModel,
},
],
}).compile();
contractService = module.get<ContractService>(ContractService);
contractModel = module.get<Contract>(Contract);
});
it("should be defined", () => {
expect(contractService).toBeDefined();
});
describe("Get contracts", () => {
it("Should return all the contracts", async () => {
expect(contractModel.findAndCountAll).not.toHaveBeenCalled();
await contractService.getContracts(defaultPagination);
expect(contractModel.findAndCountAll).toHaveBeenCalled();
});
});
});
What is the right way to mock this contractModel?
Instead of using
{
provide: Contract,
useFactory: mockContractModel
}
You should be using
{
provide: getModelToken(Contract),
useFactory: mockContractModel
}
where getModelToken is imported from #nestjs/mongoose. This will get the correct DI token for Nest to know what you're mocking. For more examples, check this git repo

Testing in Angular with service dependency

I'm trying to write my first Angular 6 test. I have a component which returns a list of Companies from a service.
It looks like this:
Template
<div *ngFor="let company of this.companies">
<h4 id="company-{{company.id}}>{{company.name}}</h4>
</div>
Component.ts
import { ApiService } from '../service/api.service';
ngOnInit(): void {
this.companies = this.apiService.getCompanies();
}
Service
import { COMPANYLIST } from '../companyList';
companyList = COMPANYLIST;
public getCompanies(): Company[] {
return this.companyList;
}
I would like to test that I can see the list of Companies in the component. In my spec.ts I have tried to add a mocked apiService as per https://angular.io/guide/testing#component-with-a-dependency with no luck.
I'm guessing the test should look something like this, but I am having issues actually injecting the mocked service into this test.
it("should show the list of Companies", () => {
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector("company-" + this.company.id).textContent).toContain("Mock Company");
});
The strategy is to inject a place holder object for your service. In the test get a reference to that place holder object and then add fake functionality to it that will be called when testing the component.
Example (I omitted code that does not illustrate the point I am trying to make)
import { ApiService } from '../service/api.service';
describe('CompaniesComponent Tests', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [CompaniesComponent],
providers: [
{ provide: ApiService, useValue: {} }
]
}).compileComponents();
TestBed.compileComponents();
fixture = TestBed.createComponent(CompaniesComponent);
comp = fixture.componentInstance;
});
it("should show the list of Companies", () => {
// get service and fake a returned company list
const apiService = TestBed.get(ApiService) as ApiService;
apiService.getCompanies = () => ['Mock Company'];
// you probably need to call ngOnInit on your component so it retrieves the companies from the api service
comp.ngOnInit();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector("company-" + this.company.id).textContent).toContain("Mock Company");
});
You can mock your service :
export class MockCompanyService {
getCompanies: Spy;
constructor(config) {
this.getCompanies = jasmine.createSpy('getCompanies').and.callFake(() => config.companies);
}
}
In your test, you will give companies to you mock so when your function is called, you will have your companies displayed.:
describe('CompanyComponent', () => {
let component: CompanyComponent;
let fixture: ComponentFixture<CompanyComponent>;
let element;
const mockCompanies = [
...
];
beforeEach(async(() => {
return TestBed
.configureTestingModule({
declarations: [
CompanyComponent
],
imports: [],
providers: [
{
provide: ComponentFixtureAutoDetect,
useValue: true
}
]
})
.overrideComponent(CompanyComponent, {
set: {
providers: [
{provide: CompanyService, useValue: new MockCompanyService(mockCompanies)}
]
}
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(CompanyComponent);
component = fixture.debugElement.componentInstance;
element = fixture.debugElement.nativeElement;
});
}));
it('should create', () => {
expect(component).toBeTruthy();
});
it('...', () => {
});
});
in your example the unit test should be pretty simple to implement.
It should be something like that:
describe("path", () => {
let component: Component;
let fixture: ComponentFixture<Component>
let service: Service;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [Component],
providers: [Service]
});
fixture = TestBet.CreateComponent(Component)
service = TestBed.get(Service)
});
afterEach(() => {
fixture.destroy();
});
it("Component_Method_WhatDoYouExpect", () => {
let testCompanies = [{c1}....];
let spy = spyOn(service, "getCompanies").and.returnValue(testCompanies);
component.ngOnInit();
expect(spy).toHaveBeenCalled();
expect(component.companies).toEqual(testCompanies);
});
});
You have to create a test file for the component and one for the service.
In service test you should do almost the same like above, but there you have to initialize the company list, to call the method and to verify if the result is right.
service.companyList = [c1, c2...]
let res = service.GetCompanies();
expect(res).toEqual(service.companyList);
Here you can find more information about TestBed and Unit tests.

Expected undefined to equal 2

Technologies Used: Karma/Jasmine, Angular2
service.ts
Injectable()
export class Service {
constructor(private http: Http) { }
getGoogle(): Observable<any>{
console.log("Inside service");
return this.http.get('https://jsonplaceholder.typicode.com/posts/1');
}}
Please ignore the Typos and all the imports are made correctly.API is getting called correctly in the UI.
service.spec.ts
describe('Provider:Service', () => {
const HERO_ONE = 'HeroNrOne';
const HERO_TWO = 'WillBeAlwaysTheSecond';
let lastConnection;
let service;
beforeEach(() => {
let injector = ReflectiveInjector.resolveAndCreate([
{ provide: ConnectionBackend, useClass: MockBackend },
{ provide: RequestOptions, useClass: BaseRequestOptions },
Http,
Service
]);
service = injector.get(Service);
let backend = injector.get(ConnectionBackend) as MockBackend;
backend.connections.subscribe((connection: any) => lastConnection = connection);
});
it('getGoogle() should return the data in json format', fakeAsync(() => {
console.log('1');
let result:String[];
service.getGoogle().toPromise().then((heroes:String[]) => result = heroes);
lastConnection.mockRespond(new Response(new ResponseOptions({
body:JSON.stringify({data: [HERO_ONE, HERO_TWO]}),
})));
tick();
console.log(result[0]);
console.log('3');
expect(result.length).toEqual(2, 'should contain given amount of
heroes'); //this spec is failing because results does is not getting the response.
}));
The result:String[] is not getting the response that is provided.

Angular 4 HTTP Jasmine Unit Testing

I'm a noob at unit testing, I've been trying to create a mock unit test for a HTTP call to an end point.
My Service:
this.http.get('endPoint',options).subscribe((res: Response) => {
let result = res.json();
let obj = new ForgotPasswordResponse(res..........);
return obj;
})
My Unit Test
it('should get response from server', (done) => {
let responseObj:ForgotPasswordResponse = {
messageEN: 'dsadsada',
messageFR: 'dsada',
headerEN: 'dsada',
headerFR: 'dsada',
pageTarget: 'dsada',
args: 'dsa',
systemLogs: 'dsada',
msgCode: 'dsa',
type: 'dsa'
}
let signinid = '1234';
let lang = 'EN';
backend.connections.subscribe((connection: MockConnection) => {
let options = new ResponseOptions({ body: responseObj });
connection.mockRespond(new Response(options));
expect(connection.request.url).toEqual('./api/v1/forgot/1234/EN');
expect(connection.request.method).toEqual(RequestMethod.Get);
});
subject.initPwdResetFlow(signinid, lang).subscribe((response) => {
expect(response).toEqual(responseObj);
done();
});
});
My response object is of type ForgotPasswordResponse, with the appropriate props. However, when I run the test, I get a failure -- it only says Type Error thrown.
To mock your HTTP connection, you have to inject the XHRBackend as follows:
my-service.spec.ts
import { HttpModule, XHRBackend, Response, ResponseOptions } from '#angular/http';
import { MockBackend, MockConnection } from '#angular/http/testing';
import { MyService } from './my-service';
(...)
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
HttpModule
],
providers: [
MyService,
{provide: XHRBackend, useClass: MockBackend}
]
});
});
it('should get a response', (done) => {
inject([XHRBackend, MyService], (mockBackend: MockBackend, service: MyService) => {
const body = {content: 'blabla'};
const status = 200;
mockBackend.connections.subscribe((connection: MockConnection) => {
connection.mockRespond(new Response(new ResponseOptions({body, status})));
});
service.get().subscribe(response => {
expect(response).not.toBeNull();
done();
});
})();
});

Categories

Resources