Change pytest rootdir - import

I am stuck with this incredibly silly error. I am trying to run pytest on a Raspberry Pi using bluepy.
pi#pi:~/bluepy/bluepy $ pytest test_asdf.py
============================= test session starts ==============================
platform linux2 -- Python 2.7.9, pytest-3.0.7, py-1.4.33, pluggy-0.4.0
rootdir: /home/pi/bluepy, inifile:
collected 0 items / 1 errors
==================================== ERRORS ====================================
______________ ERROR collecting bluepy/test_bluetoothutility.py _______________
ImportError while importing test module '/home/pi/bluepy/bluepy/test_asdf.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
test_asdf:4: in <module>
from asdf import AsDf
asdf.py:2: in <module>
from bluepy.btle import *
E ImportError: No module named btle
!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!
=========================== 1 error in 0.65 seconds ============================
I realised that my problem could be that rootdir is showing incorrect path. It should be
/home/pi/bluepy/bluepy
I've been reading pytest docs but I just do not get it how to change the rootdir.

Your problem is nothing to do with Pytest's rootdir.
The rootdir in Pytest has no connection to how test package names are constructed and rootdir is not added to sys.path, as you can see from the problem you were experiencing. (Beware: the directory that is considered rootdir may be added to the path for other reasons, such as it also being the current working directory when you run python -m pytest.)
The problem here, as others have described, is that the top-level bluepy/ is not in sys.path. The easiest way to handle this if you just want to get something running interactively for yourself is as per Cecil Curry's answer: cd to the top-level bluepy and run Pytest as python -m pytest bluepy/test_asdf.py (or just python -m pytest if you want it to discover all test_* files in or under the current directory and run them). But I think you will need to use python -m pytest, not just pytest, in order to make sure that the current working directory is in the path.
If you're looking to set up a test framework that others can easily run without mysterious failures like this, you'll want to set up a test script that sets the current working directory or PYTHONPATH or whatever appropriately. Or use tox. Or just make this a Python package using standard tools that can run the tests for you. (All that goes way beyond the scope of this question.)
By the way, I concur with Cecil's opinion of Mackie Messer's answer; messing around with conftest.py like that is overly difficult and fragile; there are better solutions for almost any circumstance.
Appendix: Use of rootdir
There are only two things, as far as I'm aware, for which rootdir is used:
The .pytest_cache/ directory is stored in the rootdir unless otherwise specified (with the cache_dir configuration option).
If rootdir contains a conftest.py, it will always be loaded, even if no test files are loaded from in or under the rootdir.
The documentation claims that the rootdir also used to generate nodeids, but adding a conftest.py containing
def pytest_runtest_logstart(nodeid, location):
print("logstart nodeid={} location={}".format(nodeid, location))
and running pytest --rootdir=/somewhere/way/outside/the/tree shows that to be incorrect (though node locations are relative to the rootdir).

My first guess would be that you don't have that directory in the python path. You can add it to the python path dynamically. One simple way to do this is in a test configuration file conftest.py, which I believe is always executed before test discovery and test running.
For example, you might have a project setup like:
root
+-- tests
| +-- conftest.py
| +-- tests_asdf.py
+-- bluepy (or main project dir)
| +-- miscellaneous modules
In which case, you could add the root dir to your python path in the conftest.py file like so:
#
# conftest.py
import sys
from os.path import dirname as d
from os.path import abspath, join
root_dir = d(d(abspath(__file__)))
sys.path.append(root_dir)
Let me know if that's helpful.

