py.test: How to avoid naming every test? Generate the test name dynamically - pytest

Occasionally I name tests like test_X1, test_X2,...
because
it is always about feature X and
the tests are small and
I don't want a descriptive name that is longer than the test
Especially when things still change a lot I don't want to think of a name at all.
The line where the test resides defines the test in the file.
So how to use the line for the test name?

Here is a way to name the tests dynamically test_<line>.
from inspect import currentframe
def namebyline(f):
line = currentframe().f_back.f_lineno
globals()['test_' + str(line)] = f
#namebyline
def _(): # becomes test_6
assert True
#namebyline
def _(): #becomes test_9
assert False

Related

Cimplicity Screen - one object/button that is dependent on hundreds of points

So I have created a huge screen that essentially just shows the robot status for every robot in this factory (individually)… At the very end of the project, they decided they want one object on the screen that blinks if any of the 300 robots fault. I am trying to think of a way to make this work. Maybe a global script of some kind? Problem is, I do not do much scripting in Cimplicity, so any help is appreciated.
All the points that are currently used on this screen (to indicate a fault) have very similar names… as in, the beginning is the same… so I was thinking of a script that could maybe recognize if a bit is high based on PART of it's string name characteristic. The end will change a little each time, but I am sure there is a way to only look for part of a string and negate the rest. If the end has to be hard coded, that's fine.
You can use a Python script in Cimplicity.
I will not go into detail on the use of python in Cimplicity, which is well described in the documentation indicated above.
Here's an example of what can be done... note that I don't have a way to test it and, of course, this will work if the name of your robots in the declaration follows the format Robot_1, Robot_2, Robot_3 ... Robot_10 ... Robot_300 and it also depends on the Name and the Type of the fault variable... as you didn't define it, I imagine it can be an integer, with ZERO indicating no error. But if you use something other than that, you can easily change it.
import cimplicity
(...)
OneRobotWithFault = False
# Here you get the values and check for fault
for i in range(0, 300):
pointName = f'MyFactory.Robot_{i}.FaultCode'
robotFaultCode = cimplicity.point_get(pointName)
if robotFaultCode > 0:
OneRobotWithFault = True
break
# Set the status to the variable "WeHaveRobotWithFault"
cimplicity.point_set("WeHaveRobotWithFault", OneRobotWithFault)

In pytest, how to run against different ip for cases with different marks or name patterns

I have a question here.
I am using Pytest.
I have two cases with mark and name as:
1)
#pytest.mark.ip1
def test_ip1_actions()
...
#pytest.mark.ip2
def test_ip2_actions()
...
what I want is:
if the mark is ip1, then I will run the test against 1st ip - 192.168.2.23; if the mark is ip2, then the case should run against 2nd ip - 192.168.2.100
or:
based on the name, if case name contains "ip1", it will run against 1st ip; if case name contains "ip2", then run against 2nd ip.
In fact I have many cases to run against 2 ips, not only 2. and the ip information (as well some other information of the two hosts) are written in an json file. So I would like to find out a general and simple solution.
I tried but didn't get it.
I thought maybe do something in the conftest.py file? for example, before the case running, judge the mark or the name? But don't know how to handle.
Looking forward to your advice, to my first question in stackoverflow! :)
thanks very much!
Don't quite understand, and it's hard to understand more without seeing an example test, but how about using parameters to determine which ips are passed in.
Say you have a test to check ips are accessible. This would be one way to do it, but it's not very nice or extensible.
def test_ip_accessible():
assert is_accessible("192.168.2.23")
assert is_accessible("192.168.2.100")
Instead you can create helper functions that return specific ips, or a list of ips, and then use these as parameters.
ip_map: Dict[int, str] = { # ip id to address
1: "192.168.2.23",
2: "192.168.2.100",
3: "192.168.2.254",
}
def get_single_ip(id_: int) -> str:
return ip_map[id_]
def get_multiple_ips(*ids) -> Tuple[str]:
return tuple(ip_map[i] for i in ids)
#pytest.mark.parametrize("ip_address", [get_single_ip(1), get_single_ip(2)])
def test_ip_accessible(ip_address):
# ip_address will be 192.168.2.23 then 192.168.2.100 in two separate tests
assert is_accessible(ip_address)
#pytest.mark.parametrize("ip_addresses", [get_multiple_ips(1, 2), get_multiple_ips(2, 3)])
def test_with_multiple_ips(ip_addresses):
# this test will also be run twice. the first time, ip_addresses will be
# ["192.162.2.23", "192.168.2.100"]. the second time, it will be
# ["192.168.2.100", "192.168.2.254"].
assert something_with_a_list_of_ips(ip_addresses)

pytest function without return value

I'm trying to do a pytest on a function without return value, but obviously value is None in pytets. I was wondering if there is a solution for that?
here is function which I'm trying to test:
def print_top_movies_url():
for item in movie_link[:100]:
print item.contents[1]
The best thing to do would be to separate getting the top movies and printing them.
For example, you could have a top_movie_urls which looks like this:
def top_movie_urls():
urls = []
for item in movie_link[:100]:
urls.append(item.contents[1])
return urls
(or make it a generator function)
That's easy to test, and wherever you call it, you can now just do something like print('\n'.join(top_movie_urls())).
If you really want to test the output instead, you can use pytest's output capturing to access the output of the tested function and check that.

Adding user defined keywords or Test Attribute

I would like to add custom attributes (or Keywords) to a test which I can access during pytest_runtest_logreport.
What I have been doing currently is to set a marker like this
#pytest.mark.TESTID(98157) for tests and then use this in pytest_runtest_logreport as report.keywords['TESTID'] which returns a tuple of length 1 having value 98157. So far so good.
But when I tried to add another marker with defect ID like this #pytest.mark.JIRA("MyJIRA-124") this report.keywords['JIRA'] this gives me integer 1.
So my question is can we not create parameterized marker with string parameters
AND
If that is the could be the probable workaround for me.
Unfortunately "report" will not have this vaules in default implmentation as it's only a dict with 1 as values for each key (source code)
I think that the easiest workaround would be to change how the "report" is constructed using pytest_runtest_makereport hook. It could be as simple as this:
from _pytest.runner import pytest_runtest_makereport as _makereport
def pytest_runtest_makereport(item, call):
report = _makereport(item, call)
report.keywords = dict(item.keywords)
return report
Then in pytest_runtest_logreport, under report.keyword['JIRA'] you will find MarkInfo object

pytest - Override a test funciton defaullt value

i have a pytest testconf and a test function
i want that when running:
pytest -q -option="choose_a"
it would pass A
and when running
pytest
it would pass B
the problem is that it does not see 'my_param' in fixtures since it has a default parameter. what can I do to make this working and elegant?
my current temp solution (which I don't like) is removing the default value and assign it with an "if" statement in conftest.pytest_generate_tests....pls give me a better one :)
conftest.py:
def pytest_addoption(parser):
parser.addoption("--myoption", action="append", default=[], help="list of choices")
def pytest_generate_tests(metafunc):
if 'my_param' in metafunc.fixturenames:
if 'choose_a' in metafunc.config.option.myoption:
metafunc.parametrize("my_param", ["A"])
mytest.py:
def test_my(my_param='B'):
do_stuff(my_param)