Typecript ile jest'te sahte bağımlılık


94

Farklı bir dosyada bağımlılığı olan bir modülü test ederken. Bu modülü typcript olarak atarken jest.Mock, yöntemin mockReturnThisOnce(veya başka bir jest.Mock yönteminin) bağımlılıkta bulunmadığına dair bir hata verir , bunun nedeni önceden yazılmış olmasıdır. Türleri jest.Mock'tan miras almak için typcript almanın doğru yolu nedir?

İşte hızlı bir örnek.

Bağımlılık

const myDep = (name: string) => name;
export default myDep;

test.ts

import * as dep from '../depenendency';
jest.mock('../dependency');

it('should do what I need', () => {
  //this throws ts error
  // Property mockReturnValueOnce does not exist on type (name: string)....
  dep.default.mockReturnValueOnce('return')
}

Bunun çok yaygın bir kullanım durumu olduğunu düşünüyorum ve bunu nasıl doğru yazacağımı bilmiyorum. Herhangi bir yardım çok takdir edilecektir!


2
Doğru hatırlıyorsam, içeri aktarmadan önce alay etmelisin. Sadece ilk 2 satırı değiştirin. Ama bundan emin değilim.
Thomas

3
@ ThomasKleßen ES6 aracılığıyla içe aktarılan modüller, içe aktarmadan importönce bazı kodlar koysanız da önce değerlendirilir. Yani bu işe yaramayacak.
mgol

@Thomas jest.mock'a yapılan çağrılar kodun en üstüne kaldırılıyor - sanırım jest magic ... ( ref ) Ancak, bu bazı tuzaklar yaratıyor, örneğin , modül fabrikası parametresiyle jest.mock () çağrılırken, bu nedenle adı mock işlevleri asmock...
Tobi

Yanıtlar:


98

Tür dökümünü kullanabilirsiniz ve şu test.tsşekilde görünmelisiniz:

import * as dep from '../dependency';
jest.mock('../dependency');

const mockedDependency = <jest.Mock<typeof dep.default>>dep.default;

it('should do what I need', () => {
  //this throws ts error
  // Property mockReturnValueOnce does not exist on type (name: string)....
  mockedDependency.mockReturnValueOnce('return');
});

TS aktarıcı, jest.mock('../dependency');türünün değiştiğinin farkında değildir , depbu nedenle tür çevrimini kullanmanız gerekir. İçe aktarılan depbir tür tanımı olmadığı için türünü birlikte almanız gerekir typeof dep.default.

Jest ve TS ile yaptığım iş sırasında bulduğum diğer bazı faydalı modeller.

İçe aktarılan öğe bir sınıf olduğunda, örneğin typeof'u kullanmanız gerekmez:

import { SomeClass } from './SomeClass';

jest.mock('./SomeClass');

const mockedClass = <jest.Mock<SomeClass>>SomeClass;

Bu çözüm, bazı düğüm yerel modülleriyle alay etmeniz gerektiğinde de kullanışlıdır:

import { existsSync } from 'fs';

jest.mock('fs');

const mockedExistsSync = <jest.Mock<typeof existsSync>>existsSync;

Jest otomatik taklidi kullanmak istemiyorsanız ve manuel bir tane oluşturmayı tercih ediyorsanız

import TestedClass from './TestedClass';
import TestedClassDependency from './TestedClassDependency';

const testedClassDependencyMock = jest.fn<TestedClassDependency>(() => ({
  // implementation
}));

it('Should throw an error when calling playSomethingCool', () => {
  const testedClass = new TestedClass(testedClassDependencyMock());
});

testedClassDependencyMock()alay edilen nesne oluşturur örneği TestedClassDependency, sınıf veya tür veya arayüz olabilir


3
IntelliJ şikayetçi olduğu için (jest.fn'den sonra tür dökümünü kaldırdım) jest.fn(() =>...yerine kullanmak zorunda kaldım jest.fn<TestedClassDependency>(() =>.... Aksi takdirde bu cevap bana yardımcı oldu teşekkürler! Bunu package.json'ımda kullanıyorum: "@ types / jest": "^ 24.0.3"
A. Masson

jest.mock('./SomeClass');yukarıdaki kodda ne var ?
Reza

11
Artık son TS sürümüyle çalışmıyor ve jest 24 :(
Vincent


6
<jest.Mock<SomeClass>>SomeClassİfadesi benim için TS hatası üretiyor:Conversion of type 'typeof SomeClass' to type 'Mock<SomeClass, any>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Type 'typeof SomeClass' is missing the following properties from type 'Mock<SomeClass, any>': getMockName, mock, mockClear, mockReset, and 11 more.ts(2352)
the21st

62

mockedYardımcısı buradats-jest açıklandığı gibi kullanın

// foo.spec.ts
import { mocked } from 'ts-jest/utils'
import { foo } from './foo'
jest.mock('./foo')

// here the whole foo var is mocked deeply
const mockedFoo = mocked(foo, true)

test('deep', () => {
  // there will be no TS error here, and you'll have completion in modern IDEs
  mockedFoo.a.b.c.hello('me')
  // same here
  expect(mockedFoo.a.b.c.hello.mock.calls).toHaveLength(1)
})

test('direct', () => {
  foo.name()
  // here only foo.name is mocked (or its methods if it's an object)
  expect(mocked(foo.name).mock.calls).toHaveLength(1)
})

ve eğer

  • kullan tslint
  • ts-jest dev-bağımlılıklarınızda,

bu kuralı tslint.json:"no-implicit-dependencies": [true, "dev"]


İşte kullanım ts-jestve sınıflarla ilgili birkaç örnek daha : github.com/tbinna/ts-jest-mock-examples ve bu gönderi: stackoverflow.com/questions/58639737/…
Tobi

5
Bu, en yüksek oyu alan cevaptan çok daha iyi bir cevap.
fakeplasticandroid

@Tobi Depodaki test başarısız
Kreator

@Kreator'a haber verdiğin için teşekkürler. Bildirilenle aynı sorunu görüyor musunuz ? Henüz herhangi bir sorunu yeniden oluşturamadım.
Tobi

@Kreator bir PR'ı birleştirdi. Sorun devam ederse bize bildirin
Tobi

18

@ Types / jest / index.d.ts'deki kalıbı Mocked için def tipinin hemen üzerinde kullanıyorum (satır 515):

import { Api } from "../api";
jest.mock("../api");

const myApi: jest.Mocked<Api> = new Api() as any;
myApi.myApiMethod.mockImplementation(() => "test");

2
Yapabileceğinden oldukça eminimconst myApi = new Api() as jest.Mocked<Api>;
snowfrogdev

4
@neoflash: TypeScript 3.4'te katı modda değil - Api türünün ile yeterince örtüşmediğinden şikayet edecektir jest.Mock<Api>. Devam etmeniz gerekir const myApi = new Api() as any as jest.Mock<Api>ve yukarıdaki ifadenin çifte iddiadan biraz daha iyi göründüğünü söyleyebilirim.
paolostyle

@tuptus: Katı mod 3.4 için yeni mi? Lütfen bununla ilgili bir bağlantınız var mı?
elmpp

@elmpp: Ne demek istediğinden emin değilim. "Katı mod" derken "strict": truetsconfig.json içinde olmasını kastettim . Bu noImplicitAny, strictNullChecksvb. Şeyleri kapsar , böylece onlar için ayrı ayrı doğru olarak ayarlamanız gerekmez.
paolostyle

Ben anlamadım Neden sadece bir örnek yöntemini saplıyorsunuz, yani myApi? ApiTest edilen modül içindeki sınıf tarafından başlatılan diğer tüm örnekleri genel olarak saplamaz , değil mi?
Ivan Wang

14

İki çözüm var, her ikisi de istenen işlevi veriyor

1) jest.MockedFunction kullanın

import * as dep from './dependency';

jest.mock('./dependency');

const mockMyFunction = dep.myFunction as jest.MockedFunction<typeof dep.myFunction>;

2) jest.Mock kullanın

import * as dep from './dependency';

jest.mock('./dependency');

const mockMyFunction = dep.default as jest.Mock;

Bu iki çözüm arasında hiçbir fark yoktur. İkincisi daha kısadır ve bu nedenle onu kullanmanızı öneririm.

Her iki döküm çözümü de mockMyFunctionbenzer mockReturnValueveya https://jestjs.io/docs/en/mock-function-api.html üzerinde herhangi bir jest mock işlevinin çağrılmasına izin verirmockResolvedValue

mockMyFunction.mockReturnValue('value');

mockMyFunction normal olarak beklemek için kullanılabilir

expect(mockMyFunction).toHaveBeenCalledTimes(1);

7

Oyuncular as jest.Mock

İşlevi basitçe çevirmek jest.Mockhile yapmalı:

(dep.default as jest.Mock).mockReturnValueOnce('return')


6

İşte jest@24.8.0 ve ts-jest@24.0.2 ile yaptıklarım :

kaynak:

class OAuth {

  static isLogIn() {
    // return true/false;
  }

  static getOAuthService() {
    // ...
  }
}

Ölçek:

import { OAuth } from '../src/to/the/OAuth'

jest.mock('../src/utils/OAuth', () => ({
  OAuth: class {
    public static getOAuthService() {
      return {
        getAuthorizationUrl() {
          return '';
        }
      };
    }
  }
}));

describe('createMeeting', () => {
  test('should call conferenceLoginBuild when not login', () => {
    OAuth.isLogIn = jest.fn().mockImplementationOnce(() => {
      return false;
    });

    // Other tests
  });
});

Varsayılan olmayan bir sınıfın ve statik yöntemlerinin nasıl alay edileceği aşağıda açıklanmıştır:

jest.mock('../src/to/the/OAuth', () => ({
  OAuth: class {
    public static getOAuthService() {
      return {
        getAuthorizationUrl() {
          return '';
        }
      };
    }
  }
}));

Burada, sınıfınızın türünden bir tür dönüştürme jest.MockedClassveya buna benzer bir şey olmalı. Ancak her zaman hatalarla sonuçlanır. Ben de doğrudan kullandım ve işe yaradı.

test('Some test', () => {
  OAuth.isLogIn = jest.fn().mockImplementationOnce(() => {
    return false;
  });
});

Ancak, bu bir işlevse, onunla dalga geçebilir ve yazım sohbeti yapabilirsiniz.

jest.mock('../src/to/the/Conference', () => ({
  conferenceSuccessDataBuild: jest.fn(),
  conferenceLoginBuild: jest.fn()
}));
const mockedConferenceLoginBuild = conferenceLoginBuild as 
jest.MockedFunction<
  typeof conferenceLoginBuild
>;
const mockedConferenceSuccessDataBuild = conferenceSuccessDataBuild as 
jest.MockedFunction<
  typeof conferenceSuccessDataBuild
>;

4

Bunu şurada buldum @types/jest:

/**
  * Wrap a function with mock definitions
  *
  * @example
  *
  *  import { myFunction } from "./library";
  *  jest.mock("./library");
  *
  *  const mockMyFunction = myFunction as jest.MockedFunction<typeof myFunction>;
  *  expect(mockMyFunction.mock.calls[0][0]).toBe(42);
*/

Not: Yaptığınızda const mockMyFunction = myFunctionve sonra şöyle bir şey mockFunction.mockReturnValue('foo')yaptığınızda, siz de değişiyorsunuz myFunction.

Kaynak: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/jest/index.d.ts#L1089


1

Kullan as jest.Mockve başka hiçbir şey

Dışa aktarılan bir modülle alay etmenin en özlü yolu defaultts-jest'teki gibi düşünebildiğim, modülü olduğu gibi kullanmaktır jest.Mock.

Kod:

import myDep from '../dependency' // No `* as` here

jest.mock('../dependency')

it('does what I need', () => {
  // Only diff with pure JavaScript is the presence of `as jest.Mock`
  (myDep as jest.Mock).mockReturnValueOnce('return')

  // Call function that calls the mocked module here

  // Notice there's no reference to `.default` below
  expect(myDep).toHaveBeenCalled()
})

Faydaları:

  • defaulttest kodunun herhangi bir yerinde özelliğe başvurmayı gerektirmez - bunun yerine gerçek dışa aktarılan işlev adına başvurursunuz,
  • adlı dışa aktarmalarla alay etmek için aynı tekniği kullanabilirsiniz,
  • * asithalat beyanında hayır ,
  • typeofanahtar kelimeyi kullanarak karmaşık bir döküm yok ,
  • gibi ekstra bağımlılıklar yok mocked.

0

Yeni bir kütüphane bu sorunu bir babel eklentisiyle çözüyor: https://github.com/userlike/joke

Misal:

import { mock, mockSome } from 'userlike/joke';

const dep = mock(import('./dependency'));

// You can partially mock a module too, completely typesafe!
// thisIsAMock has mock related methods
// thisIsReal does not have mock related methods
const { thisIsAMock, thisIsReal } = mockSome(import('./dependency2'), () => ({ 
  thisIsAMock: jest.fn() 
}));

it('should do what I need', () => {
  dep.mockReturnValueOnce('return');
}

Bunun farkında olun depve mockReturnValueOncetamamen güvenli. Üstte, tsserver içeri depencencyaktarıldığının ve atandığının farkındadır, depböylece tsserver'ın desteklediği tüm otomatik yeniden düzenlemeler de çalışacaktır.

Not: Kitaplığın bakımını yapıyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.