Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Uygulamalarınızın Azure hizmetleriyle doğru etkileşimde bulunduğundan emin olmak için JavaScript için Azure SDK tümleştirme kodunuzu test etmek çok önemlidir. Bu kılavuzda JavaScript uygulamalarınızda Azure SDK tümleştirmesini etkili bir şekilde test etme adımları gösterilmektedir.
Bulut hizmeti SDK çağrılarını taklit etmeye veya test amacıyla canlı hizmet kullanmaya karar verirken hız, güvenilirlik ve maliyet arasındaki dengeleri göz önünde bulundurmak önemlidir. Bu makalede, SDK tümleştirmesini test etmek için bir test çerçevesinin nasıl kullanılacağı gösterilmektedir. Uygulama kodu Cosmos DB'ye bir belge ekler. Test kodu, bulut kaynağını kullanmamak için bu kaynak kullanımını simüle eder.
Kullanılan çerçeveler şunlardır:
- Jest ile CommonJs
- ESM ile Vitest
- ESM ile test çalıştırıcısı Node.js
Önkoşullar
Node.js LTS. LTS sürüm durumu , genellikle kritik hataların toplam 30 ay boyunca düzeltileceğini garanti eden "uzun vadeli destektir".
Node.js test çalıştırıcısı, Node.js yüklemesinin bir parçasıdır.
Dikkat
Node.js test çalıştırıcısı için sağlanan örnek, mock.fn() ile deneysel node:test modülünü kullanır. Node'un yerleşik test çalıştırıcısında henüz tam olarak desteklenen bir sahte API sunmadığını unutmayın. Hedef Node sürümünüzün deneysel API'leri desteklediğinden emin olun veya bunun yerine bir üçüncü taraf mock kütüphanesi (veya stub işlevleri) kullanmayı göz önünde bulundurun.
Sahte bulut hizmetleri
Avantajlar:
- Ağ gecikme süresini ortadan kaldırarak test paketini hızlandırır.
- Tahmin edilebilir ve denetimli test ortamları sağlar.
- Çeşitli senaryoların ve uç durumlarının benzetimini yapmak daha kolaydır.
- Özellikle sürekli tümleştirme işlem hatlarında canlı bulut hizmetlerinin kullanımıyla ilgili maliyetleri azaltır.
Dezavantajlar:
- Mocklar gerçek SDK'dan saparak tutarsızlıklara yol açabilir.
- Canlı hizmetin belirli özelliklerini veya davranışlarını muhtemelen göz ardı edebilir.
- Üretime kıyasla daha az gerçekçi ortam.
Canlı hizmet kullanma
Avantajlar:
- Üretimi yakından yansıtan gerçekçi bir ortam mı?
- Sistemin farklı bölümlerinin birlikte çalıştığından emin olmak için tümleştirme testlerinde yararlı olur mu?
- Ağ güvenilirliği, hizmet kullanılabilirliği ve gerçek veri işleme ile ilgili sorunları tanımlamak yararlı olur mu?
Dezavantajlar:
- Ağ çağrıları nedeniyle daha yavaştır.
- Olası hizmet kullanım maliyetleri nedeniyle daha pahalıdır.
- Üretimle eşleşen bir canlı hizmet ortamı ayarlamak ve bakımını yapmak karmaşıktır ve zaman alır.
Sahte hizmetlerle canlı hizmetleri kullanma arasındaki seçim, test stratejinize bağlıdır. Hızın ve denetimin en önemli olduğu birim testleri için, sahte işlem genellikle daha iyi bir seçimdir. Gerçekçiliğin kritik öneme sahip olduğu tümleştirme testleri için canlı hizmet kullanmak daha doğru sonuçlar sağlayabilir. Bu yaklaşımların dengelenmesi, maliyetleri yönetirken ve test verimliliğini korurken kapsamlı bir test kapsamı elde edilmeye yardımcı olur.
Test çiftleri: Sahteler, saplamalar ve sahteler
Test dublörü, test amacıyla gerçek bir şey yerine kullanılan herhangi bir ikamedir. Seçtiğiniz çift türü, değiştirmesini istediğiniz öğeye bağlıdır. Sahte terimi genellikle terim gündelik olarak kullanıldığında herhangi bir çift olarak ifade edilir. Bu makalede, terim Jest test çerçevesinde özel olarak kullanılmış ve ayrıntılı olarak gösterilmiştir.
Sahte uygulamalar
Sahteler (aynı zamanda casus olarak da bilinir): Bir işlevin yerine geçin ve başka bir kod tarafından dolaylı olarak çağrıldığında bu işlevin davranışını denetleyip gözlemleyebilme kabiliyetine sahip olun.
Aşağıdaki örneklerde 2 işleviniz vardır:
-
someTestFunction: Test etmeniz gereken işlev. Yazmadığınız ve test etmeniz gerekmeyen bir bağımlılığı
dependencyFunction
çağırır. - dependencyFunctionMock: Bağımlılığın testi.
import { mock } from 'node:test';
import assert from 'node:assert';
// ARRANGE
const dependencyFunctionMock = mock.fn();
// ACT
// Mock replaces the call to dependencyFunction with dependencyFunctionMock
const { name } = someTestFunction()
// ASSERT
assert.strictEqual(dependencyFunctionMock.mock.callCount(), 1);
Testin amacı, bağımlılık kodunu çağırmadan bazıTestFunction'ın doğru şekilde davrandığından emin olmaktır. Test, bağımlılık sahtesinin çağrıldığını doğrular.
Büyük ve küçük bağımlılıkları karşılaştırma
Bir bağımlılıkla alay etmeye karar verdiğinizde, tam ihtiyacınız olanla dalga geçmeyi seçebilirsiniz, örneğin:
- Daha büyük bir bağımlılıktan bir veya iki işlev. Jest bu amaçla kısmi taklitler sunur.
- Bu makaledeki örnekte gösterildiği gibi daha küçük bir bağımlılığın tüm işlevleri.
Saptamalar
Saplamanın amacı, farklı senaryoların benzetimini yapmak için işlevin dönüş verilerini değiştirmektir. Kodunuzun bir işlevi çağırmasına ve çeşitli durumlar almasına olanak tanımak için bir stub (veya mock nesne) kullanırsınız; bunlar arasında başarılı sonuçlar, hatalar, hata durumları ve uç vakalar yer alır. Durum doğrulaması kodunuzun bu senaryoları doğru şekilde işlemesini sağlar.
import { describe, it, beforeEach, mock } from 'node:test';
import assert from 'node:assert';
// ARRANGE
const fakeDatabaseData = {first: 'John', last: 'Jones'};
const dependencyFunctionMock = mock.fn();
dependencyFunctionMock.mock.mockImplementation((arg) => {
return fakeDatabaseData;
});
// ACT
// Mock replaces the call to dependencyFunction with dependencyFunctionMock
const { name } = someTestFunction()
// ASSERT
assert.strictEqual(name, `${fakeDatabaseData.first} ${fakeDatabaseData.last}`);
Önceki testin amacı, tarafından someTestFunction
yapılan çalışmanın beklenen sonucu karşıladığından emin olmaktır. Bu basit örnekte işlevin görevi, ad ve aile adlarını birleştirmektir. Sahte verileri kullanarak beklenen sonucu bilirsiniz ve işlevin işi doğru şekilde gerçekleştirdiğini doğrulayabilirsiniz.
Sahte
Sahteler, bulut veritabanı yerine bellek içi veritabanı kullanma gibi normalde üretimde kullanmadığınız bir işlevin yerini alır.
// fake-in-mem-db.spec.ts
import { describe, it, beforeEach, afterEach, mock } from 'node:test';
import assert from 'node:assert';
class FakeDatabase {
private data: Record<string, any>;
constructor() {
this.data = {};
}
save(key: string, value: any): void {
this.data[key] = value;
}
get(key: string): any {
return this.data[key];
}
}
// Function to test
function someTestFunction(db: FakeDatabase, key: string, value: any): any {
db.save(key, value);
return db.get(key);
}
describe('In-Mem DB', () => {
let fakeDb: FakeDatabase;
let testKey: string;
let testValue: any;
beforeEach(() => {
fakeDb = new FakeDatabase();
testKey = 'testKey';
testValue = {
first: 'John',
last: 'Jones',
lastUpdated: new Date().toISOString(),
};
});
afterEach(() => {
// Restore all mocks created by node:test’s mock helper.
mock.restoreAll();
});
it('should save and return the correct value', () => {
// Create a spy on the save method using node:test's mock helper.
const saveSpy = mock.method(fakeDb, 'save').mock;
// Call the function under test.
const result = someTestFunction(fakeDb, testKey, testValue);
// Verify state.
assert.deepStrictEqual(result, testValue);
assert.strictEqual(result.first, 'John');
assert.strictEqual(result.last, 'Jones');
assert.strictEqual(result.lastUpdated, testValue.lastUpdated);
// Verify behavior
assert.strictEqual(saveSpy.callCount(), 1);
const calls = saveSpy.calls;
assert.deepStrictEqual(calls[0].arguments, [testKey, testValue]);
});
});
Önceki testin amacı veritabanıyla doğru etkileşimde olduğundan someTestFunction
emin olmaktır. Sahte bir bellek içi veritabanı kullanarak, gerçek bir veritabanına güvenmeden işlevin mantığını test edebilir ve testleri daha hızlı ve daha güvenilir hale getirebilirsiniz.
Senaryo: Azure SDK kullanarak Cosmos DB'ye belge ekleme
Tüm bilgiler gönderilip doğrulanırsa Cosmos DB'ye yeni bir belge yazması gereken bir uygulamanız olduğunu düşünün. Boş bir form gönderilirse veya bilgiler beklenen biçimle eşleşmiyorsa, uygulama verileri girmemelidir.
Cosmos DB örnek olarak kullanılır, ancak kavramlar JavaScript için Azure SDK'larının çoğu için geçerlidir. Aşağıdaki işlev bu işlevi yakalar:
// insertDocument.ts
import { Container } from '../data/connect-to-cosmos.js';
import type {
DbDocument,
DbError,
RawInput,
VerificationErrors,
} from '../data/model.js';
import Verify from '../data/verify.js';
export async function insertDocument(
container: Container,
doc: RawInput,
): Promise<DbDocument | DbError | VerificationErrors> {
const isVerified: boolean = Verify.inputVerified(doc);
if (!isVerified) {
return { message: 'Verification failed' } as VerificationErrors;
}
try {
const { resource } = await container.items.create({
id: doc.id,
name: `${doc.first} ${doc.last}`,
});
return resource as DbDocument;
} catch (error: any) {
if (error instanceof Error) {
if ((error as any).code === 409) {
return {
message: 'Insertion failed: Duplicate entry',
code: 409,
} as DbError;
}
return { message: error.message, code: (error as any).code } as DbError;
} else {
return { message: 'An unknown error occurred', code: 500 } as DbError;
}
}
}
Not
TypeScript türleri, bir işlevin kullandığı veri türlerini tanımlamaya yardımcı olur. Jest veya diğer JavaScript test çerçevelerini kullanmak için TypeScript'e ihtiyacınız olmasa da, tür açısından güvenli JavaScript yazmak için gereklidir.
Bu uygulamadaki işlevler şunlardır:
İşlev | Açıklama |
---|---|
insertDocument | Veritabanına bir belge ekler. Test etmek istediğimiz şey bu. |
inputVerified | Giriş verilerini bir şemaya göre doğrular. Verilerin doğru biçimde olmasını sağlar (örneğin, geçerli e-posta adresleri, doğru biçimlendirilmiş URL'ler). |
cosmos.items.create | Azure Cosmos DB için SDK işlevi, @azure/cosmos kullanarak. Bu, dalga geçmek istediğimiz şey. Kendi testleri paket sahipleri tarafından zaten tutulmaktadır. Gelen veriler doğrulamadan geçerse Cosmos DB işlev çağrısının yapıldığını ve veri döndürdüğünü doğrulamamız gerekir. |
Test çerçevesi bağımlılığını yükleme
Bu çerçeve, Node.js LTS'nin bir parçası olarak sağlanır.
Paketi test çalıştıracak şekilde yapılandırma
package.json
Kaynak kod dosyalarımızı test etmek için uygulamasını yeni bir betikle güncelleştirin. Kaynak kod dosyaları, kısmi dosya adı ve uzantısında eşleştirilerek tanımlanır. Test çalıştırıcısı, test dosyaları için yaygın adlandırma kuralını izleyerek dosyaları arar: <file-name>.spec.[jt]s
. Bu düzen, aşağıdaki örnekler gibi adlandırılan dosyaların test dosyaları olarak yorumlandığı ve Test çalıştırıcısı tarafından çalıştırıldığından kaynaklanır:
- * .test.js: Örneğin, math.test.js
- * .spec.js: Örneğin, math.spec.js
- tests dizinindeki dosyalar, örneğin tests/math.js
Test çalıştırıcısı ile bu test dosyası desenini desteklemek içinpackage.jsonbir betik ekleyin:
"scripts": {
"test": "node --test --experimental-test-coverage --experimental-test-module-mocks --trace-exit"
}
Azure SDK için birim testi ayarlama
insertDocument işlevini test etmek için mock'ları, saplamaları ve sahteleri nasıl kullanabiliriz?
- Sahteler: İşlevin davranışının aşağıdaki gibi test edilmesi için bir sahteye ihtiyacımız vardır:
- Veriler doğrulamayı geçerse Cosmos DB işlevine çağrı yalnızca 1 kez yapılır
- Veriler doğrulamayı geçmezse Cosmos DB işlevi çağrısı gerçekleşmez
- Saplamaları:
- Fonksiyon tarafından döndürülen yeni belgeyle eşleşen veriler iletilmiştir.
Test yaparken test kurulumu, testin kendisi ve doğrulama açısından düşünün. Bu işlev, test vernaküler açısından aşağıdaki terimleri kullanır:
- Düzenleme: Test koşullarınızı ayarlama
- Act: test edilecek sistemi veya SUT'u test etmek için işlevinizi çağırın
- Assert: Sonuçları doğrulayın. Sonuçlar davranış veya durum olabilir.
- Davranış, test işlevinizin doğrulanabilen işlevselliğini gösterir. Bir örnek, bir bağımlılığın çağrılmış olmasıdır.
- Durum işlevinden döndürülen verileri gösterir.
import { describe, it, afterEach, beforeEach, mock } from 'node:test';
import assert from 'node:assert';
describe('boilerplate', () => {
beforeEach(() => {
// Setup required before each test
});
afterEach(() => {
// Cleanup required after each test
});
it('should <do something> if <situation is present>', async () => {
// Arrange
// - set up the test data and the expected result
// Act
// - call the function to test
// Assert
// - check the state: result returned from function
// - check the behavior: dependency function calls
});
});
Testlerinizde mock'ları kullandığınızda, bu şablon kodunun, Azure istemci kitaplıkları gibi işlevde kullanılan temel bağımlılığı çağırmadan işlevi test etmek için mocking kullanması gerekir.
Test dosyasını oluşturma
Bir bağımlılık çağrısının simülasyonunu yapmak için 'mock' içeren test dosyası, ek bir ayar gerektirir.
Test dosyasının birkaç bölümü vardır:
-
import
: İçeri aktarma deyimleri, testlerinizi kullanmanıza veya taklit etmenize olanak sağlar. -
mock
: İstediğiniz varsayılan sahte davranışı oluşturun. Her test gerektiğinde değiştirilebilir. -
describe
: Dosya içininsert.ts
test grubu ailesi. -
it
: Her testinsert.ts
dosyası için.
Test dosyası, insert.ts
dosyası için iki doğrulama türüne bölünebilen üç testi kapsar:
Doğrulama türü | Test etme |
---|---|
Mutlu yol: should insert document successfully |
Taklit veritabanı yöntemi çağrıldı ve değiştirilen verileri döndürdü. |
Hata yolu: should return verification error if input is not verified |
Veriler doğrulanamadı ve bir hata döndürdü. |
Hata yolu:should return error if db insert fails |
Taklit edilen veritabanı yöntemi çağrıldı ve bir hata döndürdü. |
Aşağıdaki test dosyası insertDocument işlevinin nasıl test yapılacağını gösterir.
// insertDocument.test.ts
import { describe, it, beforeEach, mock } from 'node:test';
import assert from 'node:assert';
import { Container } from '../src/data/connect-to-cosmos.js';
import { createTestInputAndResult } from '../src/data/fake-data.js';
import type { DbDocument, DbError, RawInput } from '../src/data/model.js';
import { isDbError, isVerificationErrors } from '../src/data/model.js';
import Verify from '../src/data/verify.js';
import CosmosConnector from '../src/data/connect-to-cosmos.js';
import { insertDocument } from '../src/lib/insert.js';
describe('SDK', () => {
beforeEach(() => {
// Clear all mocks before each test
mock.restoreAll();
});
it('should return verification error if input is not verified', async () => {
const fakeContainer = {
items: {
create: async (_: any) => {
throw new Error('Create method not implemented');
},
},
} as unknown as Container;
const mVerify = mock.method(Verify, 'inputVerified').mock;
mVerify.mockImplementation(() => false);
const mGetUniqueId = mock.method(CosmosConnector, 'getUniqueId').mock;
mGetUniqueId.mockImplementation(() => 'unique-id');
const mContainerCreate = mock.method(fakeContainer.items, 'create').mock;
// Arrange: wrong shape of document on purpose.
const doc = { name: 'test' } as unknown as RawInput;
// Act:
const insertDocumentResult = await insertDocument(fakeContainer, doc);
// Assert - State verification.
if (isVerificationErrors(insertDocumentResult)) {
assert.deepStrictEqual(insertDocumentResult, {
message: 'Verification failed',
});
} else {
throw new Error('Result is not of type VerificationErrors');
}
// Assert - Behavior verification: Verify that create was never called.
assert.strictEqual(mContainerCreate.callCount(), 0);
});
it('should insert document successfully', async () => {
// Arrange: override inputVerified to return true.
const { input, result }: { input: RawInput; result: Partial<DbDocument> } =
createTestInputAndResult();
const fakeContainer = {
items: {
create: async (doc: any) => {
return { resource: result };
},
},
} as unknown as Container;
const mVerify = mock.method(Verify, 'inputVerified').mock;
mVerify.mockImplementation(() => true);
const mContainerCreate = mock.method(
fakeContainer.items as any,
'create',
).mock;
mContainerCreate.mockImplementation(async (doc: any) => {
return { resource: result };
});
// Act:
const receivedResult = await insertDocument(fakeContainer, input);
// Assert - State verification: Ensure the result is as expected.
assert.deepStrictEqual(receivedResult, result);
// Assert - Behavior verification: Ensure create was called once with correct arguments.
assert.strictEqual(mContainerCreate.callCount(), 1);
assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], {
id: input.id,
name: result.name,
});
});
it('should return error if db insert fails', async () => {
// Arrange: override inputVerified to return true.
const { input, result } = createTestInputAndResult();
const errorMessage: string = 'An unknown error occurred';
const fakeContainer = {
items: {
create: async (doc: any): Promise<any> => {
return Promise.resolve(null);
},
},
} as unknown as Container;
const mVerify = mock.method(Verify, 'inputVerified').mock;
mVerify.mockImplementation(() => true);
const mContainerCreate = mock.method(fakeContainer.items, 'create').mock;
mContainerCreate.mockImplementation(async (doc: any) => {
const mockError: DbError = {
message: errorMessage,
code: 500,
};
throw mockError;
});
// Act:
const insertDocumentResult = await insertDocument(fakeContainer, input);
// // Assert - Ensure create method was called once with the correct arguments.
assert.strictEqual(isDbError(insertDocumentResult), true);
assert.strictEqual(mContainerCreate.callCount(), 1);
assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], {
id: input.id,
name: result.name,
});
});
});
Sorun giderme
Bu makaledeki kodun çoğu MicrosoftDocs/node-essentials GitHub deposundan gelir. Cosmos DB Bulut kaynağına eklemek istiyorsanız, kaynağı bu betikle oluşturun.