Menu Docs
Página inicial do Docs
/ /
Serviços Atlas App
/

Testar Atlas Functions

Nesta página

  • Antes de começar
  • Testes de unidade para funções
  • Obtenha uma cópia local do seu App Services App
  • Criar uma nova função
  • Gravar código de função
  • Função de exportação para uso em testes de unidade
  • Código de função exportado do teste de unidade
  • Simular serviços
  • Testes de integração para funções
  • Crie um aplicativo de teste
  • Teste em ambiente ao vivo

Esta página descreve algumas estratégias que você pode utilizar para testar suas Funções do Atlas.

Devido às diferenças entre o tempo de execução das funções JavaScript e o tempo de execução padrão do Node.js, você deve levar em consideração algumas considerações exclusivas ao testar as funções. Esta página aborda como lidar com a exclusividade das funções.

Você precisará do seguinte para testar uma Função do Atlas:

  • Um aplicativo Atlas App Services. Para saber como criar uma, consulte Criar uma aplicação.

  • Um método de implementação de código para configurar aseu aplicativo. Escolha um dos seguintes:

    • Uma cópia da App Services CLI instalada e adicionada ao seu sistema local PATH. Para saber como, consulte Instalar App Services CLI.

    • Um repositório GitHub configurado para armazenar e implantar arquivos de configuração para seu aplicativo. Para saber como configurar um, consulte Implantar automaticamente com o GitHub.

Você pode validar a funcionalidade de suas funções com testes de unidade. Use qualquer framework de testes Node.js compatível estrutura de teste para testar funções. Os exemplos nesta página usam a estrutura de teste do Jest do .

Você deve usar módulos CommonJS para escrever testes de unidade para Functions.

1

Puxe a configuração mais recente do seu aplicativo do servidor.

appservices pull --remote <App ID>

Puxe a configuração mais recente do seu aplicativo do Github.

git pull <Remote Name> <Branch name>
2

Crie uma nova função. Nos arquivos de configuração do aplicativo, crie um novo arquivo JavaScript no functions diretório para sua função.

touch functions/hello.js

Você também precisa adicionar informações de configurações para a Função em functions/config.json.

{
"name": "hello",
"private": false,
"run_as_system": true
},

Dica

Veja também:

Para obter mais informações sobre como criar uma nova função, consulte Definir uma função.

3

Para tornar seu código de função fácil de testar, mantenha-o modular separando suas preocupações em componentes distintos. Você deve manter toda a lógica da Função no arquivo definido na etapa anterior. Você não pode realizar importações relativas de outros arquivos em seu projeto em um arquivo de Função. Você também pode importar dependências usando npm.

Você deve exportar sua função atribuindo-a a exports.

hello.js
function greet(word) {
return "hello " + word;
}
function greetWithPunctuation(word, punctuation) {
return greet(word) + punctuation;
}
// Function exported to App Services
exports = greetWithPunctuation;
4

Para exportar seu código para usar em arquivos de teste de unidade Node.js separados, você deve usar a sintaxe CommonJS module.exports .

Essa sintaxe não é compatível com o tempo de execução de funções. O ambiente do Atlas Function não fornece o Node.js global module. Para exportar módulos para seus testes de unidade e, ao mesmo tempo, manter o arquivo compatível com as funções, envolva a declaração module.exports com uma verificação para ver se o objeto global module existe.

functions/hello.js
function greet(word) {
return "hello " + word;
}
function greetWithPunctuation(word, punctuation) {
return greet(word) + punctuation;
}
// Function exported to App Services
exports = greetWithPunctuation;
// export locally for use in unit test
if (typeof module !== "undefined") {
module.exports = { greet, greetWithPunctuation };
}
5

Agora você pode escrever testes de unidade para os módulos que você exportou do arquivo de Função. Crie um arquivo de teste para o arquivo de Função em um diretório test separado em algum lugar do seu projeto.

mkdir -p test/unit
touch test/unit/hello.test.js

Importe os módulos exportados na etapa anterior e adicione testes de unidade.

test/unit/hello.test.js
const { greet, greetWithPunctuation } = require("../../functions/hello");
test("should greet", () => {
const helloWorld = greet("world");
expect(helloWorld).toBe("hello world");
});
test("should greet with punctuation", () => {
const excitedHelloWorld = greetWithPunctuation("world", "!!!");
expect(excitedHelloWorld).toBe("hello world!!!");
});

