BCJTI / ng2-cookies

Simple library to deal with cookies in Angular2
64 stars 31 forks source link

Can't Inject CookieService Mock into Component During Unit Test #53

Closed mylifeandcode closed 6 years ago

mylifeandcode commented 7 years ago

When attempting to provide a component with a mock for CookieService, it never gets injected correctly.

Example:

class CookieServiceMock {
    check = jasmine.createSpy('check').and.returnValue(true);
    setMock = jasmine.createSpy('set');
    delete = jasmine.createSpy('delete');
}

describe('assessments-certs.component', function () {
    let fixture: ComponentFixture<AssessmentsAndCertsComponent>;
    let comp: AssessmentsAndCertsComponent;
    let cookieSvcMock: CookieServiceMock;

    beforeEach(async(() => {
        cookieSvcMock = new CookieServiceMock();

        TestBed.configureTestingModule({
            declarations: [
                AssessmentsAndCertsComponent,
                CertificateComponentMock
            ],
            providers: [
                {
                    provide: CookieService,
                    useValue: cookieSvcMock
                }
            ],
            schemas: [CUSTOM_ELEMENTS_SCHEMA]
        }).compileComponents();
    }));

Component (partial code):
@Component({
    selector: 'assessments-certs',
    providers: [CookieService], 
    templateUrl: './assessments-certs.component.html'
})
export class AssessmentsAndCertsComponent {

    constructor(private _cookieSvc: CookieService) {
    }
...
}

Result: _cookieSvc always injects as CookieService(), and not the actual mock. I suspect this is because the CookieService class lacks the @Injectable decorator.

Bigous commented 7 years ago

Hi @mylifeandcode .

You are totally right. Even if it's not what is causing your problem, we should have make it @Injectable(). We want it to be dependent of minimum, so we didn't make it dependent of @angular/core. But we should.

I'll work on it soon... Or if you could contribute with a pull request we would be very happy. I see you and @sod are very experienced with test suits. Could you help us to set one? Which one is better for our little library? (Karma, Jasmine, Mocha?)

[]'s

mylifeandcode commented 6 years ago

Apologies for the delay in replying. I use Karma as my test runner and Jasmine as my testing framework. As for the pull request, I'd love to, but am working to meet some tight year-end deadlines. If I have some spare cycles I'll do it, but can't make any promises (sorry).

Bigous commented 6 years ago

@mylifeandcode , No problem,

I made a pull request #54 to update the build system to ngc instead of tsc and to make the services @Injectable.

If @carcamano accept it, this issue will be closed automaticaly. Yet, I didn't set a test framework, so, the review / pull request for that would be very nice.

sod commented 6 years ago

Your library is fine. No need to bloat it with dependencies. Classes without a constructor don't need @Injectable. And even if, there are easier ways.

Whats wrong in the test of OP is, you define providers on component level, so you have to override on component level with TestBed.overrideComponent.

Looks something like:

import {ComponentFixture, TestBed} from '@angular/core/testing';
import {AppComponent} from './app.component';
import {CookieService} from 'ng2-cookies';

describe('AppComponent', () => {
    let cookieServiceMock: jasmine.SpyObj<CookieService>;
    let fixture: ComponentFixture<AppComponent>;

    beforeEach(() => {
        cookieServiceMock = jasmine.createSpyObj<CookieService>('CookieService', ['check', 'set', 'delete']);
        cookieServiceMock.check.and.returnValue(true);

        TestBed.configureTestingModule({
            declarations: [
                AppComponent
            ],
        });

        fixture = TestBed.overrideComponent(AppComponent, {
            set: {
                providers: [{
                    provide: CookieService,
                    useValue: cookieServiceMock
                }]
            }
        }).createComponent(AppComponent);
    });

    it('should mock cookieService', () => {
        const app = fixture.debugElement.componentInstance;

        expect(app.cookieService.check('foobar')).toBe(true);
        expect(app.cookieService.check).toHaveBeenCalledWith('foobar');
    });
});
mylifeandcode commented 6 years ago

Thank you sod! That was the problem. Because I had the provider declared in the Component's providers metadata, I needed to use TestBed.overrideComponent() as you described. Thanks again!