96 votes

Comment faire pour bloquer une méthode d'un objet fantaisie de Jasmine ?

D'après la documentation de Jasmine, un simulacre peut être créé de la manière suivante :

jasmine.createSpyObj(someObject, ['method1', 'method2', ... ]);

Comment bloquer l'une de ces méthodes ? Par exemple, si vous voulez tester ce qui se passe lorsqu'une méthode lève une exception, comment pouvez-vous le faire ?

3 votes

Vous pouvez essayer de l'enchaîner avec andCallThrough . Il n'est pas clairement documenté :/

141voto

zbynour Points 8144

Vous devez enchaîner method1 , method2 comme l'a commenté EricG, mais pas avec andCallThrough() (ou and.callThrough() dans la version 2.0). Il déléguera à une mise en œuvre réelle .

Dans ce cas, vous devez chaîner avec and.callFake() et transmettre la fonction que vous souhaitez appeler (vous pouvez générer une exception ou ce que vous voulez):

 var someObject = jasmine.createSpyObj('someObject', [ 'method1', 'method2' ]);
someObject.method1.and.callFake(function() {
    throw 'an-exception';
});
 

Et puis vous pouvez vérifier:

 expect(yourFncCallingMethod1).toThrow('an-exception');
 

7 votes

Jasmine 2.0 a changé la syntaxe en .and.callFake() , .and.callThrough() , .and.returnValue() jasmine.github.io/2.0/introduction.html#section-Espions

27voto

Eric Swanson Points 454

Si vous utilisez Typescript, il est utile de convertir la méthode en Jasmine.Spy . Dans la réponse ci-dessus (curieusement, je n'ai pas de représentant pour les commentaires) :

(someObject.method1 as Jasmine.Spy).and.callFake(function() {
  throw 'an-exception';
});

Je ne sais pas si je fais de l'ingénierie à outrance, parce que je n'ai pas les connaissances nécessaires...

Pour Typescript, je veux :

  • Intellisense à partir du type sous-jacent
  • La possibilité de se moquer uniquement des méthodes utilisées dans une fonction

J'ai trouvé cela utile :

namespace Services {
    class LogService {
        info(message: string, ...optionalParams: any[]) {
            if (optionalParams && optionalParams.length > 0) {
                console.log(message, optionalParams);
                return;
            }

            console.log(message);
        }
    }
}

class ExampleSystemUnderTest {
    constructor(private log: Services.LogService) {
    }

    doIt() {
        this.log.info('done');
    }
}

// I export this in a common test file 
// with other utils that all tests import
const asSpy = f => <jasmine.Spy>f;

describe('SomeTest', () => {
    let log: Services.LogService;
    let sut: ExampleSystemUnderTest;

    // ARRANGE
    beforeEach(() => {
        log = jasmine.createSpyObj('log', ['info', 'error']);
        sut = new ExampleSystemUnderTest(log);
    });

    it('should do', () => {
        // ACT
        sut.doIt();

        // ASSERT
        expect(asSpy(log.error)).not.toHaveBeenCalled();
        expect(asSpy(log.info)).toHaveBeenCalledTimes(1);
        expect(asSpy(log.info).calls.allArgs()).toEqual([
            ['done']
        ]);
    });
});

11voto

Diego Herrera Points 121

Angular 9

Utilisation jasmine.createSpyObj est idéal pour tester un composant dans lequel un simple service est injecté. Par exemple : disons que dans mon composant HomeComponent, j'ai un HomeService (injecté). La seule méthode du HomeService est getAddress(). Lors de la création de la suite de tests HomeComponent, je peux initialiser le composant et le service comme suit :

describe('Home Component', () => {
    let component: HomeComponent;
    let fixture: ComponentFixture<HomeComponent>;
    let element: DebugElement;
    let homeServiceSpy: any;
    let homeService: any;

    beforeEach(async(() => {
        homeServiceSpy = jasmine.createSpyObj('HomeService', ['getAddress']);

        TestBed.configureTestingModule({
           declarations: [HomeComponent],
           providers: [{ provide: HomeService, useValue: homeServiceSpy }]
        })
        .compileComponents()
        .then(() => {
            fixture = TestBed.createComponent(HomeComponent);
            component = fixture.componentInstance;
            element = fixture.debugElement;
            homeService = TestBed.get(HomeService);
            fixture.detectChanges();
        });
    }));

    it('should be created', () => {
        expect(component).toBeTruthy();
    });

    it("should display home address", () => { 
        homeService.getAddress.and.returnValue(of('1221 Hub Street'));
        fixture.detectChanges();

        const address = element.queryAll(By.css(".address"));

        expect(address[0].nativeNode.innerText).toEqual('1221 Hub Street');
    });
 });

Il s'agit d'un moyen simple de tester votre composant à l'aide de la fonction jasmine.createSpyObj . Cependant, si votre service a plus de méthodes et une logique plus complexe, je recommanderais de créer un mockService au lieu de createSpyObj. Par exemple, je recommande de créer un mockService au lieu de createSpyObj : providers: [{ provide: HomeService, useValue: MockHomeService }]

J'espère que cela vous aidera !

0voto

Mr.wiseguy Points 840

En m'appuyant sur la réponse de @Eric Swanson, j'ai créé une fonction plus lisible et mieux documentée pour l'utiliser dans mes tests. J'ai également ajouté une certaine sécurité de type en tapant le paramètre comme une fonction.

Je recommanderais de placer ce code quelque part dans une classe de test commune, de sorte que vous puissiez l'importer dans chaque fichier de test qui en a besoin.

/**
 * Transforms the given method into a jasmine spy so that jasmine functions
 * can be called on this method without Typescript throwing an error
 *
 * @example
 * `asSpy(translator.getDefaultLang).and.returnValue(null);`
 * is equal to
 * `(translator.getDefaultLang as jasmine.Spy).and.returnValue(null);`
 *
 * This function will be mostly used in combination with `jasmine.createSpyObj`, when you want
 * to add custom behavior to a by jasmine created method
 * @example
 * `const translator: TranslateService = jasmine.createSpyObj('TranslateService', ['getDefaultLang'])
 * asSpy(translator.getDefaultLang).and.returnValue(null);`
 *
 * @param {() => any} method - The method that should be types as a jasmine Spy
 * @returns {jasmine.Spy} - The newly typed method
 */
export function asSpy(method: () => any): jasmine.Spy {
  return method as jasmine.Spy;
}

L'utilisation serait la suivante :

import {asSpy} from "location/to/the/method";

const translator: TranslateService = jasmine.createSpyObj('TranslateService', ['getDefaultLang']);
asSpy(translator.getDefaultLang).and.returnValue(null);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X