필수 과제
2주차에 진행한 server list 확장 코드에서 tox를 통해 unit test를 실행시키면 Test Failed가 발생합니다.
openstackclient.tests.unit.compute 경로에 있는 server list에 대한 unit test를 수정하여 tox 실행시 모든 unit test에서 성공하도록 해주세요
제출 내용 1: 수정된 unit test code (스크린샷 또는 실제 코드)
제출 내용 2: tox를 통해 모든 unit test 통과한 화면
결과
- 모든 self.data에 user_name과 project_name 컬럼 추가
self.data = tuple(
(
s.id,
s.name,
s.status,
server.AddressesColumn(s.addresses),
# Image will be an empty string if boot-from-volume
self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
# [YS] Add value
s.user_name,
s.project_name,
self.flavor.name,
)
for s in self.servers
)
- Test에 사용되는 mock 객체에 추가된 컬럼에 해당되는 값 추가 (현재 테스트는 값만 있으면 되기 때문에 ‘N/A’)
class TestServer(compute_fakes.TestComputev2):
def setUp(self):
super().setUp()
# Set object attributes to be tested. Could be overwritten in subclass.
self.attrs = {}
def setup_sdk_servers_mock(self, count):
servers = compute_fakes.create_servers(
attrs=self.attrs,
count=count,
)
#[YS] Add Value
for mock_server in servers:
mock_server.user_name = 'N/A'
mock_server.project_name = 'N/A'
- TestServerList와 TestServerListV273에 사용되는 columns 값 수정
# [YS] Create new col data
# Columns to be listed up.
columns = (
'ID',
'Name',
'Status',
'Networks',
'Image',
'User name',
'Project name',
'Flavor',
)
# columns = (
# 'ID',
# 'Name',
# 'Status',
# 'Networks',
# 'Image',
# 'Flavor',
# )
columns_long = (
'ID',
'Name',
'Status',
'Task State',
'Power State',
'Networks',
'Image Name',
'Image ID',
'User name',
'Project name',
'Flavor Name',
'Flavor ID',
'Availability Zone',
'Pinned Availability Zone',
'Host',
'Properties',
'Scheduler Hints',
)
# columns_long = (
# 'ID',
# 'Name',
# 'Status',
# 'Task State',
# 'Power State',
# 'Networks',
# 'Image Name',
# 'Image ID',
# 'Flavor Name',
# 'Flavor ID',
# 'Availability Zone',
# 'Pinned Availability Zone',
# 'Host',
# 'Properties',
# 'Scheduler Hints',
# )
columns_all_projects = (
'ID',
'Name',
'Status',
'Networks',
'Image',
'User name',
'Project name',
'Flavor',
'Project ID',
)
- 결과
수행 과정
1. Run tox
A. server.py 파일 수정 전
1개 Fail
Failed 1 tests - output below:
==============================
openstackclient.tests.unit.compute.v2.test_server.TestServerCreate.test_server_create_with_block_device_from_file
Captured traceback:
Traceback (most recent call last):
File "C:\Users\rladb\contribution-academy\OCA-OpenStack\python-openstackclient\openstackclient\tests\unit\compute\v2\test_server.py", line 2735, in test_server_create_with_block_device_from_file
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\rladb\contribution-academy\OCA-OpenStack\python-openstackclient\openstackclient\tests\unit\utils.py", line 86, in check_parser
parsed_args = cmd_parser.parse_args(args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\argparse.py", line 1874, in parse_args
args, argv = self.parse_known_args(args, namespace)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\argparse.py", line 1907, in parse_known_args
namespace, args = self._parse_known_args(args, namespace)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\argparse.py", line 2128, in _parse_known_args
start_index = consume_optional(start_index)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\argparse.py", line 2068, in consume_optional
take_action(action, args, option_string)
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\argparse.py", line 1983, in take_action
action(self, namespace, argument_values, option_string)
File "C:\Users\rladb\contribution-academy\OCA-OpenStack\python-openstackclient\openstackclient\compute\v2\server.py", line 1068, in __call__
with open(values) as fh:
^^^^^^^^^^^^
PermissionError: [Errno 13] Permission denied: 'C:\\Users\\rladb\\AppData\\Local\\Temp\\tmpzjz1_ewu'
openstackclient.tests.unit.compute.v2.test_server.TestServerListV273.test_server_list_v269_with_partial_constructs
B. server.py 파일 수정 후
26개 Fail
Failed 26 tests output below:
==============================
openstackclient.tests.unit.compute.v2.test_server.TestServerCreate.test_server_create_with_block_device_from_file
Captured traceback:
Traceback (most recent call last):
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\openstackclient\tests\unit\compute\v2\test_server.py", line 2735, in test_server_create_with_block_device_from_file
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\openstackclient\tests\unit\utils.py", line 86, in check_parser
parsed_args = cmd_parser.parse_args(args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\argparse.py", line 1874, in parse_args
args, argv = self.parse_known_args(args, namespace)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\argparse.py", line 1907, in parse_known_args
namespace, args = self._parse_known_args(args, namespace)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\argparse.py", line 2128, in _parse_known_args
start_index = consume_optional(start_index)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\argparse.py", line 2068, in consume_optional
take_action(action, args, option_string)
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\argparse.py", line 1983, in take_action
action(self, namespace, argument_values, option_string)
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\openstackclient\compute\v2\server.py", line 1068, in __call__
with open(values) as fh:
^^^^^^^^^^^^
PermissionError: [Errno 13] Permission denied: 'C:\\Users\\rladb\\AppData\\Local\\Temp\\tmpzjz1_ewu'
openstackclient.tests.unit.compute.v2.test_server.TestServerListV273.test_server_list_v269_with_partial_constructs
Captured traceback:
Traceback (most recent call last):
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\openstackclient\tests\unit\compute\v2\test_server.py", line 5547, in test_server_list_v269_with_partial_constructs
self.assertEqual(expected_row, partial_server)
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\.tox\py3\Lib\site packages\testtools\testcase.py", line 419, in assertEqual
self.assertThat(observed, matcher, message)
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\.tox\py3\Lib\site packages\testtools\testcase.py", line 509, in assertThat
raise mismatch_error
testtools.matchers._impl.MismatchError: !=:
reference = ('server id 95a56bfc4xxxxxx28d7e418bfd97813a',
None,
'UNKNOWN',
AddressesColumn({}),
'',
'')
actual = ('server id 95a56bfc4xxxxxx28d7e418bfd97813a',
None,
'UNKNOWN',
AddressesColumn({}),
'',
'N/A',
<Mock name='mock.get().name' id='3079133239440'>,
'')
openstackclient.tests.unit.compute.v2.test_server.TestServerList.test_server_list_all_projects_option
Captured traceback:
Traceback (most recent call last):
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\openstackclient\tests\unit\compute\v2\test_server.py", line 4794, in test_server_list_all_projects_option
self.assertEqual(self.columns_all_projects, columns)
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\.tox\py3\Lib\site packages\testtools\testcase.py", line 419, in assertEqual
self.assertThat(observed, matcher, message)
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\.tox\py3\Lib\site packages\testtools\testcase.py", line 509, in assertThat
raise mismatch_error
testtools.matchers._impl.MismatchError: !=:
reference = ('ID', 'Name', 'Status', 'Networks', 'Image', 'Flavor', 'Project ID')
actual = ('ID',
'Name',
'Status',
'Networks',
'Image',
'User name',
'Project name',
'Flavor',
'Project ID')
openstackclient.tests.unit.compute.v2.test_server.TestServerListV273.test_server_list_with_changes_before
Captured traceback:
Traceback (most recent call last):
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\openstackclient\tests\unit\compute\v2\test_server.py", line 5473, in test_server_list_with_changes_before
self.assertCountEqual(self.columns, columns)
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\unittest\case.py", line 1233, in assertCountEqual
self.fail(msg)
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\unittest\case.py", line 703, in fail
raise self.failureException(msg)
AssertionError: Element counts were not equal:
First has 0, Second has 1: 'User name'
First has 0, Second has 1: 'Project name'
openstackclient.tests.unit.compute.v2.test_server.TestServerList.test_server_list_long_option
Captured traceback:
Traceback (most recent call last):
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\openstackclient\tests\unit\compute\v2\test_server.py", line 4764, in test_server_list_long_option
self.assertEqual(self.columns_long, columns)
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\.tox\py3\Lib\site packages\testtools\testcase.py", line 419, in assertEqual
self.assertThat(observed, matcher, message)
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\.tox\py3\Lib\site packages\testtools\testcase.py", line 509, in assertThat
raise mismatch_error
testtools.matchers._impl.MismatchError: !=:
reference = ('ID',
'Name',
'Status',
'Task State',
'Power State',
'Networks',
'Image Name',
'Image ID',
'Flavor Name',
'Flavor ID',
'Availability Zone',
'Pinned Availability Zone',
'Host',
'Properties',
'Scheduler Hints')
actual = ('ID',
'Name',
'Status',
'Task State',
'Power State',
'Networks',
'Image Name',
'Image ID',
'User name',
'Project name',
'Flavor Name',
'Flavor ID',
'Availability Zone',
'Pinned Availability Zone',
'Host',
'Properties',
'Scheduler Hints')
openstackclient.tests.unit.compute.v2.test_server.TestServerListV273.test_server_list_with_locked
Captured traceback:
Traceback (most recent call last):
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\openstackclient\tests\unit\compute\v2\test_server.py", line 5425, in test_server_list_with_locked
self.assertCountEqual(self.columns, columns)
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\unittest\case.py", line 1233, in assertCountEqual
self.fail(msg)
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\unittest\case.py", line 703, in fail
raise self.failureException(msg)
AssertionError: Element counts were not equal:
First has 0, Second has 1: 'User name'
First has 0, Second has 1: 'Project name'
openstackclient.tests.unit.compute.v2.test_server.TestServerList.test_server_list_long_with_host_status_v216
Captured traceback:
Traceback (most recent call last):
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\openstackclient\tests\unit\compute\v2\test_server.py", line 5278, in test_server_list_long_with_host_status_v216
self.assertEqual(self.columns_long, columns)
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\.tox\py3\Lib\site packages\testtools\testcase.py", line 419, in assertEqual
self.assertThat(observed, matcher, message)
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\.tox\py3\Lib\site packages\testtools\testcase.py", line 509, in assertThat
raise mismatch_error
testtools.matchers._impl.MismatchError: !=:
reference = ('ID',
'Name',
'Status',
'Task State',
'Power State',
'Networks',
'Image Name',
'Image ID',
'Flavor Name',
'Flavor ID',
'Availability Zone',
'Pinned Availability Zone',
'Host',
'Properties',
'Scheduler Hints')
actual = ('ID',
'Name',
'Status',
'Task State',
'Power State',
'Networks',
'Image Name',
'Image ID',
'User name',
'Project name',
'Flavor Name',
'Flavor ID',
'Availability Zone',
'Pinned Availability Zone',
'Host',
'Properties',
'Scheduler Hints')
openstackclient.tests.unit.compute.v2.test_server.TestServerList.test_server_list_n_option
Captured traceback:
Traceback (most recent call last):
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\openstackclient\tests\unit\compute\v2\test_server.py", line 4909, in test_server_list_n_option
self.assertEqual(self.columns, columns)
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\.tox\py3\Lib\site packages\testtools\testcase.py", line 419, in assertEqual
self.assertThat(observed, matcher, message)
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\.tox\py3\Lib\site packages\testtools\testcase.py", line 509, in assertThat
raise mismatch_error
testtools.matchers._impl.MismatchError: !=:
reference = ('ID', 'Name', 'Status', 'Networks', 'Image', 'Flavor')
actual = ('ID',
'Name',
'Status',
'Networks',
'Image',
'User name',
'Project name',
'Flavor')
openstackclient.tests.unit.compute.v2.test_server.TestServerListV273.test_server_list_with_unlocked_v273
Captured traceback:
Traceback (most recent call last):
File "C:\Users\rladb\contribution academy\OCA OpenStack\python openstackclient\openstackclient\tests\unit\compute\v2\test_server.py", line 5440, in test_server_list_with_unlocked_v273
self.assertCountEqual(self.columns, columns)
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\unittest\case.py", line 1233, in assertCountEqual
self.fail(msg)
File "C:\Users\rladb\AppData\Local\Programs\Python\Python311\Lib\unittest\case.py", line 703, in fail
raise self.failureException(msg)
AssertionError: Element counts were not equal:
...
A에서는 1개가 Fail되고 B에서는 26개가 Fail 되었다. 먼저 Server List에 해당하는 B를 고치고 A를 설명하도록 하겠다.
2. Trace back from the fail log
test_server.py에서 발생한 Fail 로그를 살펴보면 2가지 타입이 있다.
Type 1. assert가 expected_row에서 발생하고, mismatch_error로 끝난다.
File "C:\Users\rladb\contribution-academy\OCA-OpenStack\python-openstackclient\openstackclient\tests\unit\compute\v2\test_server.py", line 5547, in test_server_list_v269_with_partial_constructs
self.assertEqual(expected_row, partial_server)
File "C:\Users\rladb\contribution-academy\OCA-OpenStack\python-openstackclient\.tox\py3\Lib\site-packages\testtools\testcase.py", line 419, in assertEqual
self.assertThat(observed, matcher, message)
File "C:\Users\rladb\contribution-academy\OCA-OpenStack\python-openstackclient\.tox\py3\Lib\site-packages\testtools\testcase.py", line 509, in assertThat
raise mismatch_error
testtools.matchers._impl.MismatchError: !=:
reference = ('server-id-95a56bfc4xxxxxx28d7e418bfd97813a',
None,
'UNKNOWN',
AddressesColumn({}),
'',
'')
actual = ('server-id-95a56bfc4xxxxxx28d7e418bfd97813a',
None,
'UNKNOWN',
AddressesColumn({}),
'',
'N/A',
<Mock name='mock.get().name' id='3079133239440'>,
'')
Type 2. columns에서 발생하고 mismatch_error로 끝난다.
File "C:\Users\rladb\contribution-academy\OCA-OpenStack\python-openstackclient\openstackclient\tests\unit\compute\v2\test_server.py", line 4794, in test_server_list_all_projects_option
self.assertEqual(self.columns_all_projects, columns)
File "C:\Users\rladb\contribution-academy\OCA-OpenStack\python-openstackclient\.tox\py3\Lib\site-packages\testtools\testcase.py", line 419, in assertEqual
self.assertThat(observed, matcher, message)
File "C:\Users\rladb\contribution-academy\OCA-OpenStack\python-openstackclient\.tox\py3\Lib\site-packages\testtools\testcase.py", line 509, in assertThat
raise mismatch_error
testtools.matchers._impl.MismatchError: !=:
reference = ('ID', 'Name', 'Status', 'Networks', 'Image', 'Flavor', 'Project ID')
actual = ('ID',
'Name',
'Status',
'Networks',
'Image',
'User name',
'Project name',
'Flavor',
'Project ID')
앞서 올려놓은 26개의 로그를 살펴보면 이 두 가지 type의 에러밖에 없다. 따라서 정리해보자면, server.py에서 바꾼 col과 row가 test에서 정상적으로 통과되도록 test_server.py를 수정해야한다.
3. Breakdown function
가장 맨 처음 Error가 발생했던 test_server_list_long_option을 분석해본다.
# --long 옵션과 함께 조회할 때 반환되는 컬럼과 데이터를 검증하는 유닛테스트
def test_server_list_long_option(self):
# self.servers 목록에 있는 서버 객체들을 돌면서
# 필요한 속성들을 튜플 형태로 추출해 self.data를 만든다.
self.data = tuple(
(
s.id,
s.name,
s.status,
getattr(s, 'task_state'),
server.PowerStateColumn(getattr(s, 'power_state')),
server.AddressesColumn(s.addresses),
# Image will be an empty string if boot-from-volume
self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV,
self.flavor.name,
s.flavor['id'],
getattr(s, 'availability_zone'),
getattr(s, 'pinned_availability_zone', ''),
server.HostColumn(getattr(s, 'hypervisor_hostname')),
format_columns.DictColumn(s.metadata),
format_columns.DictListColumn(None),
)
for s in self.servers
)
# self.cmf에 --long을 전달
arglist = [
'--long',
]
# 파싱 결과가 기대한 값으로 나오는지 T/F
verifylist = [
('all_projects', False),
('long', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# 실제 로직 take_action 호출하고 반환 받음
columns, data = self.cmd.take_action(parsed_args)
self.compute_client.servers.assert_called_with(**self.kwargs)
image_ids = {s.image['id'] for s in self.servers if s.image}
self.image_client.images.assert_called_once_with(
id=f'in:{",".join(image_ids)}',
)
self.compute_client.flavors.assert_called_once_with(is_public=None)
# 출력 columns가 기대한 값 `self.columns_long`과 같은지 확인
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data, tuple(data))
해당 코드에서 에러는 self.columns_long을 확인할 때 발생하므로 해당 인자의 위치를 찾아보았다.
4. self.columns_long 분석하기
# line 4570
class _TestServerList(TestServer):
# Columns to be listed up.
columns = (
'ID',
'Name',
'Status',
'Networks',
'Image',
'Flavor',
)
columns_long = (
'ID',
'Name',
'Status',
'Task State',
'Power State',
'Networks',
'Image Name',
'Image ID',
'Flavor Name',
'Flavor ID',
'Availability Zone',
'Pinned Availability Zone',
'Host',
'Properties',
'Scheduler Hints',
)
columns_all_projects = (
'ID',
'Name',
'Status',
'Networks',
'Image',
'Flavor',
'Project ID',
)
self.columns_long은 test에서 지정한 Expected Value이다. 그러나 server.py에서 넘어오는 Actual Value가 달라 문제가 발생한다.
이제 다시 한번 FAIL log를 확인해보자
reference = self.columns_long이고 actual = columns이다.
actual의 값에
Image ID 뒤에 User name과 Project name을 확인할 수 있다.
6. Fix expected value
class _TestServerList(TestServer):
# Columns to be listed up.
columns = (
'ID',
'Name',
'Status',
'Networks',
'Image',
'User name',
'Project name',
'Flavor',
)
columns_long = (
'ID',
'Name',
'Status',
'Task State',
'Power State',
'Networks',
'Image Name',
'Image ID',
'User name',
'Project name',
'Flavor Name',
'Flavor ID',
'Availability Zone',
'Pinned Availability Zone',
'Host',
'Properties',
'Scheduler Hints',
)
columns_all_projects = (
'ID',
'Name',
'Status',
'Networks',
'Image',
'User name',
'Project name',
'Flavor',
'Project ID',
)
7. Trace back from the fail log
위를 해결하니 아래와 같은 문제가 새로 발생했다.
File "C:\Users\rladb\contribution-academy\OCA-OpenStack\python-openstackclient\openstackclient\tests\unit\compute\v2\test_server.py", line 5008, in test_server_list_with_flavor
self.assertEqual(self.data, tuple(data))
self.data에 정의된 expected value가 actual value와 다른 문제가 발생했다.
따라서 모든 self.data를 변경해준다. 처음엔 setUp 만 변경해주면 일괄 적용되는 줄 알았는데, 함수 내에서 self.data를 또 고치는 경우가 있어 모두 수정해줘야 한다.
self.data = tuple(
(
s.id,
s.name,
s.status,
server.AddressesColumn(s.addresses),
# Image will be an empty string if boot-from-volume
self.image.name if s.image else server.IMAGE_STRING_FOR_BFV,
# [YS] Create new self.data
s.user_name,
s.project_name,
self.flavor.name,
)
for s in self.servers
)
Window라서 발생한 호환 문제
는 천천히 추가하겠습니다!

