Storing global configuration data in a pytest/xdist framework - python

I'm building a test framework using python + pytest + xdist + selenium grid. This framework needs to talk to a pre-existing custom logging system. As part of this logging process, I need to submit API calls to: set up each new test run, set up test cases within those test runs, and log strings and screenshots to those test cases.
The first step is to set up a new test run, and the API call for that returns (among other things) a Test Run ID. I need to keep this ID available for all test cases to read. I'd like to just stick it in a global variable somewhere, but running my tests with xdist causes the framework to lose track of the value.
I've tried:
Using a "globals" class; it forgot the value when using xdist.
Keeping a global variable inside my conftest.py file; same problem, the value gets dropped when using xdist. Also it seems wrong to import my conftest everywhere.
Putting a "globals" class inside the conftest; same thing.
At this point, I'm considering writing it to a temp file, but that seems primitive, and I think I'm overlooking a better solution. What's the most correct, pytest-style way to store and access global data across multiple xdist threads?

Might be worth looking into Proboscis, as it allows specific test dependencies and could be a possible solution.

Can you try config.cache E.g. -
request.config.cache.set('run_id', run_id)
refer documention

Related

Choose Python classes to instantiate at runtime based on either user input or on command line parameters

I am starting a new Python project that is supposed to run both sequentially and in parallel. However, because the behavior is entirely different, running in parallel would require a completely different set of classes than those used when running sequentially. But there is so much overlap between the two codes that it makes sense to have a unified code and defer the parallel/sequential behavior to a certain group of classes.
Coming from a C++ world, I would let the user set a Parallel or Serial class in the main file and use that as a template parameter to instantiate other classes at runtime. In Python there is no compilation time so I'm looking for the most Pythonic way to accomplish this. Ideally, it would be great that the code determines whether the user is running sequentially or in parallel to select the classes automatically. So if the user runs mpirun -np 4 python __main__.py the code should behave entirely different than when the user calls just python __main__.py. Somehow it makes no sense to me to have if statements to determine the type of an object at runtime, there has to be a much more elegant way to do this. In short, I would like to avoid:
if isintance(a, Parallel):
m = ParallelObject()
elif ifinstance(a, Serial):
m = SerialObject()
I've been reading about this, and it seems I can use factories (which somewhat have this conditional statement buried in the implementation). Yet, using factories for this problem is not an option because I would have to create too many factories.
In fact, it would be great if I can just "mimic" C++'s behavior here and somehow use Parallel/Serial classes to choose classes properly. Is this even possible in Python? If so, what's the most Pythonic way to do this?
Another idea would be to detect whether the user is running in parallel or sequentially and then load the appropriate module (either from a parallel or sequential folder) with the appropriate classes. For instance, I could have the user type in the main script:
from myPackage.parallel import *
or
from myPackage.serial import *
and then have the parallel or serial folders import all shared modules. This would allow me to keep all classes that differentiate parallel/serial behavior with the same names. This seems to be the best option so far, but I'm concerned about what would happen when I'm running py.test because some test files will load parallel modules and some other test files would load the serial modules. Would testing work with this setup?
You may want to check how a similar issue is solved in the stdlib: https://github.com/python/cpython/blob/master/Lib/os.py - it's not a 100% match to your own problem, nor the only possible solution FWIW, but you can safely assume this to be a rather "pythonic" solution.
wrt/ the "automagic" thing depending on execution context, if you decide to go for it, by all means make sure that 1/ both implementations can still be explicitely imported (like os.ntpath and os.posixpath) so they are truly unit-testable, and 2/ the user can still manually force the choice.
EDIT:
So if I understand it correctly, this file you points out imports modules depending on (...)
What it "depends on" is actually mostly irrelevant (in this case it's a builtin name because the target OS is known when the runtime is compiled, but this could be an environment variable, a command line argument, a value in a config file etc). The point was about both conditional import of modules with same API but different implementations while still providing direct explicit access to those modules.
So in a similar way, I could let the user type from myPackage.parallel import * and then in myPackage/init.py I could import all the required modules for the parallel calculation. Is this what you suggest?
Not exactly. I posted this as an example of conditional imports mostly, and eventually as a way to build a "bridge" module that can automagically select the appropriate implementation at runtime (on which basis it does so is up to you).
The point is that the end user should be able to either explicitely select an implementation (by explicitely importing the right submodule - serial or parallel and using it directly) OR - still explicitely - ask the system to select one or the other depending on the context.
So you'd have myPackage.serial and myPackage.parallel (just as they are now), and an additional myPackage.automagic that dynamically selects either serial or parallel. The "recommended" choice would then be to use the "automagic" module so the same code can be run either serial or parallel without the user having to care about it, but with still the ability to force using one or the other where it makes sense.
My fear is that py.test will have modules from parallel and serial while testing different files and create a mess
Why and how would this happen ? Remember that Python has no "process-global" namespace - "globals" are really "module-level" only - and that python's import is absolutely nothing like C/C++ includes.
import loads a module object (can be built directly from python source code, or from compiled C code, or even dynamically created - remember, at runtime a module is an object, instance of the module type) and binds this object (or attributes of this object) into the enclosing scope. Also, modules are garanteed (with a couple caveats, but those are to be considered as error cases) to be imported only once for a given process (and then cached) so importing the same module twice in a same process will yield the same object (IOW a module is a singleton).
All this means that given something like
# module A
def foo():
return bar(42)
def bar(x):
return x * 2
and
# module B
def foo():
return bar(33)
def bar(x):
return x / 2
It's garanteed that however you import from A and B, A.foo will ALWAYS call A.bar and NEVER call B.bar and B.foo will only ever call B.bar (unless you explicitely monkeyptach them of course but that's not the point).
Also, this means that within a module you cannot have access to the importing namespace (the module or function that's importing your module), so you cannot have a module depending on "global" names set by the importer.
To make a long story short, you really need to forget about C++ and learn how Python works, as those are wildly different languages with wildly different object models, execution models and idioms. A couple interesting reads are http://effbot.org/zone/import-confusion.htm and https://nedbatchelder.com/text/names.html
EDIT 2:
(about the 'automagic' module)
I would do that based on whether the user runs mpirun or just python. However, it seems it's not possible (see for instance this or this) in a portable way without a hack. Any ideas in that direction?
I've never ever had anything to do with mpi so I can't help with this - but if the general consensus is that there's no reliable portable way to detect this then obviously there's your answer.
This being said, simple stupid solutions are sometimes overlooked. In your case, explicitly setting an environment variable or passing a command-line switch to your main script would JustWork(tm), ie the user should for example use
SOMEFLAG=serial python main.py
vs
SOMEFLAG=parallel mpirun -np4 python main.py
or
python main.py serial
vs
mpirun -np4 python main.py parallel
(whichever works best for you needs - is the most easily portable).
This of course requires a bit more documentation and some more effort from the end-user but well...
I'm not really what you're asking here. Python classes are just (callable/instantiable) objects themselves, so you can of course select and use them conditionally. If multiple classes within multiple modules are involved, you can also make the imports conditional.
if user_says_parallel:
from myPackage.parallel import ParallelObject
ObjectClass = ParallelObject
else:
from myPackage.serial import SerialObject
ObjectClass = SerialObject
my_abstract_object = ObjectClass()
If that's very useful depends on your classes and the effort it takes to make sure they have the same API so they're compatible when replacing each other. Maybe even inheritance à la ParallelObject => SerialObject is possible, or at least a common (virtual) base class to put all the shared code. But that's just the same as in C++.

How to run all the tests on the same deal. Selenium Webdriver + Python

I'm a newbie in automation testing.
Currently doing a manual testing and trying to automate the process with Selenium Webdriver using Pyhton.
I'm creating a test suite which will run different scripts. Each script will be running tests on different functionality.
And I got stuck.
I'm working on financial web application. The initial scrip will create financial deal, and all other scripts will be testing different functionality on this deal.
I'm not sure how to handle this situation. Should I just pass the URL from the first script (newly created deal) into all other scripts in the suite, so all the tests were run on the same deal, and didn't create a new one for each test? How do I do this?
Or may be there is a better way to do this?
Deeply appreciate any advise!!! Thank you!
Preferably you would have each test be able to run in isolation. If you have a way to create the deal through an API or Database rather than creating one through the UI, you could call that for each test. And, if possible, also clean up that data after your test runs.
If this is not possible, you could also record some data from a test in a database, xml, or json file. Then your following tests could read in that data to get what it needs to run the test. In this case it would be some reference to your financial deal.
The 2nd option is not ideal, but might be appropriate in some cases.
There's a couple of approaches here that might help, and some of it depends on if you're using a framework, or just building from scratch using the selenium api.
Use setup and teardown methods at the suite or test level.
This is probably the easiest method, and close to what you asked in your post. Every framework I've worked in supports some sort of setup and teardown method out of the box, and even if it doesn't, they're not hard to write. In your case, you've got a script that calls each of the test cases, so just add a before() method at the beginning of the suite that creates the financial deal you're working on.
If you'd like a new deal made for each individual test, just put the before() method in the parent class of each test case so they inherit and run it with every case.
Use Custom Test Data
This is probably the better way to do this, but assumes you have db access or a good relationship with your dbm. You generally don't want the success of one test case to rely on the success of another(what the first answer meant by isolaton). If the creation of the document fails in some way, every single test downstream of that will fail as well, even though they're testing a different feature that might be working. This results in a lot of lost coverage.
So, instead of creating a new financial document every time, speak to your DBM and see if it's possible to create a set of test data that either sits in your test db or is inserted at the beginning of the test suite.
This way you have 1 test that tests document creation, and X tests that verify it's functionality based on the test data, and those tests do not rely on each other.

Python Behave sharing data between features

I'm starting with Python Behave as I want to make an API testing thing.
Currently I stumbled upon something that may not be even valid but the question is: Can I share data between features? I could store some in a DB or files but maybe there is something 'builtin'?
Or is this invalid and I shouldn't share data like that, or maybe it's possible inside a feature?
In practice it looks like:
I have to make a request to an endpoint, in the response I get a number that is required to make another request that's needed to be tested.
Yes, you can, and it is trivial. In the same directory where you have your feature files you can create a file named environment.py. In it, you can put:
def before_all(context):
context.db = whatever
The before_all hook is run before all the tests and whatever you set there is available to all features. I use this, for instance, to create a new Selenium instance that will be used by all tests in a test suite. context.db above could be a database connection. This kind of sharing is fine.
What you share should be read-only or be resettable to a known state between tests. It is not a good practice to share writable resources between tests. It makes it hard to figure out what went wrong when a test fails and it makes tests dependent on each other. So if you have a failure on test C but it depends on A and B you cannot just ask Behave to run test C. You have to ask it to run A, B and then C.
If you decide to go against the best practices and do it anyway, you should be aware that new values set on context are scoped by feature and scenario. So if your before_all hook sets context.foo = 1 and then feature A sets context.foo = 2. When feature B runs after feature A, it will see the value 1 for context.foo because Behave will have removed the changes made by feature A (the changes made by feature A are "scoped" to feature A.) Now, you have to remember how Python stores values. If the hook sets context.foo = [] and feature A does context.foo.append(1), then feature B will see that context.foo has the value [1] because context.foo contains a reference to the array and calling append changes the array itself. So it is possible to work around the scoping. This is still not advisable, though.
Last I checked, features are run in alphabetical order. You can force the order by specifying features on the command line: behave B.feature A.feature

Initializing MEDIA_ROOT before each Django Test

I want to my Django tests to create and modify media files. So, much like Django tests do with databases, I want to set up an empty MEDIA_ROOT folder before each test is run.
I figured I'll create a temporary folder and point MEDIA_ROOT to it. However, I can't figure out where to put the code that does this. In this example, a special Runner is created. The runner sets up the media root and tears it down.
Unfortunately, setup_test_environment is called once before the first test function is run, and not every time a test is run.
I tried creating a FileSystemTestCase class that sets up the file system in its setUp function, and have all my test cases derive from it. While this works, it requires every person who writes a testcase to remember to call my setUp method, as it's not called automatically.
Usually I wouldn't bother with this, but the cost of forgetting to call the parent setUp method can be very high - if someone forgets the call and the tests are accidentally run on a live system, bad things will happen.
EDIT: The temporary solution I've found was to implement both my own runner and a base TestCase. Both set up a temporary MEDIA_ROOT, so if someone forgets to call my setUp method, the test will run in the temporary folder of the previous test, or the one set up by the runner. This can cause tests to fail, but will not ruin live data.
I'm hoping for a more elegant solution.
It seems to me that you're trying to address two separate issues:
Allow tests to run independently (with regard to MEDIA_ROOT) when testers do the right thing (i.e. inherit from your test class and call your setUp()).
Keep testers from messing up real data when they accidentally do the wrong thing.
Given that, I think a two-pronged approach makes sense. Your setUp() solves problem 1. Setting up MEDIA_ROOT in the test runner, though, hides the fact that your testers have done the wrong thing. Instead I would just focus on protecting the data: for example, you could set MEDIA_ROOT to None. That would shield the real data in MEDIA_ROOT; make it more likely that the tester will see an error if they don't use your setUp(); and reduce code duplication.
A more robust approach would be to write your own test runner that does the setup before each test (modeled after the way Django handles the database), but that may be overkill for your needs.

Unit testing with nose: tests at compile time?

Is it possible for the nose unit testing framework to perform tests during the compilation phase of a module?
In fact, I'd like to test something with the following structure:
x = 123
# [x is used here...]
def test_x():
assert (x == 123)
del x # Deleted because I don't want to clutter the module with unnecessary attributes
nosetests tells me that x is undefined, as it apparently runs test_x() after importing the module. Is there a way of having nose perform test during the compilation phase while having the module free unnecessary resources after using them?
A simple way to handle this would be to have a TESTING flag, and write:
if not TESTING:
del x
However, you won't really be properly testing your modules as the tests will be running under different circumstances to your code.
The proper answer is that you shouldn't really be bothering with manually cleaning up variables, unless you have actually had some major performance problems because of them. Read up on Premature Optimization, it's an important concept. Fix the problems you have, not the ones you maybe could have one day.
According to nose's main developer Jason Pellerin, the nose unit testing framework cannot run tests during compilation. This is a potential annoyance if both the module "construction" and the test routines need to access a certain variable (which would be deleted in the absence of tests).
One option is to discourage the user from using any of these unnecessarily saved variables by prepending "__" to their name (this works also for variables used in class construction: they can be one of these "private" globals).
Another, perhaps cleaner option is to dedicate a module to the task: this module would contain variables that are shared by the module "itself" (i.e. without tests) and its tests (and that would not have to be shared were it not for the tests).
The problem with these option is that variables that could be deleted if there were no tests are instead kept in memory, just because it is better for the test code to use them. At least, with the above two options, the user should not be tempted to use these variables, nor should he feel the need to wonder what they are!

Categories

Resources