Para escrever testes de unidade para funções que usam o objeto de contexto global ou um dos outros módulos globais que as funções expõem, você deve criar simulações de seu comportamento.

Neste exemplo, a Função referencia um Valor do App Services via context.values.get() e cria um ObjectId utilizando o módulo global BSON.

accessAppServicesGlobals.js
function accessAppServicesGlobals() {
const mongodb = context.services.get("mongodb-atlas");
const objectId = BSON.ObjectId()
// ... do stuff with these values
}
exports = accessAppServicesGlobals;
if (typeof module !== "undefined") {
module.exports = accessAppServicesGlobals;
}

Anexe essas simulações ao namespace global do Node.js. Isso permite que você chame as simulações em seus testes da mesma forma que faz no tempo de execução das funções.

global.context = {
// whichever global context methods you want to mock.
// 'services', 'functions', values, etc.
}
// you can also mock other Functions global modules
global.BSON = {
// mock methods
}

Você também pode declarar e remover essas simulações nos blocos de configuração e desmontagem para que eles não poluam o namespace global.

// adds context mock to global namespace before each test
beforeEach(() => {
global.context = {
// your mocking services
};
});
// removes context from global namespace after each test
afterEach(() => {
delete global.context;
});
test("should perform operation using App Services globals", () => {
// test function that uses context
});

Exemplo

Zombando de uma função que acessa o contexto

A função neste exemplo acessa um valor do App Services e o retorna.

helloWithValue.js
function greet() {
const greeting = context.values.get("greeting"); // the greeting is 'beautiful world'
return "hello " + greeting;
}
exports = greet;
if (typeof module !== "undefined") {
module.exports = greet;
}

Agora crie um arquivo de teste helloWithValue.test.js. O arquivo de teste contém o seguinte:

  • Importe a função exportada de helloWithValue.js.

  • Uma simulação de context.values.get(). Envolva a simulação na configuração e desmonte os blocos para que não polua o namespace global.

  • Um teste da função importada que usa o modelo.

helloWithValue.test.js
// import the function
const greet = require("../../functions/helloWithValue");
// wrap the mock in beforeEach/afterEach blocks to avoid
// pollution of the global namespace
beforeEach(() => {
// mock of context.values.get()
global.context = {
values: {
get: (val) => {
const valsMap = {
greeting: "magnificent morning",
};
return valsMap[val];
},
},
};
});
afterEach(() => {
// delete the mock to not pollute global namespace
delete global.context;
});
// test function using mock
test("should greet with value", () => {
const greeting = greet();
expect(greeting).toBe("hello magnificent morning");
});

Você deve executar testes de integração em todas as funções antes de implantá-las em ambientes de produção. Isso é especialmente importante porque o tempo de execução do Atlas Function JavaScript é diferente do tempo de execução padrão do Node.js Erros inesperados podem ocorrer se você não testar as funções implantadas no App Services.

Não existe uma maneira única de escrever testes de integração para Functions. Como as funções podem ser usadas em uma variedade de contextos diferentes para diferentes propósitos, Cada caso de uso requer uma estratégia de teste de integração diferente.

Por exemplo, a maneira como você cria um teste de integração para uma função que invoca de um cliente Device SDK é diferente da forma como você testaria uma função trigger de banco de dados.

No entanto, existem algumas etapas gerais que você pode seguir para escrever testes de integração para funções. Em um alto nível, essas etapas são:

  1. Crie um aplicativo de teste com a mesma configuração que seu app de produção.

  2. Grave testes de integração que interajam com suas funções distribuídas em um ambiente de teste ao vivo.

O restante desta seção explica como implementar testes de integração para seu aplicativo em mais detalhes.

Dica

Veja também:

Para obter mais informações sobre os aspectos exclusivos do tempo de execução do JavaScript de funções, consulte:

Para obter mais informações sobre os diferentes casos de uso de funções, consulte Quando usar funções.

1

Crie um aplicativo para fins de teste que tenha a mesma configuração do seu aplicativo de produção, exceto usando diferentes fontes de dados e configuração de backend.

Para obter mais informações sobre como criar vários aplicativos com a mesma configuração, consulte Configurar um ambiente de aplicativo.

2

Depois de distribuir o aplicativo de teste, teste sua funcionalidade usando a linguagem e a estrutura de teste de sua preferência.

