web-dev-qa-db-fra.com

Unittest sur AWS Lambda

J'ai un script python que je lance sur AWS. Le script contient la fonction lambda_handler(event, context) appelée par AWS. Maintenant, j'aimerais créer une nouvelle fonction lambda faisant office de test unitaire. Un schéma de test unitaire typique est défini comme suit:

import unittest

def my_function(a):
    return a + 1

class Test(unittest.TestCase):

    def test_correct(self):
        self.assertEqual( my_function(1), 2)

if __== '__main__':
    unittest.main()

Dans AWS, la fonction lambda_handler(event, context) est appelée. Comment puis-je utiliser la unittest_lambda_handler(event, context) pour effectuer le test unitaire?

Je suppose donc que mon code (dans le script de test unitaire) devrait ressembler à ceci:

import main_lambda_function
import unittest

    def unittest_lambda_handler(event, context):
         #what should this function do?

    class MyTest(unittest.TestCase):
         def return_type(self,event, context):
            self.assertTrue(isinstance(main_lambda_function.lambda_handler(event, context),int))

Si c’est le cas, que devrait faire unittest_lambda_handler?

4
Titus Pullo

Vous ne savez pas combien de personnes savent que dans la plupart des cas, vous n'avez même pas besoin d'une bibliothèque de 3ème partie pour intercepter les appels boto3. Botocore fournit stubber prêt à l'emploi Référence

Cette classe vous permettra d'éliminer les demandes afin que vous n'ayez pas besoin d'appuyer sur un noeud final pour écrire des tests. Les réponses sont renvoyées premier entré, premier sorti. Si les opérations sont appelées dans le désordre, ou sans autre réponse en file d'attente, une erreur est générée.

7
b.b3rn4rd

Si vous mettez unittest.main() dans le gestionnaire lambda (unittest_lambda_handler), les tests ne seront pas découverts et la fonction échouera toujours puisque main () appelle par défaut sys.exit (..) (voir https://github.com/python/cpython/blob/master/Lib/unittest/main.py#L273 ). Vous pouvez remplacer ce comportement avec unittest.main(exit=False) mais vos tests ne seront toujours pas découverts.

La solution la plus simple consiste probablement à exécuter le test directement. Donc dans votre exemple:

def unittest_lambda_handler(event, context):
    unittest.TextTestRunner().run(
        unittest.TestLoader().loadTestsFromTestCase(MyTest))

class MyTest(unittest.TestCase):
    def return_type(self,event, context):
        self.assertTrue(isinstance(
            main_lambda_function.lambda_handler(event, context),int))
3
oneschilling

Eh bien, essayons de diviser la question en 2 parties: les tests unitaires et les tests d'intégration (ou l'application dans son ensemble). Si vous travaillez dans un projet local, utilisez virtual env, gérez vos dépendances et utilisez votre IDE préféré, vous pouvez toujours tout exécuter à tout moment, ce qui signifie que si le test unitaire veut vérifier la fonction add_car(car), eh bien en théorie, vous ne le ferez pas. t besoin de tester le cycle de vie. Je veux dire, appelez le gestionnaire pour qu'il redirige vers cette capacité (ce serait un autre test comme un test de composant ou, selon votre conception, un test d'intégration). 

Cependant, que se passe-t-il si votre add_car(car) utilise une dynamo ou un RDS (tout autre service AWS)? Bien pour ces cas et pour les tests d'intégration et de composants, vous pouvez utiliser placebo : https://github.com/garnaat/placebo . Comme leur description l'indique: "simulez des appels boto3 qui ressemblent à des appels normaux mais qui n'ont en réalité aucun effet". Il existe un très bon exemple dans cet article: https://serverless.zone/unit-and-integration-testing-for-lambda-fc9510963003 . Il existe également d’autres bibliothèques telles que https://github.com/spulec/moto , vous trouverez des exemples ici https://www.programcreek.com/python/exemple/105256/moto.mock_ec2

Jetez également un coup d’œil aux recommandations que les gars de ClaudiaJS (je sais, c’est Javascript mais les concepts sont vraiment bons) à propos de la conception de fonctions Lambdas testables. https://claudiajs.com/tutorials/designing-testable-lambdas.html

Bonne chance!

1
Juan Urrego

localstack peut être d'intérêt ici.

LocalStack fournit un cadre de test/mocking facile à utiliser pour développer des applications Cloud. Actuellement, l'accent est principalement mis sur la prise en charge de la pile de nuages ​​AWS.

0
Nathan Vērzemnieks

L'appel de lambda_handler(event, context) est un test d'intégration. Cette fonction est destinée à exécuter le flux de travail complet de votre script Lambda.

Vous devez séparer les fonctions que vous voulez tester en dehors de la fonction lambda_handler ():

def my_function(a):
    return a + 1

def lambda_handler(event, context):
    return my_function(event)

Vous pouvez maintenant tester my_function en tant que test unitaire normal.

Si vous voulez exécuter des tests simulés sur votre lambda_handler, vous pouvez créer une autre fonction qui exécute votre code pour le test avec des simulacres:

import botocore.session
from botocore.stub import Stubber

def my_s3_function(s3):
    return s3.list_objects(Bucket='test-bucket')

def main_workflow(s3):
    return my_s3_function(s3)

def lambda_handler(event, context):
    s3 = botocore.session.get_session().create_client('s3')
    return main_workflow(s3)

def test_mock_handler(event, context):
    # configure mocks here - for example using stubber for boto3
    s3 = botocore.session.get_session().create_client('s3')
    stubber = Stubber(s3)
    return main_workflow(s3)

Le concept principal est que lambda_handler() sert uniquement à gérer votre code lorsqu'il est exécuté dans l'environnement Lambda complet et normal. Ce code devrait idéalement juste être configuré puis invoquer une ou plusieurs autres fonctions. Vous pouvez ensuite tester à l'unité ces fonctions isolées et écrire des gestionnaires supplémentaires pour appeler votre code dans différentes configurations de test.

0
Wheat