Debo enfatizar en PyCharm Community Edition que no tiene integración con Django ( v 2016.3.2 en el momento de la pregunta).
Busqué mi problema con Google y (sorprendentemente) no obtuve ninguna respuesta (por supuesto, no excluyo la posibilidad de que pueda haber algunas, pero simplemente las extrañé).
La pregunta es simple: en PyCharm , uno puede ejecutar (depurar) una prueba de unidad ( TestCase o uno de sus métodos) con un simple clic con el botón derecho del ratón (en el menú contextual) como en la imagen a continuación:
Desafortunadamente, eso produce una excepción:
Traceback (most recent call last): File "C:\Install\PyCharm Community Edition\2016.3.2\helpers\pycharm\utrunner.py", line 254, in main() File "C:\Install\PyCharm Community Edition\2016.3.2\helpers\pycharm\utrunner.py", line 232, in main module = loadSource(a[0]) File "C:\Install\PyCharm Community Edition\2016.3.2\helpers\pycharm\utrunner.py", line 65, in loadSource module = imp.load_source(moduleName, fileName) File "E:\Work\Dev\Django\Tutorials\proj0\src\polls\tests.py", line 7, in from polls.models import Question File "E:\Work\Dev\Django\Tutorials\proj0\src\polls\models.py", line 9, in class Question(models.Model): File "E:\Work\Dev\Django\Tutorials\proj0\src\polls\models.py", line 10, in Question question_text = models.CharField(max_length=200) File "E:\Work\Dev\VEnvs\py2713x64-django\lib\site-packages\django\db\models\fields\__init__.py", line 1043, in __init__ super(CharField, self).__init__(*args, **kwargs) File "E:\Work\Dev\VEnvs\py2713x64-django\lib\site-packages\django\db\models\fields\__init__.py", line 166, in __init__ self.db_tablespace = db_tablespace or settings.DEFAULT_INDEX_TABLESPACE File "E:\Work\Dev\VEnvs\py2713x64-django\lib\site-packages\django\conf\__init__.py", line 53, in __getattr__ self._setup(name) File "E:\Work\Dev\VEnvs\py2713x64-django\lib\site-packages\django\conf\__init__.py", line 39, in _setup % (desc, ENVIRONMENT_VARIABLE)) django.core.exceptions.ImproperlyConfigured: Requested setting DEFAULT_INDEX_TABLESPACE, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.
Nota : Solo agregué la pregunta para proporcionar una respuesta que podría ser útil para alguien.
Teniendo en cuenta lo anterior, algunas (o todas) partes de la solución pueden parecer complicadas / estúpidas para algunos usuarios avanzados, así que, por favor, tengan paciencia conmigo. Incorporaré cualquier comentario posible que agregue valor a la solución.
De vuelta a la pregunta: hice mis pruebas / investigación en un proyecto que consiste en el Tutorial de Django ( [DjangoProject]: Escribiendo tu primera aplicación de Django ) + algunas partes del Tutorial de Marco de Descanso de Django ( [DRF]: Inicio rápido ). Como ejemplo, voy a intentar ejecutar polls / tests.py : QuestionViewTests.test_index_view_with_no_questions()
Como nota, la configuración de DJANGO_SETTINGS_MODULE como la excepción indica, activa otra , y así sucesivamente …
Aunque esta no es una respuesta a la pregunta (solo está relacionada de forma remota), la estoy publicando de todas formas (estoy seguro de que mucha gente ya lo hizo):
test QuestionViewTests.test_index_view_with_no_questions
) Por supuesto, tener que hacer esto para cada caso de prueba (y sus métodos) no es el camino a seguir (es realmente molesto), por lo que este enfoque no es escalable.
Solo se debe tener en cuenta que no veo esto como una verdadera solución, es más como una solución alternativa (lame) ( gainarie ), y también es intrusivo.
Comencemos observando qué sucede cuando hacemos clic en una prueba ( usaré este término en general, podría significar Caso de prueba o método o archivo de prueba completo, a menos que se especifique lo contrario). Para mí, se está ejecutando el siguiente comando:
"E:\Work\Dev\VEnvs\py2713x64-django\Scripts\python.exe" "C:\Install\PyCharm Community Edition\2016.3.2\helpers\pycharm\utrunner.py" E:\Work\Dev\Django\Tutorials\proj0\src\polls\tests.py::QuestionViewTests::test_index_view_with_no_questions true
Como puede ver, está lanzando ” C: \ Install \ PyCharm Community Edition \ 2016.3.2 \ helpers \ pycharm \ utrunner.py ” (me referiré a él como utrunner ) con un montón de argumentos (el primero Nos importa, ya que es la especificación de prueba). utrunner utiliza un marco de ejecución de prueba que no se preocupa por Django (en realidad, hay un código de manejo de Django , pero eso no nos ayuda).
Unas pocas palabras en las configuraciones Run / Debug de PyCharm :
Con lo anterior en mente, procedamos:
Lo primero que debe hacer es: desde el cuadro de diálogo Ejecutar / Depurar configuraciones (menú: Ejecutar -> Editar configuraciones … ), edite la configuración de Predeterminados / Pruebas de Python / Unittests :
Lo segundo y lo más complicado (también involucrando intrusión): parchear a utrunner .
utrunner.patch
--- utrunner.py.orig 2016-12-28 19:06:22.000000000 +0200 +++ utrunner.py 2017-03-23 15:20:13.643084400 +0200 @@ -113,7 +113,74 @@ except: pass -if __name__ == "__main__": + +def fileToMod(filePath, basePath): + if os.path.exists(filePath) and filePath.startswith(basePath): + modList = filePath[len(basePath):].split(os.path.sep) + mods = ".".join([os.path.splitext(item)[0] for item in modList if item]) + return mods + else: + return None + + +def utrunnerArgToDjangoTest(arg, basePath): + if arg.strip() and not arg.startswith("--"): + testData = arg.split("::") + mods = fileToMod(testData[0], basePath) + if mods: + testData[0] = mods + return ".".join(testData) + else: + return None + else: + return None + + +def flushBuffers(): + sys.stdout.write(os.linesep) + sys.stdout.flush() + sys.stderr.write(os.linesep) + sys.stderr.flush() + + +def runModAsMain(argv, codeGlobals): + with open(argv[0]) as f: + codeStr = f.read() + sys.argv = argv + code = compile(codeStr, os.path.basename(argv[0]), "exec") + codeGlobals.update({ + "__name__": "__main__", + "__file__": argv[0] + }) + exec(code, codeGlobals) + + +def djangoMain(): + djangoTests = list() + basePath = os.getcwd() + for arg in sys.argv[1: -1]: + djangoTest = utrunnerArgToDjangoTest(arg, basePath) + if djangoTest: + djangoTests.append(djangoTest) + if not djangoTests: + debug("/ [DJANGO MODE] Invalid arguments: " + sys.argv[1: -1]) + startupTestArgs = [item for item in os.getenv("DJANGO_STARTUP_TEST_ARGS", "").split(" ") if item] + startupFullName = os.path.join(basePath, os.getenv("DJANGO_STARTUP_NAME", "manage.py")) + if not os.path.isfile(startupFullName): + debug("/ [DJANGO MODE] Invalid startup file: " + startupFullName) + return + djangoStartupArgs = [startupFullName, "test"] + djangoStartupArgs.extend(startupTestArgs) + djangoStartupArgs.extend(djangoTests) + additionalGlobalsStr = os.getenv("DJANGO_STARTUP_ADDITIONAL_GLOBALS", "{}") + import ast + additionalGlobals = ast.literal_eval(additionalGlobalsStr) + flushBuffers() + runModAsMain(djangoStartupArgs, additionalGlobals) + flushBuffers() + + +def main(): arg = sys.argv[-1] if arg == "true": import unittest @@ -186,3 +253,10 @@ debug("/ Loaded " + str(all.countTestCases()) + " tests") TeamcityTestRunner().run(all, **options) + + +if __name__ == "__main__": + if os.getenv("DJANGO_TEST_MODE_GAINARIE"): + djangoMain() + else: + main()
Lo anterior es un diff ( [man7]: DIFF (1) ) (o un parche – los nombres se pueden usar de manera conjunta – yo prefiero (y usaré) parche ): muestra las diferencias entre utrunner.py.orig (el original archivo – que guardé antes de comenzar a modificar, no es necesario que lo haga) y utrunner.py (la versión actual que contiene los cambios). El comando que utilicé es diff --binary -uN utrunner.py.orig utrunner.py
(obviamente, en la carpeta de utrunner ). Como comentario personal, el parche es la forma preferida de alterar el código fuente de terceros (para mantener los cambios bajo control y por separado).
Lo que hace el código en el parche (probablemente sea más difícil de seguir que el simple código de Python ):
if __name__ == "__main__":
o el comportamiento actual) se ha movido a una función llamada main (para mantenerlo separado y evitar alterarlo por error) fileToMod("E:\Work\Dev\Django\Tutorials\proj0\src\polls\tests.py", "E:\Work\Dev\Django\Tutorials\proj0\src")
, devolverá polls.tests
E:\Work\Dev\Django\Tutorials\proj0\src\polls\tests.py::QuestionViewTests::test_index_view_with_no_questions
) en formato polls.tests.QuestionViewTests.test_index_view_with_no_questions
( polls.tests.QuestionViewTests.test_index_view_with_no_questions
) if __name__ == "__main__":
Esta función “engaña” a Python haciéndole creer que manage.py se ejecutó como su primer argumento Parche utrunner :
patch -i /tmp/utrunner.patch
. [man7]: PATCH (1) es una utilidad que se instala de forma predeterminada (parte del parche dpkg en Ubtu ). Tenga en cuenta que dado que utrunner.py es propiedad de root , para este paso necesitará sudo patch -Ri /tmp/utrunner.patch
, y lo cambiará de nuevo a su contenido original (también creará un archivo utrunner.py.orig con el contenido modificado; en realidad cambiará los archivos .py y .py.orig ). Un par de palabras sobre este enfoque :
El código puede manejar (opcional) env vars (que no sean DJANGO_TEST_MODE_GAINARIE , lo cual es obligatorio):
manage.py test
(ejecute la manage.py test --help
para obtener la lista completa). Aquí, tengo que insistir en -k / –keepdb, que conserva la base de datos de prueba ( prueba _ $ {REGULAR_DB_NAME} de forma predeterminada o establecida en la configuración en el diccionario TEST ) entre ejecuciones. Al ejecutar una única prueba, crear la base de datos (y aplicar todas las migraciones) y destruirla puede llevar mucho tiempo (y también puede ser muy molesto). Este indicador garantiza que la base de datos no se elimine al final y se reutilizará en la próxima ejecución de prueba globals()
, debe colocarse aquí Al modificar una configuración predeterminada , todas las configuraciones creadas previamente que la heredan no se actualizarán , por lo que deben eliminarse manualmente (y serán recreadas automáticamente por los nuevos RClick en sus pruebas )
RHaga clic en la misma prueba (después de eliminar su configuración anterior: d), y voilà :
E:\Work\Dev\VEnvs\py2713x64-django\Scripts\python.exe "C:\Install\PyCharm Community Edition\2016.3.2\helpers\pycharm\utrunner.py" E:\Work\Dev\Django\Tutorials\proj0\src\polls\tests.py::QuestionViewTests::test_index_view_with_no_questions true Testing started at 01:38 ... Using existing test database for alias 'default'... . ---------------------------------------------------------------------- Ran 1 test in 0.390s OK Preserving test database for alias 'default'... Process finished with exit code 0
La depuración también funciona (puntos de interrupción, y así sucesivamente …).
Advertencias (hasta ahora identifiqué 2 de ellas):
input
( raw_input
) no se maneja muy bien; el texto del mensaje: ” Escriba ‘sí’ si desea intentar eliminar la base de datos de prueba ‘test_tut-proj0’, o ‘no’ para cancelar: ” (que aparece si se ejecutó la prueba anterior y su DB no se destruyó en el final) no se muestra y el progtwig se congela (esto no ocurre fuera de utrunner ), sin permitir que el usuario ingrese texto (¿tal vez hay hilos en la mezcla?). La única forma de recuperarse es detener la ejecución de la prueba, eliminar la base de datos y volver a ejecutar la prueba. Una vez más, tengo que promover la manage.py test -k
que solucionará este problema He trabajado / probado en los siguientes entornos :
Notas :
Como dije al principio, ¡cualquier sugerencia es más que bienvenida!
@ EDIT0 :