During work on project pl-k8s-kubernetes, I encounter an issue with the unit test, which can’t pass, here is the example shows what’s wrong with that.
First create a source code with static method and named this file as aws_service_utils.py
import boto3
import json
class AwsSecretManager:
@staticmethod
def create_secret(secret_name, json_content):
secretsmanager_client = boto3.client('secretsmanager')
# Create a secret
response = secretsmanager_client.create_secret(
Name=secret_name, # Name of the secret
SecretString=json.dumps(json_content) # Secret data as a JSON string
)
return response
@staticmethod
def delete_secret(secret_id):
secretsmanager_client = boto3.client('secretsmanager')
# Create a secret
response = secretsmanager_client.delete_secret(
Name=secret_name, # Name of the secret
)
return response
Second crate a method based on this method, backup_delete_secret.py
from aws_service_utils import AwsSecretManager as SM
def operator(secret_name, json_content):
print("Create secret")
response = SM.create_secret(secret_name, json_content)
if response:
SM.delete_secret(secret_name)
print('Delete secret success')
else:
print('Create secret fail, no need to delete secret')
Third create unittest cases based on those two methods
from mock import patch, Mock
import boto3
import unittest
import sys
sys.path.append(".")
from backup_delete_secret import operator
import aws_service_utils
class TestBackupAndDelete(unittest.TestCase):
@patch('aws_service_utils.AwsSecretManager.create_secret')
@patch('aws_service_utils.AwsSecretManager.delete_secret')
def test_operator_alway_work(self, mock_delete, mock_create):
operator("test", {
"name":"wguan"
})
mock_create.return_value = {
"arn":"1234"
}
self.assertEqual(mock_create.call_count, 1)
self.assertEqual(mock_delete.call_count, 1)
def test_operator_related_order(self):
aws_service_utils.AwsSecretManager.create_secret = Mock(return_value={
"arn": "1234"
})
aws_service_utils.AwsSecretManager.delete_secret = Mock()
operator("test", {
"name":"wguan"
})
self.assertEqual(aws_service_utils.AwsSecretManager.create_secret.call_count, 1)
self.assertEqual(aws_service_utils.AwsSecretManager.delete_secret.call_count, 1)
In the above we have defined two similar methods, one is use patch to define a mock, it will release the mock object after the unit test finish, but if we define a function to mock object as follow
aws_service_utils.AwsSecretManager.create_secret = Mock(return_value={
"arn": "1234"
})
aws_service_utils.AwsSecretManager.delete_secret = Mock()
it won’t release this static method, you can verify use the following method to verify. If you write unit test with patch method, the following will seccuess, but otherwise the following method may fail. But it may also success, this is relate another question, how does nose2 load unittest cases. This can be verify by yourself. If you want to let the case work well, there is another metod to fix, reload the package.
import boto3
import unittest
import sys
sys.path.append(".")
from moto import mock_secretsmanager
from aws_service_utils import AwsSecretManager as SM
class TestAwsSecretManager(unittest.TestCase):
@mock_secretsmanager
def test_creat_secret(self):
response = SM.create_secret("test", {
"name": "wguan"
})
self.assertEqual(response['ResponseMetadata']['HTTPStatusCode'], 200)
Summary:
- For static method, never assign Mock object to static function, it will effect all the unit test after this case has been called
- The order of nose2 load unit test cases is related the name of case name
- Import AwsSecretManager in the last file, but it doesn’t import again, the root cause is python only import once, so there is anthor method to fix it, just use importlib.reload(package)