0

I can't manage to create a simple jasmine test for my Angular 2 project. This is what I'm trying to do:

Component to test (based on a service):

@Component({
    providers: [AccountService],
    selector: "account",
    templateUrl: "app/account/account.component.html",
})

export class AccountComponent implements OnInit {

    public accounts: Account[];    
    public error: string;

    constructor(private accountService: AccountService) {}

    public ngOnInit(): any {
        this.getAccounts();
    }

    public getAccounts() {
        this.accountService.getAccounts()
            .then(
                accounts => this.accounts = accounts,
                error => this.error = error
            );
    }
}

The AccountService uses Http (from @angular/http); getAccounts method returns a promise with (hopefully) the list of accounts.

I'm trying to test the AccountComponent with a fake AccountService (mock). Based on Angular 2 documentation, here is the implementation of my spec file:

describe('Account module test', () => {    
    let accountServiceMock;
    let fixture;
    let comp;
    let spy;

    beforeEach(() => {
        TestBed.configureTestingModule({
            declarations: [ AccountComponent ],
            providers:    [ AccountService ],
        });

        fixture = TestBed.createComponent(AccountComponent);
        comp    = fixture.componentInstance;

        // AccountService actually injected into the component
        accountServiceMock = fixture.debugElement.injector.get(AccountService);

        // Setup spy on the `getAccounts` method; return no account (basic test)
        spy = spyOn(AccountService, 'getAccounts')
            .and.returnValue(Promise.resolve([]));

    });

    it('Should get 0 account', () => {

        comp.ngOnInit();    
        expect(comp.accounts.length).toBe(0);
    });
});

But it does not work and I got a pretty ugly error message in the terminal:

**stuff**/node_modules/@angular/core/bundles/core.umd.js:334
                var parameters = Reflect.getMetadata('parameters', cls) || [];

TypeError: Cannot read property 'getMetadata' of undefined
    at ParamDecorator (**stuff**/node_modules/@angular/core/bundles/core.umd.js:334:41)
    at __decorate (**stuff**/dist/js/app/account/account.service.js:5:95)
    at **stuff**/dist/js/app/account/account.service.js:86:22
    at Object.<anonymous> (**stuff**/dist/js/app/account/account.service.js:91:2)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Module.require (module.js:353:17)
    at require (internal/module.js:12:17)

Any idea of what I'm missing? Can't figure out how to solve that problem... Thanks in advance!

EDIT: based on comments, I've tried another way:

describe('component: AccountComponent', () => {
    let fixture;
    let service;

    beforeEach(() => {
        TestBed.configureTestingModule({
            declarations: [
                AccountComponent
            ],
        });

        fixture = TestBed.createComponent(AccountComponent);
        service = fixture.debugElement.injector.get(AccountService);
    });

    it('should get 0 account', () => {
        spyOn(service, 'getAccounts').and.returnValue([]);
        fixture.detectChanges();
        expect(fixture.componentInstance.accounts).toBe([]);
    });
});

I got exactly the same error... I though there was a problem outside of the test file, but when I remove the beforeEach close and add a dummy test, the jasmine command just runs fine. Back to square one I guess :/

EDIT2: as requested, here is my AccountService class:

import {Http, Response, Headers} from "@angular/http";
import {Injectable} from "@angular/core";
import "rxjs/add/operator/toPromise";

export interface IAccountService {
    getAccounts();
}

@Injectable()
export class AccountService implements IAccountService {

    private static BASE_URL: string = "/api/account/";

    private static extractResult(res: Response) {
        return res.json();
    }

    /**
     * Extract error message from server response
     */
    private static handleError(error: any) {
        const errMsg: string = error._body;
        console.error(error); // log to console
        return Promise.reject(errMsg);
    }

    constructor(private http: Http) {
    }

    /**
     * Get list of accounts from server
     */
    public getAccounts() {
        return this.http.get(AccountService.BASE_URL)
            .toPromise()
            .then(AccountService.extractResult)
            .catch(AccountService.handleError);
    }
}
DavidL
  • 1,120
  • 1
  • 15
  • 34
  • Looks similar to http://stackoverflow.com/questions/40350463/angular2-testing-service-mocks/40352939#40352939 perhaps you get some hints there. – Günter Zöchbauer Nov 01 '16 at 11:11
  • Thanks for the feedback. I've tried to adapt my code to fit your example. Without success.. (see my edit) – DavidL Nov 01 '16 at 11:40
  • 1
    Can you show the complete AccountService class. Looking at the stacktrace, it look like a problem with resolving the metadata from that class' constructor. I've never seen this error, so I'm not sure what it is. – Paul Samsotha Nov 01 '16 at 11:50
  • Thanks again for your help. I've added the `AccountService`class code. – DavidL Nov 01 '16 at 20:14
  • Are you still getting the same error? If so, I don't think I can help. I've never seen that before, and your service code looks fine. Unfortunately, this is not an error I can reproduce. – Paul Samsotha Nov 02 '16 at 08:51
  • After solving this error, then you will get another one about providers not found. Just import the `HttpModule` into the test bed. But as far as the current problem, I don't know – Paul Samsotha Nov 02 '16 at 08:52
  • Full code is available online on my github account (if it can help): https://github.com/DavidLevayer/countable. Thanks anyway! – DavidL Nov 02 '16 at 11:18
  • I'll try to add the Http import as soon as possible. I'll keep you update – DavidL Nov 02 '16 at 11:20

0 Answers0