diff --git a/MANIFEST.in b/MANIFEST.in
index 944ebbada4661ba0768179e055121b922de73dfa..400dae6498e2413550e5e5b3b5252d1dc8bcd939 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -5,6 +5,10 @@ include version
 recursive-include spams_wrap *.h
 recursive-include spams_wrap *.cpp
 
+include spams/version
+
+recursive-include spams/tests *.py
+recursive-include spams/data *.png
 recursive-include tests *.py
 recursive-include data *.png
 recursive-include doc *
diff --git a/README.md b/README.md
index a04c1b805b1ab3754a58d529373f82a5d891d3d6..3f49b0905465c347617cf8ac86e5090e90401ddd 100644
--- a/README.md
+++ b/README.md
@@ -49,9 +49,26 @@ Manipulated objects are imported from numpy and scipy. Matrices should be stored
 
 ### Testing the interface
 
+- From the command line (to be called from the project root directory):
 ```bash
-python tests/test_spams.py -h # to get help
-python tests/test_spams.py    # will run all the tests
+python tests/test_spams.py -h       # print the man page
+python tests/test_spams.py          # run all the tests
+```
+
+- From Python (assuming `spams` package is installed):
+```python
+from spams.tests import test_spams
+
+test_spams('-h')                    # print the man page
+test_spams()                        # run all tests
+test_spams(['sort', 'calcAAt'])     # run specific tests
+test_spams(python_exec='python3')   # specify the python exec
+```
+
+- From the command line (assuming `spams` package is installed):
+```bash
+# c.f. previous point for the different options
+python -c "from spams.tests import test_spams; test_spams()"
 ```
 
 ---
diff --git a/setup.py b/setup.py
index 8344486396d8c6243987687711ec08ee09e10b89..561409a5300176b0af867c4037e925cf7e6b01ee 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ import os
 # import platform
 import sys
 
-from setuptools import setup, Extension, find_packages
+from setuptools import setup, Extension
 from setuptools.command.build_ext import build_ext
 
 from distutils.sysconfig import get_python_inc
@@ -193,18 +193,12 @@ opts = dict(
     python_requires='>=3',
     install_requires=['Cython>=0.29', 'numpy>=1.12',
                       'Pillow>=6.0', 'scipy>=1.0', 'six>=1.12'],
-    packages=find_packages(),
+    packages=['myscipy_rand', 'spams_wrap', 'spams', 'spams.tests'],
     cmdclass={'build_ext': CustomBuildExtCommand},
     ext_modules=get_extension(),
-    data_files=[('tests', ['tests/test_spams.py',
-                           'tests/test_decomp.py',
-                           'tests/test_dictLearn.py',
-                           'tests/test_linalg.py',
-                           'tests/test_prox.py',
-                           'tests/test_utils.py']),
-                ('doc', ['doc/doc_spams.pdf']),
-                ('tests', ['data/boat.png', 'data/lena.png'])],
-    include_package_data=True,
+    package_data={
+        "spams": ["data/*.png", "version"]
+    },
     zip_safe=True
 )
 
