Nuestro SUT es una clase que implementa el sdk @aws-sdk/client-dynamodb
en un método llamado create
que implementa el sdk.
// index.ts
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
export class DynamoDBService {
private tableName: string;
private dynamoDBDocumentClient: DynamoDBDocumentClient;
constructor({ region, tableName }: { region: string; tableName: string }) {
const translateConfig = { marshallOptions };
const client: DynamoDBClient = new DynamoDBClient({ region });
this.dynamoDBDocumentClient = DynamoDBDocumentClient.from(
client,
translateConfig
);
this.tableName = tableName;
}
async create({
newItem,
conditionExpression,
}: {
newItem: object;
conditionExpression?: string;
}) {
try {
const params: PutCommandInput = {
TableName: this.tableName,
Item: newItem,
};
if (conditionExpression) params.ConditionExpression = conditionExpression;
const response: PutCommandOutput = await this.dynamoDBDocumentClient.send(
new PutCommand(params)
);
return { status: "success", response };
} catch (error: any) {
console.error(new Error(`Fail execution in: ${this.constructor.name}`));
// eslint-disable-next-line prefer-rest-params
console.error("Arguments received", arguments);
console.error(error);
return { status: "fail", error, errorName: error?.name };
}
}
}
En el test que vamos a escribir para método create
vamos va validar que efectivamente se llame al método send
de dynamoDBDocumentClient
sin llamarlo realmente, para esto nos vamos a ayuda de dos recurso provistos por jest
mockImplementation
: Que nos permite simular la implementación de una método o recurso.toHaveBeenCalled
: Validar que un método, normalmente un mock ha sido llamado en durante la ejecución del test.
// test.ts
import { DynamoDBService } from "../src";
const mockResponseFromSendCommand = {
$metadata: {
httpStatusCode: 200,
requestId: "9ABQ1FE6VQ7VKP5J4D6GDN4BNFVV4KQNSO5AEMVJF66Q9ASUAAJG",
extendedRequestId: undefined,
cfId: undefined,
attempts: 1,
totalRetryDelay: 0,
},
Attributes: undefined,
ConsumedCapacity: undefined,
ItemCollectionMetrics: undefined,
};
const mockSend = jest.fn(() => mockResponseFromSendCommand);
jest.mock("@aws-sdk/lib-dynamodb", () => ({
DynamoDBDocumentClient: {
from: () => ({
send: mockSend,
}),
},
PutCommand: jest.fn().mockImplementation(() => ({})),
}));
describe("Create Item", () => {
test("Method dynamoDBDocumentClient.send is called", async () => {
const dynamodb = new DynamoDBService({
region: "us-east-1",
tableName: "table-axample-qa",
});
const newItem = {
id: 88,
firstName: "Carry",
lastName: "Morgan",
};
const response = await dynamodb.create({ newItem });
const expectedResponse = {
status: "success",
response: {
$metadata: {
httpStatusCode: 200,
requestId: expect.any(String),
extendedRequestId: undefined,
cfId: undefined,
attempts: 1,
totalRetryDelay: 0,
},
Attributes: undefined,
ConsumedCapacity: undefined,
ItemCollectionMetrics: undefined,
},
};
expect(response).toEqual(expectedResponse);
expect(mockSend).toHaveBeenCalled();
});
});
Si queremos evaluar mas casos de uso, podemos hacer el mockSend.mockImplementation
dentro de cada test.
import { DynamoDBService } from "../src";
const mockSend = jest.fn(() => {});
jest.mock("@aws-sdk/lib-dynamodb", () => ({
DynamoDBDocumentClient: {
from: () => ({
send: mockSend,
}),
},
PutCommand: jest.fn().mockImplementation(() => ({})),
}));
describe("Create Item", () => {
test("Method dynamoDBDocumentClient.send is called", async () => {
mockSend.mockImplementation(() => ({
$metadata: {
httpStatusCode: 200,
requestId: "9ABQ1FE6VQ7VKP5J4D6GDN4BNFVV4KQNSO5AEMVJF66Q9ASUAAJG",
extendedRequestId: undefined,
cfId: undefined,
attempts: 1,
totalRetryDelay: 0,
},
Attributes: undefined,
ConsumedCapacity: undefined,
ItemCollectionMetrics: undefined,
}));
const dynamodb = new DynamoDBService({
region: "us-east-1",
tableName: "table-axample-qa",
});
const newItem = {
id: 88,
firstName: "Carry",
lastName: "Morgan",
};
const response = await dynamodb.create({ newItem });
const expectedResponse = {
status: "success",
response: {
$metadata: {
httpStatusCode: 200,
requestId: expect.any(String),
extendedRequestId: undefined,
cfId: undefined,
attempts: 1,
totalRetryDelay: 0,
},
Attributes: undefined,
ConsumedCapacity: undefined,
ItemCollectionMetrics: undefined,
},
};
expect(response).toEqual(expectedResponse);
expect(mockSend).toHaveBeenCalled();
});
test("fail to create", async () => {
// MockError
mockSend.mockImplementation(() => {
const error = new Error(
"One or more parameter values were invalid: Missing the key contentType in the item"
);
error.name = "ValidationException";
throw error;
});
const dynamodb = new DynamoDBService({
region: "us-east-1",
tableName: "table-axample-qa",
});
const newItem = {
id: 88,
firstName: "Carry",
lastName: "Morgan",
};
const response = await dynamodb.create({ newItem });
const expectedResponse = {
status: "fail",
error: expect.any(Object),
errorName: "ValidationException",
};
expect(response).toEqual(expectedResponse);
expect(mockSend).toHaveBeenCalled();
});
});