Найти - Пользователи
Полная версия: Slacker Tracker - "классный журнал" для преподавателя
Начало » Python для новичков » Slacker Tracker - "классный журнал" для преподавателя
1 2 3
py.user.next
frpaul
Кстати, тесты мы пишем как?
1. Сначала пишешь набросок функции, и вот она работает.
На этом этапе ты придумываешь функцию: что она принимает, что она возвращает и какое у неё имя.

2. Пишешь для этой функции юнит-тест один.
Этим ты заготавливаешь среду для юнит-тестов к этой функции, то есть придумываешь, где они будут лежать (в каком файле и директории), как они будут запускаться, как они будут храниться внутри своего файла. Бывает, там нужно make подрубать, чтобы с верхушки проекта запускать тесты где-нибудь в глубине.

3. Потом функцию комментируешь внутри и запускаешь юнит-тест, и он показывает, что функция не работает.
Так ты удостоверяешься, что юнит-тест действительно проверяет функцию, а не просто работает сам по себе. Тут же смотришь, как он сообщает, что функция не работает, чтобы было видно причину отчётливо прямо из сообщения юнит-теста и не надо было лезть внутрь функции, чтобы понять, что в ней не сработало.

4. Потом функцию раскомментируешь внутри и запускаешь юнит-тест, он показывает, что функция работает.
Так ты всё отладил, всё работает, всё правильно пишется как в случае успеха, так и в случае ошибки и тебе не надо никуда лазить, чтобы это понять. Всё удобно запускается без поисков юнит-тестов в дебрях проекта.

Теперь наступает TDD, это когда сначала пишутся юнит-тесты все как бы для невидимой функции, а потом пишется функция, которая должна пройти эти юнит-тесты.

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

То есть основное правило: ты должен проверить, что юнит-тест как падает при сбое в функции, так и не падает при успешном выполнении функции.

А дальше уже выбираешь платформу для юнит-тестов. Есть doctest, unittest и py.test.
doctest - это очень быстрые (по разработке) тесты, и они включены в стандартную библиотеку питона.
unittest - это основные тесты (медленные по разработке, но развитые по возможностям), и они включены в стандартную библиотеку питона.
py.test - это сторонние тесты (очень модные и продуктивные), но они не включены в стандартную библиотеку питона.

Наверное, потом py.test включат в стандартную библиотеку, так как сделаны хорошо и все предпочитают их, а не unittest. Но unittest ты должен знать, потому что они основные в питоне. Ты просто на них напорешься где-нибудь, а выучить за пять минут их нельзя, это громадная платформа. Я учил их, наверное, целый месяц.
py.user.next
Вот, например, я взял метод vis из твоего str.py
  
    def vis(self, model, itr):
        """Callback for the model-filter in Viewer.
        Show/hide rows that contain info for students 
        not present in this (or any) class
        """
        av_l = model.get_value(itr, 5)
        act = model.get_value(itr, 2)
#        print 'act', act
        if type(av_l) == types.NoneType:
#            print 'av_l', av_l
            return False
 
        if av_l != '0':
            av_ls = av_l.split('/')
            av = float(av_ls[0])
        else: 
            av = 0
#        av =  self.w_model.get(itr, 0,1,2,3,4,5,6,7,8,9)
 
        if self.c_group == 0: # weak
#            print 'group - weak'
            if not act:
                if av:
                    if av < 3.5:
                        return True
                    else:
                        return False
                else:
                    return True
            else:
                return False
        elif self.c_group == 1: # strong
#            print 'group - strong'
            if not act:
                if av:
                    if av >= 3.5:
                        return True
                    else:
                        return False
                else:
                    return False
            else:
                return False
        elif self.c_group == 2: # active
#            print 'group - active'
            if not act:
                return True
            else:
                return False
        elif self.c_group == 3: # all
            return True
