github icontwitter icon

Test Test Diye Nice Nice: Mocklama (2) - Modül

Author imageEnes Başpınar /25 Eki 2022
21 min read •––– views

Bölümler

İçerik

Önceki yazıda mock kavramının ne olduğuna ve hangi ihtiyaçtan doğduğuna değindik. Kütüphane ve built-in metodların mocklanış yöntemlerinden bahsettik. Artık modüllere böldüğümüz fonksiyonları nasıl mocklayabileceğimize göz atalım.

Yazının kodlarına Github üzerinden erişebilirsiniz.

Modül Nedir?

Uygulamalar büyüdükçe dosyalara bölme ihtiyacı hissedilmeye başlanır. Fonksiyon gruplarını içeren bu dosyalara modül denir. Mocklarken hangi yöntemleri kullanabileceğimiz anlayabilmek için içe aktarılan dosyaların içeriklerine (nesne mi? değerleri neler?) göz atacağız.

CommonJs kullanarak yapabileceğimiz farklı import çeşitleri:

Dosyaların içeriklerini görmek için sekmeler arasında geçiş yapabilirsiniz.

utils.test.ts
utils.ts

_7
const utils = require("./utils");
_7
const { getProduct } = require("./utils");
_7
_7
test("playground", () => {
_7
console.log("require with default:", utils);
_7
console.log("require with partial :", getProduct);
_7
}

=== Output:

require with default: { get: [Function: get], getUser: [Function: getUser], getProduct: [Function: getProduct], default: { get: [Function: get], getUser: [Function: getUser], getProduct: [Function: getProduct] } }

require with partial : [Function: getProduct]

ES Modules kullanarak yapabileceğimiz farklı import çeşitleri:

utils.test.ts
utils.ts

_8
import * as utilsWithStar from "./utils";
_8
import utilsWithDefault, { getProduct } from "./utils";
_8
_8
test("playground", () => {
_8
console.log("import with * as:", utilsWithStar);
_8
console.log("import with default:", utilsWithDefault);
_8
console.log("import with partial:", getProduct);
_8
});

=== Output:

import with * as: { get: [Function: get], getUser: [Function: getUser], getProduct: [Function: getProduct], default: { get: [Function: get], getUser: [Function: getUser], getProduct: [Function: getProduct] } }

import with default: { get: [Function: get], getUser: [Function: getUser], getProduct: [Function: getProduct] }

import with partial: [Function: getProduct]

Farklı içe aktarma yöntemlerinin farklarına sıradaki başlıkta değineceğiz.

Girizgah

Uygulamamızda ürün ve kullanıcı bilgilerini çekmek için yardımcı fonksiyonlara sahip olduğumuzu varsayalım.

Kod kümelerimizi yardımcı metodlara çıkarma işlemine abstraction denir. Örneğin ürün bilgisini çekmek görevini ifa eden kodları getProduct() isimli bir fonksiyona yerleştirebiliriz. Koda bakan birisi bu fonksiyonunun ne döndüreceğini isminden anlayabilmelidir. Abstraction'ı sevelim çünkü test yazılmasını inanılmaz derecede kolaylaştırır.

utils.ts

_21
import axios from "axios";
_21
_21
const API_URL = "https://dummyjson.com";
_21
_21
export async function get(apiUrl: string): Promise<any> {
_21
try {
_21
const response = await axios.get(apiUrl);
_21
_21
return response.data;
_21
} catch (error) {
_21
return null;
_21
}
_21
}
_21
_21
export function getProduct(productId: number): Promise<any> {
_21
return get(`${API_URL}/products/${productId}`);
_21
}
_21
_21
export function getUser(userId: number): Promise<any> {
_21
return get(`${API_URL}/users/${userId}`);
_21
}

Elimizdeki bilgilerle mocklamayı deneyelim. İlk akla gelen import edip ezerek override etmek olabilir.

utils.test.ts

_7
import { get } from "./index";
_7
_7
test("should be mock", () => {
_7
get = jest.fn();
_7
_7
expect(jest.isMockFunction(get)).toBe(true);
_7
});

=== Output:

error TS2632: Cannot assign to 'get' because it is an import.

Mocklamaya çalıştığımız fonksiyonun import olduğuna dair uyarı ile karşılaştık. Dosyanın nesne olarak import edilmesinin çözüm olacağını düşünebiliriz.

utils.test.ts

_9
import * as UtilsModule from "./index";
_9
// `import Utils from "./index"` yapmayı denerseniz default
_9
// export'a sahip olmadığına dair uyarı alırsınız.
_9
_9
test("should be mock", () => {
_9
UtilsModule.get = jest.fn();
_9
_9
expect(jest.isMockFunction(UtilsModule.get)).toBe(true);
_9
});

=== Output:

error TS2540: Cannot assign to 'get' because it is a read-only property.

Ancak alnımızın çatına TypeScript hatasını yeriz. Olsun, güzel denemeydi. Zaten import'un direkt ezmek güzel bir uygulama olmazdı. Bu işi Jest'in maharetli ellerine bırakalım.

Modül Mocklama

Modülleri mocklamak için jest.mock(relativeFilePath, factory, options) kullanılır. Yalnızca dosya yolu verilmişse export edilen tüm metodları otomatik mocklar.

utils.test.ts
utils.ts

_7
import * as utils from "./utils";
_7
_7
jest.mock("./utils");
_7
_7
test("playground", () => {
_7
console.log("utils Module:", utils);
_7
});

=== Output:

utils Module: { __esModule: true, getProduct: [Function: getProduct] { _isMockFunction: true, getMockImplementation: [Function (anonymous)], mock: [Getter/Setter], mockClear: [Function (anonymous)], mockReset: [Function (anonymous)], mockRestore: [Function (anonymous)], mockReturnValueOnce: [Function (anonymous)], mockResolvedValueOnce: [Function (anonymous)], mockRejectedValueOnce: [Function (anonymous)], mockReturnValue: [Function (anonymous)], mockResolvedValue: [Function (anonymous)], mockRejectedValue: [Function (anonymous)], mockImplementationOnce: [Function (anonymous)], withImplementation: [Function: bound withImplementation], mockImplementation: [Function (anonymous)], mockReturnThis: [Function (anonymous)], mockName: [Function (anonymous)], getMockName: [Function (anonymous)] }, getUser: [Function: getUser] { _isMockFunction: true, ... }, default: [Function: get] { _isMockFunction: true, ... } }

Dosya yolunu mockladığımızda kendisini çağıran import'lar takip edilerek sahte modül döndürülür. Ancak unutmayalım ki test dosyasının içindeki modül çağrısı da mocklanmış olur.


_7
import * as UtilsModule from "./utils";
_7
_7
jest.mock("./utils");
_7
_7
test("playground", () => {
_7
expect(jest.isMockFunction(UtilsModule.get)).toBe(true);
_7
});

=== Output:

PASS src/utils.test.ts ✓ playground

Fonksiyonları kullanmadan önce mocklamıştık. Modüllerin de import edilmeden önce mocklanması gerekmez mi?


Çok güzel bir noktaya parmak bastın. Herhangi bir nesnenin kullanılmadan önce mocklanması zorunludur. Fakat biliyoruz ki import ifadelerinin dosyanın en üstünde olması JS yazılımcılarının genel alışkanlığıdır. Jest'de bu yapıyı bozmamak adına jest. mock ifadelerini hoist eder yani import'un üzerine taşır.

Jest'in sınırlarını keşfetmeye devam edelim.

Modül Fonksiyonların İmplementasyonunun Mocklanması

Mocklanan modüllerdeki fonksiyonlara varsayılan olarak jest.fn() atanır. Dolayısıyla implementasyonu ya da dönüş değerini önceki yazıdaki yöntemlerle mocklayabiliriz.

Utils modülümüzün testlerine get ile başlayalım. İçerisinde axios paketinin bir metodu kullanılmıştır ve bu paketi yani modülü mocklamamız gerekir.

utils.test.ts
utils.ts

_61
import axios from "axios";
_61
import * as UtilsModule from "./utils";
_61
_61
// axios paketini mockluyoruz.
_61
jest.mock("axios");
_61
_61
// jest metodlarının tiplerini modül fonksiyonlarına sarıyoruz.
_61
const mockedAxios = jest.mocked(axios);
_61
_61
describe("utils tests", () => {
_61
afterEach(() => {
_61
jest.clearAllMocks();
_61
});
_61
_61
describe("get() tests", () => {
_61
test("should return product when request is success", async () => {
_61
// fonksiyonumuzun verdiğimiz parametreyi axios.get'e doğru
_61
// şekilde ilettiğini test etmek için url'i değişkene atıyoruz.
_61
const apiUrl = "https://dummyjson.com/product/1";
_61
// sahte ürünümüzü oluşturuyoruz.
_61
const mockProduct = {
_61
id: 1,
_61
title: "iPhone 9",
_61
description: "An apple mobile which is nothing like apple",
_61
price: 549,
_61
discountPercentage: 12.96,
_61
rating: 4.69,
_61
stock: 94,
_61
brand: "Apple",
_61
category: "smartphones",
_61
};
_61
// test ettiğimiz fonksiyon içerisindeki farklı pakete bağımlı
_61
// olan axios.get fonksiyonunun çağrıldığında mock veri ile
_61
// resolve olmasını sağlıyoruz.
_61
mockedAxios.get.mockResolvedValueOnce({
_61
data: mockProduct,
_61
});
_61
_61
// test edeceğimiz fonksiyonu çağırıyoruz.
_61
const result = await UtilsModule.get(apiUrl);
_61
_61
// axios.get'in ilettiğimiz url ile çağrıldığını test ediyoruz
_61
expect(mockedAxios.get).toHaveBeenCalledWith(apiUrl);
_61
// istek başarısız olduğu zaman null döndürdüğünü test ediyoruz.
_61
expect(result).toStrictEqual(mockProduct);
_61
});
_61
_61
test("should return null when request is failed", async () => {
_61
const apiUrl = "https://dummyjson.com/product/1000";
_61
_61
mockedAxios.get.mockRejectedValueOnce(
_61
new Error("Error occured when fetching data!")
_61
);
_61
_61
const result = await UtilsModule.get(apiUrl);
_61
_61
expect(mockedAxios.get).toHaveBeenCalledWith(apiUrl);
_61
expect(result).toBeNull();
_61
});
_61
});
_61
});

=== Output:

PASS src/tests/utils.test.ts utils tests get() tests ✓ should return product whe request is success ✓ should return null when request is failed

Jest, modülleri mockladıktan sonra kendi metodları için TypeScript tiplerini eklemez. Barındırdığı tüm fonksiyonlara Jest metodlarının tiplerini sarmalamak istersek jest.mocked(source) metodunu kullanabiliriz.

Factory Parametresi

Modül mocklanırken fonksiyonların otomatik mocklanmasını istemiyorsak ve kendimiz konfigüre edeceksek factory parametresini kullanabiliriz. Örneğimizde eğer dönüş değeri mocklanmamışsa reject edecek bir yapı kuralım ve ikinci testi refactor edelim.


_53
import axios from "axios";
_53
import * as UtilsModule from "./utils";
_53
_53
jest.mock("axios", () => {
_53
return {
_53
get: jest
_53
.fn()
_53
.mockRejectedValue(new Error("Error occured when fetching data!")),
_53
};
_53
});
_53
_53
const mockedAxios = jest.mocked(axios);
_53
_53
describe("utils tests", () => {
_53
afterEach(() => {
_53
jest.clearAllMocks();
_53
});
_53
_53
describe("get() tests", () => {
_53
test("should return product when request is success", async () => {
_53
const apiUrl = "https://dummyjson.com/product/1";
_53
const mockProduct = {
_53
id: 1,
_53
title: "iPhone 9",
_53
description: "An apple mobile which is nothing like apple",
_53
price: 549,
_53
discountPercentage: 12.96,
_53
rating: 4.69,
_53
stock: 94,
_53
brand: "Apple",
_53
category: "smartphones",
_53
};
_53
_53
mockedAxios.get.mockResolvedValueOnce({
_53
data: mockProduct,
_53
});
_53
_53
const result = await UtilsModule.get(apiUrl);
_53
_53
expect(mockedAxios.get).toHaveBeenCalledWith(apiUrl);
_53
expect(result).toStrictEqual(mockProduct);
_53
});
_53
_53
test("should return null when request is failed", async () => {
_53
const apiUrl = "https://dummyjson.com/product/1000";
_53
_53
const result = await UtilsModule.get(apiUrl);
_53
_53
expect(mockedAxios.get).toHaveBeenCalledWith(apiUrl);
_53
expect(result).toBeNull();
_53
});
_53
});
_53
});

=== Output:

PASS src/tests/utils.test.ts utils tests get() tests ✓ should return product whe request is success ✓ should return null when request is failed

Peki ne zaman factory kullanmalıyız?


Fonksiyonların mocklamaları testler boyunca aynı kalacaksa, default mock değeri tanımlamamız gerekiyorsa ya da kısmi mocklama yapacaksak kullanabiliriz.

Dikkat etmemiz gereken önemli bir nokta, factory ve test içerisinde mockların ahengidir. Lafı uzatmadan şu üç farklı kullanımı özümseyelim.


_17
import axios from "axios";
_17
_17
jest.mock("axios", () => {
_17
return {
_17
get: jest.fn().mockResolvedValue("Mock in module factory"),
_17
};
_17
});
_17
_17
const mockedAxios = jest.mocked(axios);
_17
_17
test("playground", async () => {
_17
const apiUrl = "https://dummyjson.com";
_17
_17
mockedAxios.get.mockResolvedValue("Mock in test");
_17
console.log(await mockedAxios.get(apiUrl)); // Output: Mock in test
_17
console.log(await mockedAxios.get(apiUrl)); // Output: Mock in test
_17
});


_17
import axios from "axios";
_17
_17
jest.mock("axios", () => {
_17
return {
_17
get: jest.fn().mockResolvedValue("Mock in module factory"),
_17
};
_17
});
_17
_17
const mockedAxios = jest.mocked(axios);
_17
_17
test("playground", async () => {
_17
const apiUrl = "https://dummyjson.com";
_17
_17
mockedAxios.get.mockResolvedValueOnce("Mock in test");
_17
console.log(await mockedAxios.get(apiUrl)); // Output: Mock in test
_17
console.log(await mockedAxios.get(apiUrl)); // Output: Mock in module factory
_17
});


_17
import axios from "axios";
_17
_17
jest.mock("axios", () => {
_17
return {
_17
get: jest.fn().mockResolvedValueOnce("Mock in module factory"),
_17
};
_17
});
_17
_17
const mockedAxios = jest.mocked(axios);
_17
_17
test("playground", async () => {
_17
const apiUrl = "https://dummyjson.com";
_17
_17
mockedAxios.get.mockResolvedValue("Mock in test");
_17
console.log(await mockedAxios.get(apiUrl)); // Output: Mock in module factory
_17
console.log(await mockedAxios.get(apiUrl)); // Output: Mock in test
_17
});


_17
import axios from "axios";
_17
_17
jest.mock("axios", () => {
_17
return {
_17
get: jest.fn().mockResolvedValueOnce("Mock in module factory"),
_17
};
_17
});
_17
_17
const mockedAxios = jest.mocked(axios);
_17
_17
test("playground", async () => {
_17
const apiUrl = "https://dummyjson.com";
_17
_17
mockedAxios.get.mockResolvedValueOnce("Mock in test");
_17
console.log(await mockedAxios.get(apiUrl)); // Output: Mock in module factory
_17
console.log(await mockedAxios.get(apiUrl)); // Output: Mock in test
_17
});

Kısmi Mocklama

Şimdiye kadar daima tüm modülü mockladık. Ancak yalnızca bazı işlevleri mocklamak isteyebiliriz. Modüllerin gerçek içeriğine erişmek için jest.requireActual kullanılır.


_9
jest.mock('../moduleName', () => {
_9
const originalModule = jest.requireActual('../moduleName');
_9
_9
return {
_9
__esModule: true,
_9
...originalModule,
_9
functionThatBeMock: jest.fn(),
_9
};
_9
});

Spesifik Testlerde Modül Mocklamak

get fonksiyonun testlerini yazdık. Dilerseniz getProduct testine geçelim. Test ettiğimiz foksiyonun bağımlı olduğu diğer fonksiyonları mocklamak iyi bir pratiktir. Bu sebeple getProduct içerisinde kullanılan get fonksiyonunu mocklayacağız.


_44
import axios from "axios";
_44
import * as UtilsModule from "./utils";
_44
_44
jest.mock("./utils");
_44
jest.mock("axios", () => {
_44
return {
_44
get: jest
_44
.fn()
_44
.mockRejectedValue(new Error("Error occured when fetching data!")),
_44
};
_44
});
_44
_44
const mockedUtils = jest.mocked(UtilsModule);
_44
const mockedAxios = jest.mocked(axios);
_44
_44
describe("utils tests", () => {
_44
// ...
_44
_44
describe("getProduct() tests", () => {
_44
test("should call get func with api product endpoint when given product id", () => {
_44
const productId = 1;
_44
const mockProduct = {
_44
id: 1,
_44
title: "iPhone 9",
_44
description: "An apple mobile which is nothing like apple",
_44
price: 549,
_44
discountPercentage: 12.96,
_44
rating: 4.69,
_44
stock: 94,
_44
brand: "Apple",
_44
category: "smartphones",
_44
};
_44
_44
mockedUtils.get.mockResolvedValue(mockProduct);
_44
_44
const result = UtilsModule.getProduct(productId);
_44
_44
expect(UtilsModule.get).toHaveBeenCalledWith(
_44
`https://dummyjson.com/products/${productId}`
_44
);
_44
expect(result).toStrictEqual(mockProduct);
_44
});
_44
});
_44
});

=== Output:

utils tests get() tests ✕ should return product whe request is success (4 ms) ✕ should return null when request is failed getProduct() tests ✕ should call get func with api product endpoint when given product id

● utils tests › get() tests › should return product whe request is success Expected: "https://dummyjson.com/product/1" Number of calls: 0

● utils tests › get() tests › should return null when request is failed Expected: "https://dummyjson.com/product/1000" Number of calls: 0

● utils tests › getProduct() tests › should call get func with api product endpoint when given product id Expected: "https://dummyjson.com/products/1" Number of calls: 0

Hoppalaa... Yeni testimiz patladı, üstüne üstlük geçen testlerde patlamaya başladı. Çıktıyı incelediğimizde, mockladığımız fonksiyonların çağırılmadığını anlıyoruz. Jest bize, "Sen bi değerle çağırılmasını bekliyordun fakat bu fonksiyon hiç çağırılmadı." diyor.

getProduct fonksiyonunun implementasyonunu mocklamak için Utils modülünü mockladık. Ancak testi için orijinal implementasyonuna ihtiyaç duyduğumuz get fonksiyonu da mocklanmış oldu. Test bazlı fonksiyon mocklamak işimize yarardı. Bu problemi birkaç farklı yöntemle çözebiliriz.

jest.doMock()

Jest'te modül mocklamanın diğer bir yolu jest.doMock() kullanmaktır. Bunun jest.mock'tan farkı, hoist edilmiyor olmasıdır. Yani sadece kendisinden sonra yazılan importları mocklar.

Mocklanmasını beklediğiniz bir modül mocklanmamışsa import edildikten sonra mocklanmış olma ihtimali çok yüksektir. Kontrol etmeyi unutmayın.


_53
import axios from "axios";
_53
import UtilsModule from "./utils";
_53
_53
jest.mock("axios", () => {
_53
return {
_53
get: jest.fn().mockRejectedValue(new Error("Error occured when fetching data!")),
_53
};
_53
});
_53
_53
const mockedAxios = jest.mocked(axios);
_53
_53
describe("utils tests", () => {
_53
afterEach(() => {
_53
jest.clearAllMocks();
_53
// bu dosyadaki tüm modül mockları temizler.
_53
jest.resetModules();
_53
});
_53
_53
//...
_53
_53
describe("getProduct() tests", () => {
_53
test("should call get func with api product endpoint when given product id", () => {
_53
const productId = 1;
_53
const mockProduct = {
_53
id: 1,
_53
title: "iPhone 9",
_53
description: "An apple mobile which is nothing like apple",
_53
price: 549,
_53
discountPercentage: 12.96,
_53
rating: 4.69,
_53
stock: 94,
_53
brand: "Apple",
_53
category: "smartphones",
_53
};
_53
_53
jest.doMock("./utils", () => ({
_53
__esModule: true,
_53
...jest.requireActual("./utils"),
_53
get: jest.fn().mockResolvedValue(mockProduct),
_53
}));
_53
_53
// burası kritik bir nokta. doMock hoist edilmeyeceği
_53
// için sonrasında yaptığımız require'ı kullanmalıyız.
_53
const GetModule = require("./utils");
_53
const UtilsModule = require("./utils");
_53
_53
const result = UtilsModule.getProduct(productId);
_53
_53
expect(GetModule.default).toHaveBeenCalledWith(`https://dummyjson.com/products/${productId}`);
_53
expect(result).toStrictEqual(mockProduct);
_53
});
_53
});
_53
});

get metodunu mocklayamadığımıza dair bir hata aldık. getProduct ve get aynı dosyada yer aldığı ve biz mocklamak için import beklediğimizden dolayı başarısız olur. Teyit etmek için mockedUtils değişkenini yazdıralım.


_7
test("should call get func with api product endpoint when given product id", () => {
_7
// ...
_7
const UtilsModule = require("./utils");
_7
_7
console.log("get:", UtilsModule.get);
_7
// ...
_7
})

=== Output:

get: [Function: mockConstructor] { _isMockFunction: true, getMockImplementation: [Function (anonymous)], mock: [Getter/Setter], mockClear: [Function (anonymous)], mockReset: [Function (anonymous)], mockRestore: [Function (anonymous)], mockReturnValueOnce: [Function (anonymous)], mockResolvedValueOnce: [Function (anonymous)], mockRejectedValueOnce: [Function (anonymous)], mockReturnValue: [Function (anonymous)], mockResolvedValue: [Function (anonymous)], mockRejectedValue: [Function (anonymous)], mockImplementationOnce: [Function (anonymous)], withImplementation: [Function: bound withImplementation], mockImplementation: [Function (anonymous)], mockReturnThis: [Function (anonymous)], mockName: [Function (anonymous)], getMockName: [Function (anonymous)] }

Şimdi getProduct içinde get fonksiyonunu yazdıralım.


_4
export function getProduct(productId: number): Promise<any> {
_4
console.log("get Function: ", get.toString());
_4
// ...
_4
}

=== Output:

get Function: [Function: get]

Düşündüğümüz gibi fonksiyon mocklanmamış. Ayrı bir dosyaya çıkarıp yeni dosya üzerinden mocklayabiliriz.

utils.test.ts
utils.ts
get.ts

_83
import axios from "axios";
_83
import * as GetModule from "./get";
_83
_83
jest.mock("axios", () => {
_83
return {
_83
get: jest.fn().mockRejectedValue(new Error("Error occured when fetching data!")),
_83
};
_83
});
_83
_83
const mockedAxios = jest.mocked(axios);
_83
_83
describe("utils tests", () => {
_83
afterEach(() => {
_83
jest.clearAllMocks();
_83
jest.resetModules(); // clears all module mocks in this file.
_83
});
_83
_83
describe("get() tests", () => {
_83
test("should return product when request is success", async () => {
_83
const apiUrl = "https://dummyjson.com/product/1";
_83
const mockProduct = {
_83
id: 1,
_83
title: "iPhone 9",
_83
description: "An apple mobile which is nothing like apple",
_83
price: 549,
_83
discountPercentage: 12.96,
_83
rating: 4.69,
_83
stock: 94,
_83
brand: "Apple",
_83
category: "smartphones",
_83
};
_83
_83
mockedAxios.get.mockResolvedValueOnce({
_83
data: mockProduct,
_83
});
_83
_83
const result = await GetModule.default(apiUrl);
_83
_83
expect(mockedAxios.get).toHaveBeenCalledWith(apiUrl);
_83
expect(result).toStrictEqual(mockProduct);
_83
});
_83
_83
test("should return null when request is failed", async () => {
_83
const apiUrl = "https://dummyjson.com/product/1000";
_83
_83
const result = await GetModule.default(apiUrl);
_83
_83
expect(mockedAxios.get).toHaveBeenCalledWith(apiUrl);
_83
expect(result).toBeNull();
_83
});
_83
});
_83
_83
describe("getProduct() tests", () => {
_83
test("should call get func with api product endpoint when given product id", async () => {
_83
const productId = 1;
_83
const mockProduct = {
_83
id: 1,
_83
title: "iPhone 9",
_83
description: "An apple mobile which is nothing like apple",
_83
price: 549,
_83
discountPercentage: 12.96,
_83
rating: 4.69,
_83
stock: 94,
_83
brand: "Apple",
_83
category: "smartphones",
_83
};
_83
_83
jest.doMock("./get", () => {
_83
return {
_83
__esModule: true,
_83
default: jest.fn().mockResolvedValue(mockProduct),
_83
};
_83
});
_83
const GetModule = require("./get");
_83
const UtilsModule = require("./utils");
_83
_83
const result = await UtilsModule.getProduct(productId);
_83
_83
expect(GetModule.default).toHaveBeenCalledWith(`https://dummyjson.com/products/${productId}`);
_83
expect(result).toStrictEqual(mockProduct);
_83
});
_83
});
_83
});

=== Output:

utils tests get() tests ✓ should return product whe request is success (4 ms) ✓ should return null when request is failed getProduct() tests ✓ should call get func with api product endpoint when given product id

jest.spyOn()

Eski bir dostla karşılaşmış gibiyiz. Evet, modüllerde de jest.spyOn() kullanabiliriz. Ayrı dosyaya çıkartmak bu yöntemle de gerekli olsa bile çok daha temiz bir kullanım sağlar.

utils.test.ts
utils.ts
get.ts

_43
import axios from "axios";
_43
import * as GetModule from "./get";
_43
import * as UtilsModule from "./utils";
_43
_43
jest.mock("axios", () => {
_43
return {
_43
get: jest
_43
.fn()
_43
.mockRejectedValue(new Error("Error occured when fetching data!")),
_43
};
_43
});
_43
_43
const mockedAxios = jest.mocked(axios);
_43
_43
describe("utils tests", () => {
_43
// ...
_43
_43
describe("getProduct() tests", () => {
_43
test("should call get func with api product endpoint when given product id", async () => {
_43
const productId = 1;
_43
const mockProduct = {
_43
id: 1,
_43
title: "iPhone 9",
_43
description: "An apple mobile which is nothing like apple",
_43
price: 549,
_43
discountPercentage: 12.96,
_43
rating: 4.69,
_43
stock: 94,
_43
brand: "Apple",
_43
category: "smartphones",
_43
};
_43
_43
jest.spyOn(GetModule, "default").mockResolvedValue(mockProduct);
_43
_43
const result = await UtilsModule.getProduct(productId);
_43
_43
expect(GetModule.default).toHaveBeenCalledWith(
_43
`https://dummyjson.com/products/${productId}`
_43
);
_43
expect(result).toStrictEqual(mockProduct);
_43
});
_43
});
_43
});

=== Output:

utils tests get() tests ✓ should return product whe request is success (4 ms) ✓ should return null when request is failed getProduct() tests ✓ should call get func with api product endpoint when given product id

Modül Mockunu Temizleme

Modülleri mockladık ancak mockları temizlemek de isteyebiliriz. Bunun için iki methoda sahibiz. jest.dontMock() kendinden sonraki import nesnelerinin mocklarını temizler.


_17
jest.mock("axios");
_17
_17
test("playground", () => {
_17
const axiosInstance1 = require("axios"); // mocked
_17
console.log(
_17
"Is axiosInstance1.get mocked:",
_17
jest.isMockFunction(axiosInstance1.get)
_17
);
_17
_17
jest.dontMock("axios");
_17
_17
const axiosInstance2 = require("axios"); // unmocked
_17
console.log(
_17
"Is axiosInstance2.get mocked:",
_17
jest.isMockFunction(axiosInstance2.get)
_17
);
_17
});

=== Output:

Is axiosInstance1.get mocked: true Is axiosInstance2.get mocked: false

jest.unmock() ise bulunduğu kod bloğundaki ilgili tüm import nesnelerinin mocklarını temizler.


_17
jest.mock("axios");
_17
_17
test("playground", () => {
_17
const axiosInstance1 = require("axios"); // mocked
_17
console.log(
_17
"Is axiosInstance1.get mocked:",
_17
jest.isMockFunction(axiosInstance1.get)
_17
);
_17
_17
jest.unmock("axios");
_17
_17
const axiosInstance2 = require("axios"); // unmocked
_17
console.log(
_17
"Is axiosInstance2.get mocked:",
_17
jest.isMockFunction(axiosInstance2.get)
_17
);
_17
});

=== Output:

Is axiosInstance1.get mocked: false Is axiosInstance2.get mocked: false

Kapanış

İşin içine girip gerçek senaryolarla ilgili kod yazmadıkça test öğrenmek zor görünebilir. Ancak olumsuz bir yargıya varmayın istediğimiz takdirde öğrenebileceğimizi biliyoruz.

Yazı burada biter. Sağlıcakla kalın.

Kaynaklar

2022 © No rights are reserved.Inspired by Lee Robinson's blog.