Firefox 6.0 Is My Default Browser Again

I've just made Firefox my default browser again (after using Chrome/Chromium as my default for quite some time). FF6 renders things almost as fast as Chrome, even on our ridiculously complicated pages at work. This was my primary reason for not using FF for such a long time.

However, there are other things that I've decided are worth going back to FF at least for the time being. If any of you know of ways to accomplish this stuff with Chrome, please enlighten me!

  • Tab groups. I often have dozens and dozens of tabs open. Some I like to have open for my "night life." Some I like to have open for research. Some I like to have open for work. Tab groups let me organize these, and hide the ones I'm currently not interested in.
  • Vimperator. This was one thing I missed dearly when I decided to ditch FF for Chrome back in the day. Vimium just doesn't work as well. It's close, but still not as robust. For example, I can still use Vimperator shortcuts when viewing a raw file. Not so with Vimium.
  • Permanent exceptions for bad certificates. Normally this wouldn't be a big deal. You visit a site that has a bad certificate, your browser warns you that bad things could be happening and advises you to be careful. That's great! However, for work we all use several VMs for development (I often have 7 VMs running all the time just for my day-to-day routine). They all share the same certificate, which is bad. Chrome doesn't let me tell it to stop bothering me about the bad certificate. Firefox does.
  • Memory usage. Strangely enough, memory usage isn't something that most people say is awesome in FF these days. But I've personally discovered that it is better than Chrome on "poorly-endowed" systems. Both Chrome and FF are consume gobs of RAM on my regular 8GB+ RAM machines, but I have plenty to spare so it doesn't bother me much. I recently inherited my wife's old MacBook, which only has 1GB of RAM. Chrome is absolutely painful to use--it's slow and brings the rest of the system to a crawl. Firefox is noticeably more responsive on that machine.
  • Selenium. I don't do much UI testing these days, but I do still use Selenium to automate some mundane tasks for work. Haven't checked to see if this is available for Chrome for a while...

So far, the only regret I really have with my decision to move back to Firefox (again, for the time being) is the amount of chrome. Chrome does a good job of staying out of the way and letting me see the web page. Firefox just has just a bit more going on at the top and bottom of the window. Definitely not a deal-breaker on my 1920x1080 laptop though ;)

Selenium Unit Test Reuse

Yesterday, one of the QA guys at work approached me with a question that turned out to be much more interesting to me than I think he had planned. He's been doing some unit testing using Selenium, exporting his test cases to Python. His question was this: how can I run the same unit tests using multiple browsers and multiple target servers?

I'm pretty sure he expected a simple 3-step answer or something like that. Instead, he got my crazy wide-eyed "ohhh... that's something I want to experiment with!" look. I started rambling on about inheritance, dynamic class creation, and nested for loops. His eyes started to look a little worried. He didn't really appreciate the nerdy lingo that much. I told him to pull up a chair and get comfortable.

Since I already had some other work I needed to pay attention to, I didn't want to spend too much time trying to figure out a good way to solve his problem. After about 20 minutes of devilish chuckles and frantic rustling through Python documentation, I came up with the following code:

from types import ClassType
from selenium import selenium
import unittest

IPS = ['192.168.0.1', '192.168.0.2']
BROWSERS = ['safari', 'chrome']

class SomeUnitTest(object):

    def test_something(self):
        sel = self.selenium
        # test code

def main(base):
    suites = []
    results = unittest.TestResult()

    for iidx, ip in enumerate(IPS):
        for bidx, browser in enumerate(BROWSERS):
            def setUp(self):
                self.verificationErrors = []
                self.selenium = selenium("localhost", 4444, "*%s" % self.browser, "http://%s/" % self.ip)
                self.selenium.start()

            def tearDown(self):
                self.selenium.stop()
                self.assertEqual([], self.verificationErrors)

            ut = ClassType('UT_%i_%i' % (iidx, bidx), (unittest.TestCase, base), {'ip': ip, 'browser': browser})
            ut.setUp = setUp
            ut.tearDown = tearDown

            suites.append(unittest.TestLoader().loadTestsFromTestCase(ut))

    unittest.TestSuite(suites)(results)
    for obj, error in results.errors:
        print 'In: ', obj
        print error

if __name__ == "__main__":
    main(SomeUnitTest)

I know, I know... it's got some dirty rotten tricks in it, and there are probably more efficient ways of doing what I've done. If the code offends you, look up at my previous disclaimer: I had other things I needed to be working on, so I didn't spend much time refining this. One thing I'm almost certain could be done better is not monkey patching the dynamic classes with the setUp and tearDown methods. Also, the output at the end of the test execution could definitely use some love. Oh well. Perhaps another day I'll get around to that.

Basically, you just set the servers you need to test and the browsers you want Selenium to run the tests in. Those are at the top of the script: IPS and BROWSERS. Then a new unittest.TestCase class is created for each combination of IP/server+browser. Finally, each of the test cases is thrown into a TestSuite, and the suite is processed. If there were any errors during the tests, they'll be printed out. We weren't really concerned with printing out other information, but you can certainly make other meaningful feedback appear.

Anyway, I thought that someone out there might very well benefit from my little experiment on my co-worker's question. Feel free to comment on your personal adventures with some variation of the code if you find it useful!