A Smattering of Selenium #115

The big news in the twitter-verse yesterday was the announcement of Apple Sauce and Android Sauce from Sauce Labs.
  • The big news in the twitter-verse yesterday was the announcement of Apple Sauce and Android Sauce from Sauce Labs. I of course jumped all over that bandwagon with Apple Sauce and Android Sauce .. Yummy! (And Fully Supported) * Accessing priviledged Javascript APIs from your web page in Firefox with Selenium lets to pull even more strings of the browser than you were supposed to be able to. Something I suspect Marionette will make redundant, but until then…

  • Use Xvfb, Selenium and Chrome to drive a web browser in PHP starts with the same-old, same-old, but scroll down to the bottom for squid and iptables and other things

  • Integrating Selenium with ALM – a simple recipe shows how to do it, but I still say whomever packages this up really well will make a lot of money

  • You Papers Please is something that will start to come up in the Se community soon-ish I fear.

  • Emulator Networking is a link I wish I had before spending an hour or so trying to search SO etc. for it

  • The Axis of Eval is just funny

  • BrowsermobRESTclient is a Java client for the BrowserMob Proxy

  • My Shallow Dive into iOS automation looks at the various options out there for native automation

  • Py.Test is awesome. And makes my brain hurt.
    slides:

    Funcargs & other fun with pytest from Brianna Laugher

    video:

    code:

    These are snippets of py.test in action, used in a talk given at
    PyCon AU 2012 in Hobart, Tasmania. They are all relevant for
    py.test 2.2 except where specified. Where taken from open source
    projects I have listed a URL, some examples are from the py.test
    documentation, some are from my workplace.
    Apart from things called test_*, these functions should probably
    be in your conftest.py, although they can generally start life in
    your test files.
    Talk info: http://2012.pycon-au.org/schedule/52/view_talk
    Slides: http://www.slideshare.net/pfctdayelise/funcargs-other-fun-with-pytest
    http://pytest.org/
    http://stackoverflow.com/questions/tagged/py.test
    http://codespeak.net/mailman/listinfo/py-dev
    http://lists.idyll.org/listinfo/testing-in-python
    ##############
    # informative error reporting
    # tb==native
    Traceback (most recent call last):
    File "/workspace/Review/GFESuite/tests/unit/formatters/test_Precis.py",
    line 882, in test_lowDetail12hourWxBrackets
    assert 'morningZ' in words
    AssertionError: assert 'morningZ' in 'morning shower or two'
    # tb==short
    tests/unit/formatters/test_Precis.py:882: in test_lowDetail12hourWxBrackets
    > assert 'morningZ' in words
    E assert 'morningZ' in 'morning shower or two'
    # tb==long
    mockAccessor =
    def test_lowDetail12hourWxBrackets(mockAccessor):
    """
    Initial 6 hours of wx is correctly being washed out to the first 12 hours.
    """
    mockAccessor.mockGrids([
    Flat("Sky", 0, 24, 0),
    Flat("Wx", 0, 6, "Sct:SH:m::"),
    Flat("Wx", 6, 24, "NoWx"),
    ])
    _icon, words = precisIconWords(mockAccessor, period=6, detail='low')
    assert 'early' not in words
    > assert 'morningZ' in words
    E assert 'morningZ' in 'morning shower or two'
    tests/unit/formatters/test_Precis.py:882: AssertionError
    # add back in unittest assert statements
    # apparently cribbed from something in nose
    def pytest_namespace():
    """Make unittest assert methods available.
    This is useful for things such floating point checks with assertAlmostEqual.
    """
    import unittest
    class Dummy(unittest.TestCase):
    def dummy(self):
    pass
    obj = Dummy('dummy')
    names = {name: member
    for name, member in inspect.getmembers(obj)
    if name.startswith('assert') and '_' not in name}
    return names
    # test file
    def test_kn2kmh():
    py.test.assertAlmostEqual(UnitConvertor.kn2kmh(10), 18.52, places=4)
    ###########################################################
    # add a hook for winpdb
    def pytest_addoption(parser):
    """pytest hook that adds a GFE specific option.
    """
    # Add options.
    group = parser.getgroup('graphical forecast editor options')
    group.addoption('--winpdb', dest='usewinpdb', action='store_true', default=False,
    help=('start the WinPDB Python debugger before calling each test function. '
    'Suggest only using this with a single test at a time (i.e. -k .'))
    def pytest_configure(config):
    # Only do these if this process is the master.
    if not hasattr(config, 'slaveinput'):
    # Activate winpdb plugin if appropriate.
    if config.getvalue("usewinpdb"):
    config.pluginmanager.register(WinPdbInvoke(), 'winpdb')
    class WinPdbInvoke:
    def __init__(self):
    print "initialising winpdb invoker"
    def pytest_pyfunc_call(self, pyfuncitem):
    import rpdb2
    rpdb2.start_embedded_debugger('0')
    # then run: py.test -k test_some_specific_thing --winpdb
    # SKIP
    # inside a test, if you need to check something after the environment
    # has been loaded
    if not config.get('ifpServer.allowOfficalWrites'):
    py.test.skip('Official DB writes are not allowed.')
    # decorators:
    import sys
    win32only = pytest.mark.skipif("sys.platform != 'win32'")
    @win32only
    def test_foo():
    ....
    @py.test.mark.skipif('True')
    def test_foo1():
    print "foo1"
    @py.test.mark.skipif('False')
    def test_foo2():
    print "foo2"
    def test_foo3():
    py.test.skip('inside skip')
    print "foo3"
    # XFAIL
    @py.test.mark.xfail
    def test_foo4():
    assert False
    @py.test.mark.xfail(reason='This is a bad idea')
    def test_foo5():
    assert False
    @py.test.mark.xfail(reason='Maybe this was a bad idea once')
    def test_foo6():
    assert True
    def test_foo7():
    # force test to be recorded as an xfail,
    # even if it would otherwise pass
    py.test.xfail()
    assert True
    # output:
    test_foo4 xfail
    test_foo5 xfail
    test_foo6 XPASS
    test_foo7 xfail
    # with --runxfail:
    test_foo4 FAILED
    test_foo5 FAILED
    test_foo6 PASSED
    test_foo7 FAILED
    # plus tracebacks
    # example custom marks
    @py.test.mark.slow
    @py.test.mark.dstAffected
    @py.test.mark.mantis1543
    @py.test.mark.flaky
    @py.test.mark.unicode
    @py.test.mark.regression
    # in 2.2 - parametrize
    @pytest.mark.parametrize(("input", "expected"), [
    ("3+5", 8),
    ("2+4", 6),
    ("6*9", 42),
    ])
    def test_eval(input, expected):
    assert eval(input) == expected
    view raw 02-marks.py hosted with ❤ by GitHub
    # code
    def isSquare(n):
    n = n ** 0.5
    return int(n) == n
    # test file
    def pytest_generate_tests(metafunc):
    squares = [1, 4, 9, 16, 25, 36, 49]
    for n in range(1, 50):
    expected = n in squares
    if metafunc.function.__name__ == 'test_isSquare':
    metafunc.addcall(id=n, funcargs=dict(n=n, expected=expected))
    def test_isSquare(n, expected):
    assert isSquare(n) == expected
    # then:
    # conftest.py
    def pytest_generate_tests(__multicall__, metafunc):
    """Supports parametrised tests using generate_ fns.
    Use multicall to call any other pytest_generate_tests hooks first.
    If the test_ fn has a generate_ fn then call it with the metafunc
    to let it parametrise the test.
    """
    __multicall__.execute()
    name = metafunc.function.__name__.replace('test_', 'generate_')
    fn = getattr(metafunc.module, name, None)
    if fn:
    fn(metafunc)
    # generate function is simplified, no boilerplate!
    # and we can have one per test function with multiple pairs in a single module, woot!
    def generate_isSquare(metafunc):
    squares = [1, 4, 9, 16, 25, 36, 49]
    for n in range(1, 50):
    expected = n in squares
    metafunc.addcall(id=n, funcargs=dict(n=n, expected=expected))
    import os.path
    def getssh(): # pseudo application code
    return os.path.join(os.path.expanduser("~admin"), '.ssh')
    def test_getssh(monkeypatch):
    def mockreturn(path):
    return '/abc'
    monkeypatch.setattr(os.path, 'expanduser', mockreturn)
    x = getssh()
    assert x == '/abc/.ssh'
    ######################
    # a funcarg to hide/abstract away some monkeypatching
    def pytest_funcarg__noPreviousWarnings(request):
    """pytest funcarg to avoid retrieving REAL previously issued warnings"""
    _ = request.getfuncargvalue("textImporter")
    def setup():
    import RecognisedWarnings as RW
    monkeypatch = request.getfuncargvalue("monkeypatch")
    noPreviousWarnings = lambda _x, _y, _z: None
    monkeypatch.setattr(RW, '_getFDBViewerXML', noPreviousWarnings)
    def teardown(obj):
    pass
    return request.cached_setup(setup, teardown, scope='function')
    # http://anders.conbere.org/blog/2011/05/03/setup_and_teardown_methods_with_py.test/
    # conftest.py
    def setup_fixtures():
    db.insert('...')
    return db
    def teardown_fixtures(db):
    db.destroy('..')
    def py_test_funcarg__db(request):
    return request.cached_setup(
    setup = setup_fixtures,
    teardown = teardown_fixtures,
    scope = "module")
    # test_db.py
    def test_db(db):
    assert(len(db.query(x=y)) >= 1)
    #####################
    # a real DB example
    # still far from a good example for most use cases I suspect, what with the lack of ORM and all
    def setupTestDb():
    """Setup test gfedb ensuring we only do this once."""
    import gfeDB
    if gfeDB.DB_NAME == 'gfedb':
    from NeoConfig import config as neoConfig
    name = 'gfedbtest{}'.format(neoConfig['instance.port'])
    cmd = """\
    mysql -u root -e "DROP DATABASE IF EXISTS {name};
    CREATE DATABASE {name};
    USE {name};
    GRANT ALL ON * TO gfe;
    GRANT ALL ON * TO gfe@localhost;
    FLUSH PRIVILEGES;";
    mysqldump -u root --no-data gfedb | mysql -u root {name}
    """.format(name=name)
    subprocess.check_output(cmd, shell=True)
    gfeDB.DB_NAME = name
    def pytest_funcarg__testDb(request):
    """pytest funcarg for a test gfedb."""
    return request.cached_setup(setup=setupTestDb,
    scope='session')
    def pytest_funcarg__emptyDb(request):
    """pytest funcarg to truncate all tables in the test gfedb."""
    _ = request.getfuncargvalue('testDb')
    def setup():
    from NeoConfig import config as neoConfig
    name = 'gfedbtest{}'.format(neoConfig['instance.port'])
    cmd = """\
    mysql -u root -e "USE {name};
    DELETE FROM fire_event_tb;
    DELETE FROM forecast_tb;
    DELETE FROM issuance_tb;
    DELETE FROM precis_pop_areas_tb;
    DELETE FROM warning_tb;"
    """.format(name=name)
    subprocess.check_output(cmd, shell=True)
    return request.cached_setup(setup=setup,
    scope='function')
    ###########################
    # funcarg to express a prerequisite
    # https://github.com/lunaryorn/pyudev/blob/develop/tests/test_libudev.py
    def pytest_funcarg__libudev(request):
    try:
    return _libudev.load_udev_library()
    except ImportError:
    pytest.skip('udev not available')
    view raw 05-funcargs.py hosted with ❤ by GitHub
    # django example
    # http://pytest-django.readthedocs.org/en/latest/helpers.html
    from myapp.views import my_view
    def test_details(rf):
    request = rf.get('/customer/details')
    response = my_view(request)
    assert response.status_code == 200
    def test_an_admin_view(admin_client):
    response = admin_client.get('/admin/')
    assert response.status_code == 200
    ##############################
    # Google App Engine
    # I wonder if these examples should be using monkeypatch to do os.environ.update??
    # http://pypi.python.org/pypi/pytest_gae/0.2.1
    import os
    from webtest import TestApp
    from main import make_application
    def pytest_funcarg__anon_app(request):
    os.environ.update({'USER_EMAIL': '',
    'USER_ID': '',
    'AUTH_DOMAIN': 'google',
    'USER_IS_ADMIN': '0'})
    return TestApp(make_application())
    def pytest_funcarg__user_app(request):
    os.environ.update({'USER_EMAIL': 'simple@google.com',
    'USER_ID': '1',
    'AUTH_DOMAIN': 'google',
    'USER_IS_ADMIN': '0'})
    return TestApp(make_application())
    def pytest_funcarg__admin_app(request):
    os.environ.update({'USER_EMAIL': 'admin@google.com',
    'USER_ID': '2',
    'AUTH_DOMAIN': 'google',
    'USER_IS_ADMIN': '1'})
    return TestApp(make_application())
    def test_index(anon_app):
    assert "Index" in anon_app.get('/index')
    def test_user_with_user(user_app):
    assert "User" in user_app.get('/users')
    def test_user_with_anon(anon_app):
    assert '302 Moved Temporarily' == anon_app.get('/users').status
    def test_user_with_admin(admin_app):
    assert "Admin" in admin_app.get('/users')
    # https://bitbucket.org/hpk42/py/src/0fca612a4bbd/conftest.py:
    def pytest_generate_tests(metafunc):
    multi = getattr(metafunc.function, 'multi', None)
    if multi is not None:
    assert len(multi.kwargs) == 1
    for name, l in multi.kwargs.items():
    for val in l:
    metafunc.addcall(funcargs={name: val})
    elif 'anypython' in metafunc.funcargnames:
    for name in ('python2.4', 'python2.5', 'python2.6',
    'python2.7', 'python3.1', 'pypy-c', 'jython'):
    metafunc.addcall(id=name, param=name)
    def pytest_funcarg__anypython(request):
    name = request.param
    executable = getexecutable(name)
    if executable is None:
    if sys.platform == "win32":
    executable = winpymap.get(name, None)
    if executable:
    executable = py.path.local(executable)
    if executable.check():
    return executable
    py.test.skip("no %s found" % (name,))
    return executable
    #probable in 2.3 (not yet released)
    # content of conftest.py
    import pytest
    import smtplib
    @pytest.factory(scope="session",
    params=["merlinux.eu", "mail.python.org"])
    def smtp(testcontext):
    smtp = smtplib.SMTP(testcontext.param)
    def fin():
    print ("finalizing %s" % smtp)
    smtp.close()
    testcontext.addfinalizer(fin)
    return smtp
    # content of test_module.py
    def test_ehlo(smtp):
    response = smtp.ehlo()
    assert response[0] == 250
    assert "merlinux" in response[1]
    assert 0 # for demo purposes
    def test_noop(smtp):
    response = smtp.noop()
    assert response[0] == 250
    assert 0 # for demo purposes
    $ py.test --collectonly
    =========================== test session starts ============================
    platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev8
    plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov
    collecting ... collected 4 items
    <Module 'test_module.py'>
    <Function 'test_ehlo[merlinux.eu]'>
    <Function 'test_noop[merlinux.eu]'>
    <Function 'test_ehlo[mail.python.org]'>
    <Function 'test_noop[mail.python.org]'>
    ============================= in 0.02 seconds =============================
    # v0
    # Feb 2010
    # data/test/dbconfig/TEXT/Misc/District_TestScript_2.py
    {
    "name": "Thunderstorms with heavy rain are not dry",
    "commentary": "Mantis 01530",
    "productType": "District",
    "createGrids": [
    ("Fcst", "Wx", "WEATHER", 0, 24, "Chc:TS:!::r", "all"),
    ],
    "notCheckStrings": [
    "dry"
    ],
    "fileChanges": [
    ("District_NSWRO_Definition", "TextUtility", "add", defaultEditAreas, "undo"),
    ],
    "cmdLineVars": str({
    ('Generate Days', 'productIssuance'): 'All Periods',
    ('Issuance Type', 'issuanceType'): 'Morning',
    ('Issued By', 'issuedBy'): None,
    ('CompleteUpdate', 'completeUpdate'): 'yes'}),
    },
    # v1
    # early 2011 - move to pytest, directory restructure
    def test_Thunderstorms_with_heavy_rain_are_not_dry(formatterTester):
    formatterTester.run({
    "commentary": "Mantis 01530",
    "productType": "District",
    "createGrids": [
    Flat("Wx", 0, 24, "Chc:TS:!::r"),
    ],
    "notCheckStrings": [
    "dry"
    ],
    "cmdLineVars": cmdLineVarsIssuanceMorning
    }, defaults())
    # v2
    # feb 2011 - introduction of gridCreator
    def test_Thunderstorms_with_heavy_rain_are_not_dry(formatterTester, gridCreator):
    """Mantis 01530
    """
    gridCreator.createGrids([
    Flat("Wx", 0, 24, "Chc:TS:!::r"),
    ])
    formatterTester.run({
    "productType": "District",
    "notCheckStrings": [
    "dry"
    ],
    "cmdLineVars": cmdLineVarsIssuanceMorning
    }, defaults())
    # lots of specifying areas like this:
    siteID = siteConfig.GFESUITE_SITEID
    DistrictEditAreaDictionary = {
    "NSWRO" : ['NSW_CW013'],
    "VICRO" : ['VIC_CW010', 'VIC_CW011'],
    "QLDRO" : [],
    "SARO" : [],
    "TASRO" : [],
    "WARO" : [],
    "NTRO" : [],
    }
    area = DistrictEditAreaDictionary[siteID]
    # v3
    # March 2011
    def pytest_funcarg__districtArea(request):
    def start():
    areas = {
    "VICRO": DefaultEditArea("VIC_PW007", "Central"),
    "NSWRO": DefaultEditArea("NSW_PW014", "Riverrina"),
    "TASRO": DefaultEditArea("TAS_PW002", "North East"),
    "SARO": DefaultEditArea("SA_PW012", "North West Pastoral"),
    }
    return __areaStart(areas)
    return request.cached_setup(setup=start, scope='session')
    def test_Thunderstorms_with_heavy_rain_are_not_dry(formatterTester, gridCreator, districtArea):
    """Mantis 01530
    """
    gridCreator.createGrids([
    Flat("Wx", 0, 24, "Chc:TS:!::r"),
    ])
    formatterTester.run({
    "productType": "District",
    "notCheckStrings": [
    "dry"
    ],
    "cmdLineVars": cmdLineVarsIssuanceMorning(districtArea.aac)
    }, defaults(districtArea.aac))
    # v4
    # Nov 2011
    # tests/system/formatters/wxPhrase/test_attributes.py
    @py.test.mark.mantis1530
    def test_thunderstormsWithHeavyRainAreNotDry(mockAccessor):
    """'heavy rain' and 'dry' attributes were getting mixed up.
    """
    mockAccessor.mockGrids([
    Flat("Wx", 0, 24, "Chc:TS:!::r"),
    ])
    words = weatherWordsLandAreal(mockAccessor)
    assert "dry" not in words