И тут вопрос: откуда ты знаешь, что он всегда правильно возвращает свои значения?
Ниоткуда не знаешь, просто немножко проверил, вроде всё правильно и всё. Нет времени проверять каждую шпуньку, поэтому ты их теоретически считаешь правильными. Основные вещи проверяешь, а на мелочи забиваешь типа “да они, скорее всего, правильно работают”. И вот так пропускаются логические ошибки.
Вот для этого и нужны юнит-тесты: они его покрывают и он уже не может вернуть что-то неправильно, потому что какой-то юнит-тест это увидит и упадёт.
Rodegast
Ну что же вы к человеку пристали? У него вполне вменяемый проект, хотя он и не без проблем.
Например:
1) Много мест в коде которые нужно переделать. Например вместо:
         command = "CREATE TABLE students (s_num INTEGER PRIMARY KEY, s_name TEXT, email TEXT, phone TEXT, photo TEXT, active TEXT, comment TEXT)"
        command2 = "CREATE TABLE attendance (a_num TEXT, s_num INTEGER, date TEXT, absence TEXT, comment TEXT)"
        command3 = "CREATE TABLE grades (g_num TEXT, s_num INTEGER, e_name TEXT, e_num INT, date TEXT, mark REAL, comment TEXT)"
        command4 = "CREATE TABLE lectures (e_id INTEGER PRIMARY KEY, date TEXT, topic TEXT, comment TEXT)"
        command5 = "CREATE TABLE seminars (e_id INTEGER PRIMARY KEY, date TEXT, topic TEXT, comment TEXT)"
        command6 = "CREATE TABLE tests (e_id INTEGER PRIMARY KEY, date TEXT, topic TEXT, comment TEXT)"
        command7 = "CREATE TABLE essays (e_id INTEGER PRIMARY KEY, date TEXT, enddate TEXT, topic TEXT, comment TEXT)"
        command8 = 'CREATE TABLE notes (c_num TEXT, s_num INTEGER, date TEXT, comment TEXT)' # TODO: make "fulfilled" or "acted_on" column
        command9 = 'CREATE TABLE assignments (a_num TEXT, s_num INTEGER, e_id INTEGER, delivered TEXT, date TEXT, mark REAL, comment TEXT)'
        for com in [command, command2, command3, command4, command5, command6, command7, command8, command9]:
cur.execute(com) # TODO: executescript()
Я бы писало так:
 cursor.executescript("""
        /*
        *Создаём таблицы:
        *.......
        /*
        CREATE TABLE students (s_num INTEGER PRIMARY KEY, s_name TEXT, email TEXT, phone TEXT, photo TEXT, active TEXT, comment TEXT);
        CREATE TABLE grades (g_num TEXT, s_num INTEGER, e_name TEXT, e_num INT, date TEXT, mark REAL, comment TEXT);
        CREATE TABLE lectures (e_id INTEGER PRIMARY KEY, date TEXT, topic TEXT, comment TEXT);
        CREATE TABLE seminars (e_id INTEGER PRIMARY KEY, date TEXT, topic TEXT, comment TEXT);
        CREATE TABLE tests (e_id INTEGER PRIMARY KEY, date TEXT, topic TEXT, comment TEXT);
        CREATE TABLE essays (e_id INTEGER PRIMARY KEY, date TEXT, enddate TEXT, topic TEXT, comment TEXT);
        CREATE TABLE notes (c_num TEXT, s_num INTEGER, date TEXT, comment TEXT);
        CREATE TABLE assignments (a_num TEXT, s_num INTEGER, e_id INTEGER, delivered TEXT, date TEXT, mark REAL, comment TEXT);
""")
2) Сам GUI на иностранном языке, хотя и рассчитан на отечественного потребителя.
3) В качестве СУБД лучше использовать не sqlite3, а что-то серверное + ORM. Тогда будет проще масштабироваться.
4) Я не вникал, но по скриншоту видно что над юзабилити нужно серьёзно работать.