diff --git a/spams/data b/spams/data
new file mode 120000
index 0000000000000000000000000000000000000000..4909e06efb479a01e44e67265074c726796f4959
--- /dev/null
+++ b/spams/data
@@ -0,0 +1 @@
+../data
\ No newline at end of file
diff --git a/spams/tests b/spams/tests
new file mode 120000
index 0000000000000000000000000000000000000000..6dd24e02b51335a68a3fcb605ac7754f9435ec66
--- /dev/null
+++ b/spams/tests
@@ -0,0 +1 @@
+../tests
\ No newline at end of file
diff --git a/spams/version b/spams/version
new file mode 120000
index 0000000000000000000000000000000000000000..1748d5b2e856f6c5a64d3bbda9a7cf0754d3f96a
--- /dev/null
+++ b/spams/version
@@ -0,0 +1 @@
+../version
\ No newline at end of file
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..23aa21946bed4f5b6c7ee5fc3251f2f247d968ee
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1 @@
+from .run import test_spams
diff --git a/tests/run.py b/tests/run.py
new file mode 100644
index 0000000000000000000000000000000000000000..1838ac7d4b5202d95ed74b983ea8aeb603221234
--- /dev/null
+++ b/tests/run.py
@@ -0,0 +1,59 @@
+import subprocess
+import os
+
+def test_spams(args=None, python_exec = 'python'):
+    """Run spams-python tests from python
+    
+    To run the tests, from the command line, please check the README.md file
+    
+    Input:
+        args (string|string list): argument to pass to the the main test script,
+            use `args='-h'` for more details. If None (default), all tests are
+            run.
+        python_exec (string): python executable. By default, it is `'python'`.
+            Python 3+ is required. On some system, you may have to set 
+            `python_exec = 'python3'`.
+    """
+    
+    # check spams availability
+    try:
+        import spams
+    except:
+        raise ModuleNotFoundError("No module named 'spams'")
+    
+    # main test file
+    test_file = os.path.join("tests", "test_spams.py")
+    if os.path.isfile(test_file):
+        test_file = os.path.abspath(test_file)
+    else:
+        test_file = os.path.join(
+            os.path.dirname(os.path.abspath(spams.__file__)), test_file
+        )
+        
+    # test dir
+    test_dir = os.path.dirname(test_file)
+    
+    # check python executable
+    try:
+        python_version = subprocess.run([python_exec, "-V"], capture_output=True)
+    except:
+        raise SystemError(f"{python_exec} is not a valid python executable")
+    # check python version
+    if not "Python 3." in python_version.stdout.decode('UTF-8'):
+        raise SystemError("Python 3+ is required, try using python_exec = 'python3'")
+    
+    # check args
+    if args is None:
+        args = []
+    elif not (isinstance(args, str) or \
+        (isinstance(args, list) and \
+        all(isinstance(arg, str) for arg in args))):
+        raise TypeError("'args' input should be a string or a list of strings.")
+    
+    if isinstance(args, str):
+        args = [args]
+    
+    # run
+    subprocess.run([python_exec, 'test_spams.py'] + args, cwd = test_dir)
+    
+    
diff --git a/tests/test_dictLearn.py b/tests/test_dictLearn.py
index 88714a0a7bf624a321e7f109bafb60e1a25e6a48..c6ea44a460f9646ad4da8d02c427e64cd8744374 100644
--- a/tests/test_dictLearn.py
+++ b/tests/test_dictLearn.py
@@ -1,6 +1,7 @@
 from __future__ import absolute_import, division, print_function
 
 import sys
+import os
 import numpy as np
 import scipy
 import scipy.sparse as ssp
@@ -15,6 +16,33 @@ if not ('rand' in ssp.__dict__):
     ssprand = myscipy_rand.rand
 else:
     ssprand = ssp.rand
+    
+    
+def get_img_file_path(img):
+    """Return path to an image file
+    
+    Arguments:
+        img (string): image filename without path among 'boat.png' or 
+        'lena.png'.
+        
+    Output:
+        img_file (string): normalized path to image input filename.
+    
+    """
+    # check input
+    if not img in ["boat.png", "lena.png"]:
+        raise ValueError("bad input, `img` should be 'boat.png' or 'lena.png'")
+    # try local file
+    img_file = os.path.join("data", img)
+    if os.path.isfile(img_file):
+        img_file = os.path.abspath(img_file)
+    else:
+        # file from install
+        img_file = os.path.join(
+            os.path.dirname(os.path.abspath(spams.__file__)), img_file
+        )
+    # output
+    return img_file
 
 
 def _extract_lasso_param(f_param):
@@ -46,8 +74,8 @@ def _objective(X, D, param, imgname=None):
 
 
 def test_trainDL():
-    img_file = 'data/boat.png'
     try:
+        img_file = get_img_file_path("boat.png")
         img = Image.open(img_file)
     except:
         print("Cannot load image %s : skipping test" % img_file)
@@ -140,8 +168,8 @@ def test_trainDL():
 
 
 def test_trainDL_Memory():
-    img_file = 'data/lena.png'
     try:
+        img_file = get_img_file_path("lena.png")
         img = Image.open(img_file)
     except:
         print("Cannot load image %s : skipping test" % img_file)
@@ -202,8 +230,8 @@ def test_trainDL_Memory():
 
 
 def test_structTrainDL():
-    img_file = 'data/lena.png'
     try:
+        img_file = get_img_file_path("lena.png")
         img = Image.open(img_file)
     except Exception as e:
         print("Cannot load image %s (%s) : skipping test" % (img_file, e))
@@ -376,8 +404,8 @@ def test_structTrainDL():
 
 
 def test_nmf():
-    img_file = 'data/boat.png'
     try:
+        img_file = get_img_file_path("boat.png")
         img = Image.open(img_file)
     except:
         print("Cannot load image %s : skipping test" % img_file)
@@ -413,8 +441,8 @@ def test_nmf():
 
 # Archetypal Analysis, run first steps with FISTA and run last steps with activeSet,
 def test_archetypalAnalysis():
-    img_file = 'data/lena.png'
     try:
+        img_file = get_img_file_path("lena.png")
         img = Image.open(img_file)
     except Exception as e:
         print("Cannot load image %s (%s) : skipping test" % (img_file, e))
diff --git a/version b/version
index 0ce6c9df5cb1a0caeb7671af7d5fa924298be93c..dacc32b008abee77516e2844a77cf0549fa23780 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-2.6.5.1
+2.6.5.2