Actually, py.test is correctly discovering the rootdir for your project to be /home/pi/bluepy. That's good.
Tragically, you are erroneously attempting to run py.test within your project's package subdirectory (i.e., /home/pi/bluepy/bluepy) rather than within your project's rootdir (i.e., /home/pi/bluepy). That's bad.
Let's break this down a little. From within the:
/home/pi/bluepy directory, there is a bluepy.btle submodule. (Good.)
/home/pi/bluepy/bluepy subdirectory, there is no bluepy.btle submodule. (Bad.) Unless you awkwardly attempt to manually inject the parent directory of this subdirectory (i.e., /home/pi/bluepy) onto sys.path as Makie Messer perhaps inadvisably advises, Python has no means of inferring that the package bluepy actually refers to the current directory coincidentally also named bluepy. To avoid ambiguity issues of this sort, Python is typically only run outside rather than inside of a project's package subdirectory.
Since you ran py.test from the latter rather than the former directory, Python is unable to find the bluepy.btle submodule on the current sys.path. For this and similar reasons, py.test should typically only ever be run from your project's top-level rootdir (i.e., /home/pi/bluepy):
pi#pi:~/ $ cd ~/bluepy
pi#pi:~/bluepy $ py.test bluepy/test_asdf.py
Lastly, note that it's typically preferable to defer test discovery to py.test. Rather than explicitly listing all test script filenames on the command line, consider instead letting py.test implicitly find and run all tests containing some substring via the -k option. For example, to run all tests whose function names are prefixed by test_asdf (regardless of the test script they reside in):
pi#pi:~/ $ cd ~/bluepy
pi#pi:~/bluepy $ py.test -k test_asdf .
The suffixing . is optional, but often useful. It instructs py.test to set its rootdir property to the current directory (i.e., /home/pi/bluepy). py.test is usually capable of finding your project's rootdir and setting this property on its own, but it can't hurt to specify it manually. (Especially as you're having... issues.)
For further details on rootdir discovery, see Initialization: determining rootdir and inifile in the official py.test documentation.

Related

Is there a way to configure pytest_plugins from a pytest.ini file?

I may have missed this detail but I'm trying to see if I can control the set of plugins made available through the ini configuration itself.
I did not find that item enumerated in any of the configurable command-line options nor in any of the documentation around the pytest_plugins global.
The goal is to reuse a given test module with different fixture implementations.
#hoefling is absolutely right, there is actually a pytest command line argument that can specify plugins to be used, which along with the addopts ini configuration can be used to select a set of plugin files, one per -p command.
As an example the following ini file selects three separate plugins, the plugins specified later in the list take precedence over those that came earlier.
projX.ini
addopts =
-p projX.plugins.plugin_1
-p projX.plugins.plugin_2
-p projY.plugins.plugin_1
We can then invoke this combination on a test module with a command like
python -m pytest projX -c projX.ini
A full experiment is detailed here in this repository
https://github.com/jxramos/pytest_behavior/tree/main/ini_plugin_selection

Powershell Core + Pester - Separating tests from src

Question:
What would be the best way to import functions to tests that don't reside in the same directory?
Example
๐Ÿ“ src
๐Ÿ“„ Get-Emoji.ps1
๐Ÿ“ test
๐Ÿ“„ Get-Emoji.Tests.ps1
Inb4
Pester documentation[1] suggests test files are placed in the same directory as the code that they test. No examples of alternatives provided.
Pester documentation[2] suggests dot-sourcing to import files. Only with examples from within same directory
Whether breaking out tests from the src is good practice, is to be discussed elsewhere
Using Powershell Core for cross platform support on different os filesystems (forward- vs backward slash)
[1] File placement and naming convention
Pester considers all files named .Tests.ps1 to be test files. This is the default naming convention that is used by almost all projects.
Test files are placed in the same directory as the code that they test. Each file is called as the function it tests. This means that for a function Get-Emoji we would have Get-Emoji.Tests.ps1 and Get-Emoji.ps1 in the same directory. What would be the best way to referencing tests to functions in Pester.
[2] Importing the tested functions
Pester tests are placed in .Tests.ps1 file, for example Get-Emoji.Tests.ps1. The code is placed in Get-Emoji.ps1.
To make the tested code available to the test we need to import the code file. This is done by dot-sourcing the file into the current scope like this:
Example 1
# at the top of Get-Emoji.Tests.ps1
BeforeAll {
. $PSScriptRoot/Get-Emoji.ps1
}
Example 2
# at the top of Get-Emoji.Tests.ps1
BeforeAll {
. $PSCommandPath.Replace('.Tests.ps1','.ps1')
}
I tend to keep my tests together in a single folder that is one or two parent levels away from where the script is (which is usually under a named module directory and then within a folder named either Private or Public). I just dot source my script or module and use .. to reference the parent path with $PSScriptRoot (the current scripts path) as a point of reference. For example:
Script in \SomeModule\Public\get-something.ps1
Tests in \Tests\get-something.tests.ps1
BeforeAll {
. $PSScriptRoot\..\SomeModule\Public\get-something.ps1
}
Use forward slashes if cross platform compatibility is a concern, Windows doesn't mind if path separators are forward or backslashes. You could also run this path through Resolve-Path first if you wanted to be certain a valid full path is used, but I don't generally find that necessary.

Import modules and functions from a file in a specific directory in Julia 1.0

Let's say I had a file File.jl that had a module MyModule containing the functions foo and bar in it. In the same directory as the module-file, I had a script Script.jl, and I wanted to use the functions in MyModule in the script.
How would one go about doing this?
In order to find Modules that are not in the standard LOAD_PATH and be able to import them, you need to update your LOAD_PATH variable for the current folder explicitly
push!( LOAD_PATH, "./" )
then you will be able to import a module appropriately.
Note that, if the file is called File.jl and defines the module MyModule, what you should be importing is import MyModule, not import File. It is generally recommended you use the same name for the file as for the defined module in this kind of scenario, to avoid confusion.
Also note, As #crstnbr noted above, you can also simply 'dump' the file's contents into the current session by simply 'including' it; note however that this simply creates the module on the spot, so any precompilation directives etc will not be honoured.
Somewhat related questions / answers (disclaimer: by me) you might find useful:
https://stackoverflow.com/a/50627721/4183191
https://stackoverflow.com/a/49405645/4183191
You include the file with the module definition and call the functions in your script file:
include(joinpath(#__DIR__,"File.jl"))
MyModule.foo()
MyModule.bar()
# or just foor() and bar() if MyModule exports those functions
The #__DIR__ expands to the directory of the script file, see
help?> #__DIR__
#__DIR__ -> AbstractString
Expand to a string with the absolute path to the directory of the file containing the macrocall. Return the current working directory if run from a REPL or if evaluated by julia -e <expr>.

Generate Swift Interface from Objective-C Header from Command Line

In Xcode, for any Objective-C header, we can view the Generated Interface, which shows how it is seen by Swift in interop.
I'd like to generate that from the command line. Any idea how to do it?
Bonus task: The header should be precompiled first, so all #imports should be replaced already.
Invoke interpreter command :type lookup on the module you are trying to inspect.
Suppose you have a header file named header.h. Put it into a separate directory, so that the interpreter would recognise it as a module. Also create a modulemap in the same directory. Let's call this directory Mod:
./
./Mod/
/header.h
/module.modulemap
Fill in the modulemap with the following:
module Mod {
header "./header.h"
export *
}
Once it's done, issue a command like this:
echo "import Mod\n:type lookup Mod" | swift -I./Mod | tail -n+2 >| generated-interface.swift
Alternatively, you might want use a command like this with equal effect:
echo "import Mod\n:print_module Mod" | swift -deprecated-integrated-repl -I./Mod >| generated-interface.swift
It's broken down as follows:
first we echo the script to be executed: import module and type-lookup it;
then we launch the interpreter and feed the script into it; the -I argument helps it find our module, which is crucial;
then we cut off the โ€œWelcome to Swiftโ€ part with Tail
and write the result into generated-interface.swift.
While running the above commands, make sure your working directory is set to one level higher than the Mod directory.
Note that the output might not be exactly the same as from Xcode, but it's very similar.
Just for the record, if you want to produce the interface from a Swift file, then it's just this:
swiftc -print-ast file.swift

Devel::Cover with options for test coverage

In a project I am working on the directory layout that does not have a lib directory so we have
/X.pm
/X/Y.pm
...
/t/test.t
when I run
$ PERL5OPT=-MDevel::Cover make test
$ cover
I get report only for the files in t/
how can I tell Devel::Cover to report about all the files in the current directory except those in t?
I thought I can do it by this:
cover -t +inc . -inc t
but I get:
Unknown option: inc
Invalid command line options at /home/gabor/perl5/lib/perl5/x86_64-linux-thread-multi/Devel/Cover/Report/Html_minimal.pm line 677.
from the documentation it is unclear to me how can I supply these options.
cover doesn't actually generate coverage statistics, only reports on it IIRC.
Also, the +inc seems to need to be a part of PERL5OPT (comma separated to have -M pass them to import(), e.g. -MDevel::Cover=+inc,"/sometething")
I could be wrong - I only ever use Devel::Cover when actually running .t files, so never tried to do "all modules in directory" approach.