I am attempting to run tests from a python module using Azure DevOps. I've got a pipeline build set up to build of a yml file and also using classic editor. I'm getting an error that my module name on my imports is not right. When I run this locally, it works just fine.
My Repo structure:
I'm running my test using this command as a batch file:
cd testcases
pytest -v test_msoffice.py
and gives me error:
______________________ ERROR collecting test_msoffice.py ______________________
ImportError while importing test module 'D:\a\1\s\testcases\test_msoffice.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
test_msoffice.py:7: in <module>
from util import utility_method_class
E ModuleNotFoundError: No module named util
Someone please help me.
I ran into the same symptoms over the last two days.
I assume locally your package is in a directory called "util", with (or as per your comment without) an __init__.py
file. This leads python to recognise all modules under that directory as belonging to the utils package. (From Python 3.something python will manage "namespace references" to the local directory structure without __init__.py
)
On the Pipeline agent the code is directly copied into D:\a\1\s\ - therefore python will consider the package to be called "s"
I found the following workaround: include a script in the pipeline to move the source code into a new suitably named directory and then test it there.
My Pipeline YAML looks like this. It is based on the assumption that your ADO project is named the same as your package - so in your case "util" and is set up for ubuntu / bash. If you must test on windows agents then the scripting will need porting, or you can switch to ubuntu agents and copy the whole thing.
# Python package
# Create and test a Python package on multiple Python versions.
# Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and more:
# https://learn.microsoft.com/azure/devops/pipelines/languages/python
trigger:
- azure-pipelines
- master
- pipeline/*
variables:
PythonPackagePath: ORIGINALVALUE
pool:
vmImage: ubuntu-latest
strategy:
matrix:
# Python27:
# python.version: '2.7'
# Python35:
# python.version: '3.5'
# Python36:
# python.version: '3.6'
# Python37:
# python.version: '3.7'
Python310:
python.version: '3.10'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(python.version)'
displayName: 'Use Python $(python.version)'
- script: |
cd $(System.DefaultWorkingDirectory)
export ParentDir=${PWD%/*}
echo Setting PythonPackagePath to $ParentDir/$(System.TeamProject)
echo "##vso[task.setvariable variable=PythonPackagePath;]$ParentDir/$(System.TeamProject)"
displayName: 'Set Package Path'
- script: |
echo PythonPackagePath is $(PythonPackagePath)
echo Moving files from $(System.DefaultWorkingDirectory) to $(PythonPackagePath)
mv $(System.DefaultWorkingDirectory) $(PythonPackagePath)
echo SymLinking old Working Directory back in case needed
ln -s $(PythonPackagePath) $(System.DefaultWorkingDirectory)
ls -l $(System.DefaultWorkingDirectory)/..
displayName: 'Move Working Directory'
- script: |
python -m pip install --upgrade pip
pip install -r requirements.txt
displayName: 'Install dependencies'
# continueOnError: true
- script: |
export PYTHONPATH=$(PythonPackagePath)
echo PYTHONPATH is set to $PYTHONPATH
cd $(PythonPackagePath)
echo Working in:
pwd
pip install pytest pytest-azurepipelines
pip install pytest-cov
pytest --cov=. --cov-report=xml
displayName: 'pytest'
- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: Cobertura
summaryFileLocation: '$(PythonPackagePath)/**/coverage.xml'
A few traps I fell into along the way:
$(VariableName)
are resolved when the step starts, not when the line is reached - so you need to set the variable in a previous step before using it.export ParentDir=..
nor export ParentDir=$(System.DefaultWorkingDirectory)/..
in the first case ParentDir is always the parent of the current working directory, in the second the symlinking will fail due to circular references. The RegEx ${PWD%/*}
gets the value of the current directory and returns the value up to the last slash.ln -s path/to/code/s path/to/code/PackageName
pytest will still consider the package to be called sPYTHONPATH
is not strictly necessary, running pytest from the right directory seems to be the most stable option in thie configurationThis works regardless of whether tests are in a subfolder. My overall structure is:
FooBar
__init__.py
¦
-- Doomsday
__init__.py
Fuel.py
test_Fuel.py
...
¦
-- Utils
__init__.py
Maths.py
...
¦
--test
test_Maths1.py
test_Maths2.py
...
where:
from FooBar.Utils.Maths import prod
and these work correctlyIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With