I have to do some testing using pytest, but I have no idea where to start.
Here is piece of a code which i would like to test:
def print_url_and_id():
for item in movie_link[:100]:
print item.contents[1], "The ID of this movie is:", '"' + item.contents[1]['href'][7:16] + '"'
Could anyone tell me how it suppose to look like?
You can do something like this:
import pytest
def parametrized():
expected_results = ["movie_link_01", "movie_link_02"]
movie_link = ["movie_link_01", "movie_link_02", "movie_link_03"]
# you can define your new_movie_link list like this as you have done, but instead of
# printing it, add it to a this new_movie_link list
return [(item_1, item_2) for item_1, item_2 in zip(movie_link[:2], expected_results)]
#pytest.mark.parametrize("movie_link, expected", parametrized())
def test_parametrizer(movie_link, expected):
assert movie_link == expected
Related
I have a parameterized test that receives a long string as an argument, and the problem is that when executing pytest it prints the entire string
#pytest.mark.parametrize(
"long_string_p1, long_string_p2",
[
(a,b) for a,b in zip(list_str_1, list_str_2)
]
)
def test_with_long_strings(long_string_p1, long_string_p2):
# Logic
assert long_string_p1 != long_string_p2
pytest
test_example.py::test_with_long_strings[looooooooong string1-loooooooooong string2] PASSED
I would like it to only show the last part of the string, somehow change the logic so that it doesn't show the whole string but only a part
You should be able to use pytest.param to enhance the id values used. Could make a helper to determine the name based on your custom logic.
Here's an example where it strips off the last value in the string and joins them with a dash:
import pytest
list_str_1 = ["looooooooong string1"]
list_str_2 = ["loooooooooong string2"]
def make_param(args):
return pytest.param(*args, id='-'.join([s.split(" ")[-1] for s in args]))
#pytest.mark.parametrize(
"long_string_p1, long_string_p2",
list(map(make_param, zip(list_str_1, list_str_2)))
)
def test_with_long_strings(long_string_p1, long_string_p2):
assert long_string_p1 != long_string_p2
You can also supply a function as the ids argument to format each entry individually:
import pytest
list_str_1 = ["looooooooong string1"]
list_str_2 = ["loooooooooong string2"]
def id_function(val):
return val.split(" ")[-1]
#pytest.mark.parametrize(
"long_string_p1, long_string_p2",
[*zip(list_str_1, list_str_2)],
ids=id_function
)
def test_with_long_strings(long_string_p1, long_string_p2):
assert long_string_p1 != long_string_p2
I want to pass code to a test() routine, which has to :
print the code
execute it
and finally do stuff with the result.
should handle args in the background
For quick code snippets I can use eval(code-string), like this:
def test_eval(expr_str, expected):
global a,b
res = eval(expr_str) == expected
print(f'{res} : {expr_str}')
but for:
code with assignment
test() should do argumentless calling of fun(), even for fun(a, b...)
or longer code
the approach is unusable.
SOLVED
def test(fun,expected,args):
res = fun(*args) == expected
expr = inspect.getsource(fun)
print(f'{res} : {expr}')
def tests():fun()
def w(a,b):#args
a += b #assignment
return a.sym == "(a + b)"
a = ...
b = ...
test(w,True,(a,b))
better ideas?
I am using Selenium Webdriver with Firefox to run my code. I am using pytest so I can run the functions in parallel. I have three main functions: first and second function return something and third function uses the data from the pervious functions. My problem is that I want to execute third function with the data returned from two functions once the first two functions have finished.
The code itself is actually much more complex but to clarify I made a new code which demonstrates my problem. I have two functions, one is named "test1" and other is named "test2" - both of these run parallel and return information. I also have a third function named "test3" to process the data returned from test1 and test2 once they're both finished. In this example "comp" should just print out some text.
Here's the code:
import unittest
from time import sleep
from selenium import webdriver
# pytest -s -v tests.py <----- I use to execute this script
# py.test -s tests.py -d --tx 2*popen//python=python2.7 <------- I use this to run the tests in parallel
# For some reason program doesnt print in parallel mode. Although "-s" fixes that in the normal execution of pytest.
class TestParallel(unittest.TestCase):
def setUp(self):
self.browser = webdriver.Firefox(executable_path='./dependencies/geckodriver')
def test1(self):
browser = self.browser
browser.get('https://www.google.com/')
asd = browser.find_element_by_xpath("/html/body/div/div[3]/div[1]/div/div/div/div[1]/div[1]/a").text # returns "Gmail"
sleep(2)
print asd
return asd
def test2(self):
browser = self.browser
browser.get('https://www.google.com/')
asd2 = browser.find_element_by_xpath("/html/body/div[1]/div[3]/div[1]/div/div/div/div[1]/div[2]/a").text # returns "Images"
sleep(1)
print asd2
return asd2
def test3(self):
print "word from test1 is " + TestParallel.test1(self) + " and word from test2 is " + TestParallel.test2(self)
def tearDown(self):
self.browser.quit()
if __name__ == "__main__":
unittest.main()
Maybe someone has ideas/suggestions as to how I could resolve this issue. Thank you!
Edit
Ok, here's another idea:
Declare two empty global string variables before test1 and test2 are
run.
Have test1 and test2 write to those two variables
Have test3
wait with a while loop until both of the variables contain something
or until a specific amount of time passes.
class TestParallel(unittest.TestCase):
string_a = ""
string_b = ""
def setUp(self):
self.browser = webdriver.Firefox(executable_path='./dependencies/geckodriver')
def test1(self):
[..]
string_a = "result_a"
def test2(self):
[..]
string_b = "result_b"
def test3(self):
counter = 0;
while ("" in string_a && "" in string_b):
sleep(0.1)
counter = counter + 1
if counter > 200:
break
print "word from test1 is " + string_a + " and word from test2 is " + string_b
Hmm, perhaps:
def test3(self):
counter = 0;
while ("" in string_a && "" in string_b):
sleep(0.1)
counter = counter + 1
if counter > 1000:
break
print "word from test1 is " + string_b + " and word from test2 is " + string_b
test1 and test2 don't really look like tests... they look like they should be functions that return data from the page that are used in a test. I would do something more like
def get_thing1(self):
return self.browser.find_element_by_xpath("/html/body/div/div[3]/div[1]/div/div/div/div[1]/div[1]/a").text # returns "Gmail"
def get_thing2(self):
return self.browser.find_element_by_xpath("/html/body/div[1]/div[3]/div[1]/div/div/div/div[1]/div[2]/a").text # returns "Images"
def test1(self):
browser = self.browser
browser.get('https://www.google.com/')
print "word from test1 is " + get_thing1(self) + " and word from test2 is " + get_thing2(self)
These two methods, get_thing1 and get_thing2 assume that you are on the right page and all they do is return whatever you are looking for. Please change the names to something more descriptive... I wasn't sure what exactly they were returning so I made up some generic name.
Now you only have test1 that runs and two methods that pull things from the page. You can run test1 as many times as you want... in parallel, etc. ... in a loop or whatever you desire.
Extra notes:
You really don't want to use XPaths that are that many levels deep and/or that start at the HTML tag. They are very brittle (likely to break with any little change to the HTML).
You should take some time to read about the page object model. You should create a GoogleSearchResultsPage page object and have get_thing1() and get_thing2() be in that page object. You test would then progress from the GoogleSearchPage to the GoogleSearchResultsPage and would then call those two methods, etc. to get data from the page.
I want to get inner function result so i code it like
def main():
def sub():
a = 1
print a
exec main.__code__.co_consts[1]
using above code working successfully but i want to pass the argument to the sub function like...
def main():
def sub(x):
a = x + 1
return a
ans = exec main.__code__.co_consts[1]
print ans
in that problem is i don't know how to pass that x value.
that work must need to exec so that how to pass that x value with exec without interaction of main function
Maybe something like the code below, as suggested by this SO answer
def main():
def sub():
a = x + 1
print a
return a
exec(main.__code__.co_consts[1], {'x': 1} )
I am really new to python, so this might be really easy.
I want to print two strings defined in a class as static members with a class method that yields each string.
This is a simplified version of what I am trying to do:
#!/usr/bin/python
import sys
class test:
str1 = "Hello"
str2 = "World\n" #"\n" is needed for the example
def printMe(self):
yield test.str1
yield test.str2
hello = test()
print "Testing initiated:"
sys.stdout.write(hello.printMe())
sys.stdout.write(hello.printMe())
This is the output:
sys.stdout.write(hello.printMe()) TypeError: expected a character
buffer object
You are attempting to use a generator function, read about the yield keyword here
import sys
class Test:
def __init__(self): # it's possible to initialise these attributes in the __init__ method, so they are created on class instantiation(when you did hello = Test())
self.str1 = "Hello"
self.str2 = "World\n" #"\n" is needed for the example
def printMe(self):
for i in [self.str1, self.str2]:
yield i
app = Test()
print "Testing initiated:"
for i in app.printMe():
print i # is there a reason why you can't use print?
If however you want to print the lines one at a time, at specific points in the code, like in your loop you mentioned in the comment:
gen = app.printMe()
then every time you want to print:
gen.next()
this triggers the next yield statement. The generator function effectively 'holds'/remembers it's place until you call next again, until all the yield statements have been yielded.
You should do something like this
for line in hello.printMe():
print line
But really there are a lot of easier ways than using yield statements.
using yield turns your function into a generator. If this is really what you want, you will need to iterate over the generator to get the values:
gen = hello.printMe()
sys.stdout.write(gen.next())
sys.stdout.write(gen.next())
or better:
for prop in hello.printMe():
sys.stdout.write(prop)
Your printMe method is a generator function, which returns an iterable. You need to iterate over it to get the results :
for item in hello.printMe():
print item
You can do this, but I'm using print, hope this helps you:
class test:
str1 = "Hello"
str2 = "World\n" #"\n" is needed for the example
def printMe(self):
yield test.str1
yield test.str2
hello = test()
print "Testing initiated:"
out = hello.printMe()
print(out.next(),end=' ')
print(out.next(),end=' ')