Теперь о том что насоветовали критики (да я знаю что бомбить будет, но это моё ИХМО):
1) PEP8 - Пещерный стандарт. Я считаю хорошей практикой его не придерживаться.
2) pylint - Ужасный инструмент выдающий слишком много “мусора”. Если нужен статический анализ, то лучше использовать pyflakes.
3) Тесты/TDD - Они актуальны в больших проектах, над которыми работают несколько человек. Пока про это думать рано.
4) ИДЕ - Если речь не идёт о командной разработки, то это дело каждого в чём работать/мучиться. А говнокод всё-таки не из-за ИДЕ не возникает.
frpaul
py.user.next
Вот, например, я взял метод vis из твоего str.py

Отлично! А то я прочел Ваше первое объяснение по юнит-тестам и мало что понял. С примером будет проще разобраться. Я было начал писать wizard для того, чтобы проще было начать работу с программой (“фичреквест” от FishHook'а), но, кажется, важнее с юнит-тестами повозиться. Если ткнете еще в сторону какой-нибудь статьи, где подробнее описывается технология, буду очень признателен.
frpaul
Rodegast
Ну что же вы к человеку пристали? У него вполне вменяемый проект, хотя он и не без проблем.Например:1) Много мест в коде которые нужно переделать. Например вместо:

Спасибо за поддержку. И еще - я именно это собирался сделать - скриптом все CREATE выполнить (недавно про это прочитал).
Я уже перенес функцию создания таблицы в основную программу, надо будет это тоже исправить.

Меня вообще впечатляет, что все мои косяки и недоделки здесь сразу разглядели (включая кривую функцию vis). Спасибо всем, кто не поленился копаться в моей поделке! Буду исправлять по мере сил и наличия времени.
frpaul
py.user.next

По поводу pylinter. Установил версию для python2, но по умолчанию в Arch Linux 3й питон. Не соображу, как запускать 2й (3й сразу ругается про скобки при print - и все). Попробовал сделать обертку и импортировать epylint но ничего не выходит:

 #!/usr/bin/env python2
# -*- coding: utf-8 -*-
import sys
from pylint import epylint as lint
argv = sys.argv
modname = argv[1]
#(pylint_stdout, pylint_stderr) = lint.py_run(modname, return_std=True)
#lint.py_run(modname)
lint.py_run()

Результат:
 Fatal Python error: Py_Initialize: Unable to get the locale encoding
  File "/usr/lib/python2.7/encodings/__init__.py", line 123
    raise CodecRegistryError,\
                            ^
SyntaxError: invalid syntax
Current thread 0x00007f13dc640400 (most recent call first):

Кто-нибудь сталкивался?
py.user.next
frpaul
По поводу pylinter. Установил версию для python2, но по умолчанию в Arch Linux 3й питон.
Для каждой версии питона свой линтер. Для второго - pylint, для третьего - python3-pylint. И так со всеми пакетами, потому что python3 не совместим с python2.

frpaul
С примером будет проще разобраться.
Вот книжка по питону
http://getpython3.com/diveintopython3/

Это описание TDD и пример разработки функции через тестирование
http://getpython3.com/diveintopython3/unit-testing.html
http://getpython3.com/diveintopython3/refactoring.html

Для начального уровня этого хватит, а вообще тесты пишутся по теории. Там используется представление функции в виде графа с путями и тесты должны покрывать весь граф (проходит по всем путям графа). Там существует проблема повторения тестов, и чтобы они не повторялись, сначала нужно определить, какие тесты нужно писать, а какие не нужно и как их там разделить. А без всего этого ты напишешь сто тестов на одну функцию, а потом не будешь знать даже, это вообще все тесты или не все, а если не все, то какие ещё нужны.
frpaul
Да, спасибо. Dive into python есть, кое-что читал, но учился по Линцу, или как там его. А в Дайве есть что-то про тесты?

За примеры спасибо.

Насчёт второго и третьего питона я понял. Просто не пойму, почему линтер для второго питона в обертке не работает. Может я вывод не туда направляю?
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB