2
votes

Moto ne semble pas se moquer des interactions aws dans un pytest

Dites que je veux me moquer de ce qui suit:

>           raise ProfileNotFound(profile=profile_name)
E           botocore.exceptions.ProfileNotFound: The config profile (foo) could not be found

Comment puis-je commencer à me moquer de cela avec in pytest? Je pourrais créer des objets simulés en créant une classe factice et les attributs nécessaires, mais je soupçonne que ce n'est pas la bonne approche.

Quelques détails supplémentaires, voici ce que j'essaye de tester:

@mock_sts
def test_check_aws_profile(self):
    session = boto3.Session(profile_name='foo')
    client = session.client('sts')
    client.get_caller_identity().get('Account')

Mais je n'arrive pas à être en mesure de corriger correctement ce problème. J'essaie de faire en sorte que je puisse patcher la session et les appels de fonction dans cette méthode

J'ai essayé d'utiliser moto et j'ai obtenu ceci:

def test_check_aws_profile(self, mocker):
    mocked_boto3 = mocker.patch('myapp.services.utils.boto3.Session')
    mocker.patch(mocked_boto3.client.get_caller_identity.get, return_value='foo-account-id')
    assert 'foo-account-id' == my_func('foo')

#in myapp.services.utils.py
def my_func(profile):
    session = boto3.Session(profile_name=profile)
    client = session.client('sts')
    aws_account_number = client.get_caller_identity().get('Account')
    return aws_account_number

Mais je rencontre

session = boto3.Session(profile_name=profile)
resource = session.resource('iam')
iam_users = resource.users.all()
policies = resource.policies.filter(Scope='AWS', OnlyAttached=True, PolicyUsageFilter='PermissionsPolicy')

Il semble donc qu'il ne se moque de rien: |

Éditer:

Il s'avère que vous devez avoir les informations d'identification simulées dans un fichier de configuration et d'informations d'identification pour que cela fonctionne.


1 commentaires

"Comment est-ce que je vais commencer ...?" est une question trop large pour SO, mais peut-être jeter un œil à docs.getmoto.org/en/latest


3 Réponses :


1
votes

Je ne sais pas exactement ce que vous voulez, alors je vais vous donner quelque chose pour commencer.

Vous laissez unittest.mock se moquer de tout pour vous, par exemple. (Lecture utile: https://docs.python.org/3/library/unittest.mock.html )

module.py :

$ pytest
================================ test session starts ================================
platform darwin -- Python 3.7.6, pytest-5.3.2, py-1.8.1, pluggy-0.13.1
rootdir: /private/tmp/one
collected 1 item                                                                    

test_module.py .                                                              [100%]

================================= 1 passed in 0.09s =================================

test_module.py :

from unittest.mock import patch

import module

@patch("module.boto3")  # this creates mock which is passed to test below
def test_function(mocked_boto):
    # mocks below are magically created by unittest.mock when they are accessed
    mocked_session = mocked_boto.Session()
    mocked_client = mocked_session.resource()
    mocked_identity = mocked_client.get_caller_identity()

    # now mock the return value of .get()
    mocked_identity.get.return_value = "foo-bar-baz"

    result = module.function()
    assert result == "foo-bar-baz"

    # we can make sure mocks were called properly, for example
    mocked_identity.get.assert_called_once_with("Account")

Résultats du test pytest:

import boto3

def function():
    session = boto3.Session(profile_name="foobar")
    client = session.resource("sts")
    return client.get_caller_identity().get('Account')

Je recommanderais également d'installer pytest-socket et d'exécuter pytest --disable-socket pour vous assurer que vos tests ne communiquent pas avec le réseau par accident.


6 commentaires

Ah, cela pourrait fonctionner, j'ai essayé quelque chose de similaire mais je n'arrive pas à le faire fonctionner correctement. J'ai mis à jour ma question avec l'exemple de code que j'utilise.


@ Stupid.Fat.Cat J'ai mis à jour le code dans la réponse pour correspondre à votre question mise à jour.


Évalué en raison de la réinvention de la roue. Cette approche est totalement erronée. Le test de tout code qui interagit avec AWS via boto3 doit utiliser la classe botocore.stub pour stubbing le transport à la limite appropriée, et / ou une maquette AWS de niveau supérieur telle que moto .


C'est exactement pourquoi j'ai dit au début "Je ne sais pas exactement ce que vous voulez". Si l'idée est simplement d'exclure tous les trucs boto3 du code en cours de test, pourquoi ne pas s'en moquer? Si l'idée est de tester les interactions avec AWS, c'est une autre histoire.


Si vous n'êtes pas sûr de ce que souhaite OP, posez des questions de clarification dans les commentaires et / ou votez pour clore la question car elle n'est pas claire. Cette réponse les envoie vraiment dans la mauvaise direction (par exemple, boto3.Session effectue une boto3.Session des informations d'identification que vous ne voulez pas faire lors du test - et ce sera beaucoup de travail supplémentaire inutile pour empêcher correctement cela en utilisant unittest.mock uniquement).


Quant à «pourquoi ne pas s'en moquer [le module boto3]», c'est une question raisonnable: si vous faites cela, vous manquez des changements dans boto3 lui-même. Le meilleur endroit pour se moquer est à la limite de l'accès au réseau.



1
votes

Bien qu'il n'y ait rien de mal à patcher manuellement boto à l'aide de mock.patch , vous pouvez également envisager d'utiliser un utilitaire de test de niveau supérieur comme moto .


1 commentaires

J'ai mis à jour la question avec moto avec quelques problèmes



0
votes

Si vous souhaitez utiliser moto , vous pouvez utiliser la variable d'environnement AWS_SHARED_CREDENTIALS_FILE pour la pointer vers un fichier d'informations d'identification factice qui peut être conservé dans le dossier tests. Vous pouvez y définir vos profils. Exemple:

Fichiers: test_stuff.py. dummy_aws_credentials

test_stuff.py:

[foo]
aws_access_key_id = mock
aws_secret_access_key = mock

dummy_aws_credentials:

import os
from pathlib import Path
import boto3
import pytest
from moto import mock_sts


@pytest.fixture(scope='module')
def aws_credentials():
    """Mocked AWS Credentials for moto."""
    moto_credentials_file_path = Path(__file__).parent.absolute() / 'dummy_aws_credentials'
    os.environ['AWS_SHARED_CREDENTIALS_FILE'] = str(moto_credentials_file_path)


@mock_sts
def test_check_aws_profile(aws_credentials):
    session = boto3.Session(profile_name='foo')
    client = session.client('sts')
    client.get_caller_identity().get('Account')


0 commentaires