Как правильно юниттест в Python - PullRequest
0 голосов
/ 07 февраля 2020

У меня есть метод, который делает следующее. Вопрос в том, как мне провести модульное тестирование этого метода. Я довольно новичок в этом модуле Python модульного тестирования.

Вопрос и решение следующие:

Given a string containing of ‘0’, ‘1’ and ‘?’ wildcard characters, generate all binary strings that can be formed by replacing each wildcard character by ‘0’ or ‘1’.
Example :
Input str = "1??0?101"
Output: 
        10000101
        10001101
        10100101
        10101101
        11000101
        11001101
        11100101
        11101101

Решение:

def _print(string, index):
    if index == len(string):
        print(''.join(string))
        return

    if string[index] == "?":

        # replace '?' by '0' and recurse
        string[index] = '0'
        _print(string, index + 1)

        # replace '?' by '1' and recurse
        string[index] = '1'
        _print(string, index + 1)

        # NOTE: Need to backtrack as string
        # is passed by reference to the
        # function
        string[index] = '?'
    else:
        _print(string, index + 1)

# Driver code
if __name__ == "__main__":

    string = "1??0?101"
    string = list(string) #don’t forget to convert to string
    _print(string, 0)

Вывод:

        10000101
        10001101
        10100101
        10101101
        11000101
        11001101
        11100101
        11101101

Вопросы:

1. Кроме того, есть ли способ вернуть список в качестве вывода вместо его распечатки?

2. Какие контрольные примеры утверждения подходят в этом сценарии?

3. Каковы наилучшие сквозные тестовые случаи для этого случая?

4. Что может быть лучшим подходом к решению этой проблемы с точки зрения сложности времени и пространства?

Я пробовал это, которое, похоже, не работает:

import unittest
from wildcard import _print
class TestWildCard(unittest.TestCase):

    def test_0_print(self):
        print("Start wildCard _print test: \n")
        result = 111
        self.assertEquals(_print("1?1",0),result,"Results match")

1 Ответ

1 голос
/ 07 февраля 2020

Ответы:

1: конечно, вместо того, чтобы что-то печатать, добавьте результат в список result.append('some value') и не забудьте инициализировать список в начале кода result = [] и вернуть его как только функция завершена return result - и, вероятно, не вызывайте функцию _print, но что-то вроде bit_strings.

ad 1: поскольку ваша функция рекурсивная, вам теперь также нужно захватить верните значение и добавьте его к результату при рекурсивном вызове функции, поэтому result += _print(string, index + 1)

2: обычно вы должны думать о крайних случаях и тестировать их отдельно, или группировать те, которые действительно проверяют один аспект вашего функция. Нет единого способа указать, как должен выглядеть тест - если бы он был, среда тестирования просто сгенерировала бы его для вас.

3: тот же ответ, что и для 2.

Ваш код становится :

def bit_strings(s, index):
    result = []

    if index == len(s):
        result.append(''.join(s))
        return result

    if s[index] == "?":
        # replace '?' by '0' and recurse
        s[index] = '0'
        result += bit_strings(s, index + 1)

        # replace '?' by '1' and recurse
        s[index] = '1'
        result += bit_strings(s, index + 1)

        # NOTE: Need to backtrack as string
        # is passed by reference to the
        # function
        s[index] = '?'
    else:
        result += bit_strings(s, index + 1)

    return result


# Driver code
if __name__ == "__main__":
    x = "1??0?101"
    xl = list(x)  #don’t forget to convert to string
    print(bit_strings(xl, 0))

Есть более эффективные способы сделать это, но я просто изменил ваш код в соответствии с вопросами и ответами.

Я переименовал string в s, поскольку string немного сбивает с толку, напоминая другим о типе или затенении (встроенного) модуля.

Что касается модульного теста:

import unittest
from wildcard import bit_strings


class TestWildCard(unittest.TestCase):
    def test_0_print(self):
        print("Start wildCard _print test: \n")
        # you only had one case here and it's a list now
        result = ['101', '111']
        # user assertEqual, not Equals
        # you were passing in a string, but your code assumed a list, so list() added
        self.assertEqual(bit_strings(list("1?1"), 0), result, "Results match")      

При использовании среды, подобной PyCharm, он помогает вызывать файл test<something>.py (т. Е. Иметь test в имени), так что он помогает вам легче запускать модульные тесты.

Два альтернативных решения в соответствии с запросом в комментарии (одно еще рекурсивный, просто намного более краткий, другой не рекурсивный, но, возможно, немного расточительный со списками результатов - всего два быстрых):

from timeit import timeit


def unblank_bits(bits):
    if not bits:
        yield ''
    else:
        for ch in '01' if bits[0] == '?' else bits[0]:
            for continuation in unblank_bits(bits[1:]):
                yield ch + continuation


print(list(unblank_bits('0??100?1')))


def unblank_bits_non_recursive(bits):
    result = ['']
    for ch in bits:
        if ch == '?':
            result = [s+'0' for s in result] + [s+'1' for s in result]
        else:
            result = [s+ch for s in result]
    return result


print(list(unblank_bits_non_recursive('0??100?1')))

print(timeit(lambda: list(unblank_bits('0??100?1'))))
print(timeit(lambda: list(unblank_bits_non_recursive('0??100?1'))))

Это решение не перемещается между списками и строками, так как нет нужно и не манипулирует вводом v alues. Как вы можете сказать, рекурсивный немного медленнее, но я предпочитаю его для удобства чтения. Вывод:

['00010001', '00010011', '00110001', '00110011', '01010001', '01010011', '01110001', '01110011']
['00010001', '01010001', '00110001', '01110001', '00010011', '01010011', '00110011', '01110011']
13.073874
3.9742709000000005

Обратите внимание, что ваше собственное решение работало примерно за 8 секунд с использованием той же настройки, поэтому предложенная мною «улучшенная версия» проще, но не быстрее, поэтому вы можете предпочесть последнее решение.

...