web-dev-qa-db-fra.com

Modifier la valeur de l'observable renvoyée par le service simulé pour chaque test dans Angular / Jasmine / Redux

J'ai un service qui renvoie les valeurs exposées par les sélecteurs ngrx. Un composant définit ce service pour obtenir des données. J'écris des tests unitaires pour le composant à l'aide d'une maquette du service et j'ai besoin que le service simulé renvoie des valeurs différentes pour chaque test unitaire. Comment puis-je atteindre cet objectif?

Composant

@Component({
    selector: 'app-test',
    templateUrl: './test.component.html',
    providers: [TestService],

})
    export class TestComponent {

        test$ = this.testService.test$;
        test: number;

        constructor(private service: TestService) {
            service.test$.subscribe(test => this.test = test);
        }
    }

Service

export class TestService {

        test$ = this.store.select(getTestValueFromStore);

        constructor(private store: Store<any>){}
}

Tentative 1 (réinitialiser la valeur du service): ne fonctionne pas

class MockTestService {
    **test$ = of(10);**
}

describe('TestComponent', () => {
    let testService: TestService;

    beforeEach((() => {
        // Define component service
        TestBed.overrideComponent(
            TestComponent,
            { set: { providers: [{ provide: TestService, useClass: MockTestService }] } }
        );

        TestBed.configureTestingModule({
            declarations: [TestComponent]
        })
            .compileComponents();
    }));

    beforeEach(async() => {
        fixture = TestBed.createComponent(TestComponent);
        component = fixture.componentInstance;
        testService = fixture.debugElement.injector.get(TestService);
        fixture.detectChanges();
    });

it('should do something when the value returned by the service is 20', fakeAsync(() => {

        **testService.test$ = of(20);**

        tick();

        expect(component.test).toEqual(20);
    }));
});

Tentative 2: utilisez des sujets. Karma renvoie l'erreur "La propriété 'next' n'existe pas sur le type 'Observable'" car TestService renvoie des observables, pas des sujets

class MockTestService {
        **test$ = new BehaviourSubject(10);**
    }

    describe('TestComponent', () => {
        let testService: TestService;

        beforeEach((() => {
            // Define component service
            TestBed.overrideComponent(
                TestComponent,
                { set: { providers: [{ provide: TestService, useClass: MockTestService }] } }
            );

            TestBed.configureTestingModule({
                declarations: [TestComponent]
            })
                .compileComponents();
        }));

        beforeEach(async() => {
            fixture = TestBed.createComponent(TestComponent);
            component = fixture.componentInstance;
            testService = fixture.debugElement.injector.get(TestService);
            fixture.detectChanges();
        });

    it('should do something when the value returned by the service is 20', fakeAsync(() => {

            **testService.test$.next(20);**

            tick();

            expect(component.test).toEqual(20);
        }));
    });
8
bubble

Vous avez dit dans votre question que vous vouliez que "le service simulé renvoie des valeurs différentes pour chaque test unitaire". Pour ce faire, vous devrez apporter des modifications à la façon dont votre composant est défini.

  1. La modification la plus importante consiste à déplacer l'abonnement dans ngOnInit () et hors du constructeur de votre composant. Si vous faites cela, vous pouvez contrôler le moment où l'abonnement se déclenche, sinon il se déclenchera lorsque le composant sera créé, ce qui rendra très difficile le retour de valeurs différentes pour chaque test unitaire.

  2. Une fois cela fait, vous devez faire attention à l'endroit où vous appelez fixture.detectChanges(). En effet, cela exécutera ngOnInit(), qui exécutera l'abonnement et stockera la valeur renvoyée par l'observable dans component.test. Notez que puisque vous utilisez of() il émettra simplement une fois puis se terminera. Il ne sera plus émis si vous mettez une nouvelle valeur dans testService.test$.

  3. Une fois cette configuration effectuée, vous pouvez modifier la valeur de testService.test$ AVANT d'appeler fixture.detectChanges et de définir les valeurs comme vous le souhaitez.

J'ai mis en place un StackBlitz pour le démontrer, en changeant le moins possible votre code juste pour le faire fonctionner. Il existe deux tests, chacun testant différentes valeurs renvoyées.

J'espère que ça aide!

7
dmcgrandle

Essayez quelque chose comme ça

describe('TestComponent', () => {
    beforeEach((() => {
        TestBed.configureTestingModule({
            declarations: [TestComponent],
            providers: [
              { provide: TestService, useValue: { test$: of(20) } }
            ]
        })
            .compileComponents();
    }));

    beforeEach(async() => {
        fixture = TestBed.createComponent(TestComponent);
        component = fixture.componentInstance;
        testService = fixture.debugElement.injector.get(TestService);
        fixture.detectChanges();
    });

   it('should do something when the value returned by the service is 20', () => {
        expect(component.test).toEqual(20);
    });
});

Vous n'avez pas besoin d'utiliser fakeAsync ici, car of() émet de manière synchrone.

En outre, pensez à vous abonner à l'observable dans le crochet ngOnInit. Le constructeur doit être utilisé uniquement pour l'injection de dépendances.

1
Tomasz Kula