Os SDKs do cliente do Realm são úteis para testar aplicativos. Esses SDKs fornecem acesso de primeira classe aos App Services. Em seu pacote de testes, você pode se conectar ao seu aplicativo de teste com um Realm SDK. Teste a interação com o aplicativo usando o Realm SDK.

Exemplo

Testando uma função de trigger do banco de dados

Este exemplo usa o Node.js SDK do Realm e o framework de testes do Jest para testar um gatilho de banco de dados.

A função Trigger cria uma visão materializada do total de vendas de um produto sempre que uma nova venda é feita.

O gatilho é acionado toda vez que uma entrada é adicionada à tabela sales. Ele incrementa o campo total_sales na tabela total_sales_materialized por um.

O gatilho de banco de dados tem a seguinte configuração:

triggers/materializeTotalSales.json
{
"id": "62bb0d9f852c6e062432c454",
"name": "materializeTotalSales",
"type": "DATABASE",
"config": {
"operation_types": ["INSERT"],
"database": "store",
"collection": "sales",
"service_name": "mongodb-atlas",
"match": {},
"project": {},
"full_document": true,
"full_document_before_change": false,
"unordered": false,
"skip_catchup_events": false
},
"disabled": false,
"event_processors": {
"FUNCTION": {
"config": {
"function_name": "materializeTotalSales"
}
}
}
}

O trigger invoca a seguinte função:

functions/materializeTotalSales.js
exports = function (changeEvent) {
const {
fullDocument: { productId },
} = changeEvent;
const totalSalesMaterialization = context.services
.get("mongodb-atlas")
.db("store")
.collection("total_sales_materialized");
totalSalesMaterialization.updateOne(
{ _id: productId },
{ $inc: { total_sales: 1 } },
{ upsert: true }
);
};

Este exemplo testa o trigger usando o Node.js Realm SDK para interagir com o MongoDB Atlas. Você também pode usar qualquer Realm SDK com a MongoDB Query API ou um dos drivers do MongoDB para consultar o MongoDB Atlas e testar um gatilho de banco de dados (trigger).

test/integration/materializeTotalSales.test.js
const { app_id } = require("../../root_config.json");
const Realm = require("realm");
const { BSON } = require("realm");
let user;
const app = new Realm.App(app_id);
const sandwichId = BSON.ObjectId();
const saladId = BSON.ObjectId();
// utility function
async function sleep(ms) {
await new Promise((resolve) => setTimeout(resolve, ms));
}
// Set up. Creates and logs in a user, which you need to query MongoDB Atlas
// with the Realm Node.js SDK
beforeEach(async () => {
const credentials = Realm.Credentials.anonymous();
user = await app.logIn(credentials);
});
// Clean up. Removes user and data created in the test.
afterEach(async () => {
const db = user.mongoClient("mongodb-atlas").db("store");
await db.collection("sales").deleteMany({});
await db.collection("total_sales_materialized").deleteMany({});
await app.deleteUser(user);
});
test("Trigger creates a new materialization", async () => {
const sales = user
.mongoClient("mongodb-atlas")
.db("store")
.collection("sales");
await sales.insertOne({
_id: BSON.ObjectId(),
productId: sandwichId,
price: 12.0,
timestamp: Date.now(),
});
// give time for the Trigger to execute on Atlas
await sleep(1000);
const totalSalesMaterialized = user
.mongoClient("mongodb-atlas")
.db("store")
.collection("total_sales_materialized");
const allSandwichSales = await totalSalesMaterialized.findOne({
_id: sandwichId,
});
// checks that Trigger increments creates and increments total_sales
expect(allSandwichSales.total_sales).toBe(1);
});
test("Trigger updates an existing materialization", async () => {
const sales = user
.mongoClient("mongodb-atlas")
.db("store")
.collection("sales");
await sales.insertOne({
_id: BSON.ObjectId(),
productId: saladId,
price: 15.0,
timestamp: Date.now(),
});
await sales.insertOne({
_id: BSON.ObjectId(),
productId: saladId,
price: 15.0,
timestamp: Date.now(),
});
// give time for Trigger to execute on Atlas
await sleep(1000);
const totalSalesMaterialized = user
.mongoClient("mongodb-atlas")
.db("store")
.collection("total_sales_materialized");
const allSaladSales = await totalSalesMaterialized.findOne({
_id: saladId,
});
// checks that Trigger increments total_sales for each sale
expect(allSaladSales.total_sales).toBe(2);
});

Voltar

Dependências externas