<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Dave Dash</title>
 <link href="http://davedash.com/tag/testing/atom.xml" rel="self"/>
 <link href="http://davedash.com/tag/testing"/>
 <updated>2012-01-17T21:54:19-08:00</updated>
 <id>http://davedash.com/</id>
 <author>
   <name>Dave Dash</name>
   <email>dd+atom1@davedash.com</email>
 </author>

 
 <entry>
   <title>DjangoCon Testing Tutorial</title>
   <link href="http://davedash.com/2011/08/10/djangocon-testing-tutorial/"/>
   <updated>2011-08-10T00:00:00-07:00</updated>
   <id>http://davedash.com/2011/08/10/djangocon-testing-tutorial</id>
   <content type="html">&lt;div class=&quot;side&quot;&gt;
   &lt;img src=&quot;/static/images/2011/08/10/djangocon.png&quot;
        width=&quot;287&quot; height=&quot;184&quot;
        alt=&quot;DjangoCon 2011&quot; /&gt;
&lt;/div&gt;


&lt;p&gt;If you want to learn all you can about testing anything in your Django App, see
&lt;a href=&quot;http://djangocon.us/schedule/presentations/30/&quot;&gt;my tutorial&lt;/a&gt; at &lt;a href=&quot;http://djangocon.us/&quot;&gt;DjangoCon&lt;/a&gt;.
It's on September 5th, it'll be 3 hours
long and so far with seven sign ups it will be very hands-on.&lt;/p&gt;

&lt;p&gt;Here's what I think I will cover, but I may change this depending on what the
audience wants:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Testing issues

&lt;ul&gt;
&lt;li&gt;ask people to fill out etherpad with issues they've run into&lt;/li&gt;
&lt;li&gt;ask someone to rank them in order of complexity&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;List an outline of topics

&lt;ul&gt;
&lt;li&gt;post them on etherpad&lt;/li&gt;
&lt;li&gt;have people + them if they are interested&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Testing overview

&lt;ul&gt;
&lt;li&gt;We started in late 2009 early 2010&lt;/li&gt;
&lt;li&gt;Our largest project has 2500 tests&lt;/li&gt;
&lt;li&gt;Our next largest has 1100&lt;/li&gt;
&lt;li&gt;We have pretty good coverage&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;How testing works    in Django

&lt;ul&gt;
&lt;li&gt;I'm not 100% sure on this&lt;/li&gt;
&lt;li&gt;Test runner setups up a new database&lt;/li&gt;
&lt;li&gt;Test runner finds and runs tests&lt;/li&gt;
&lt;li&gt;Tests run class setup&lt;/li&gt;
&lt;li&gt;Test runs each test in a test case

&lt;ul&gt;
&lt;li&gt;Load fixtures&lt;/li&gt;
&lt;li&gt;Tests run setup&lt;/li&gt;
&lt;li&gt;Tests runs the test&lt;/li&gt;
&lt;li&gt;Tests runs teardown&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tests run class Teardown&lt;/li&gt;
&lt;li&gt;You get an F if you're bad and a . if your not.&lt;/li&gt;
&lt;li&gt;Now that you know it, you can hack it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;How we've hacked testing

&lt;ul&gt;
&lt;li&gt;2500 tests is a lot&lt;/li&gt;
&lt;li&gt;We no longer recreate the database when you run the test suite&lt;/li&gt;
&lt;li&gt;In each test case we just load the fixtures once.&lt;/li&gt;
&lt;li&gt;We rearrange the tests so things with the same fixture set run together&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Testing tools that we use at Mozilla

&lt;ul&gt;
&lt;li&gt;nose/django_nose&lt;/li&gt;
&lt;li&gt;nose plugins

&lt;ul&gt;
&lt;li&gt;nicedots&lt;/li&gt;
&lt;li&gt;progressive&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;coverage

&lt;ul&gt;
&lt;li&gt;git + whatchangedpy&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Testing everything, no excuses

&lt;ul&gt;
&lt;li&gt;100% Coverage isn't important&lt;/li&gt;
&lt;li&gt;80% is nice&lt;/li&gt;
&lt;li&gt;Good coverage on tricky things is important&lt;/li&gt;
&lt;li&gt;Some coverage on everything is important&lt;/li&gt;
&lt;li&gt;External&lt;/li&gt;
&lt;li&gt;If you start depending on APIs, Search or different tools you need to be able to test for them.&lt;/li&gt;
&lt;li&gt;Writing these test cases will take less time than this tutorial&lt;/li&gt;
&lt;li&gt;It will save you so much headache in the future.&lt;/li&gt;
&lt;li&gt;The same headaches you save yourself by writing &quot;normal&quot; tests&lt;/li&gt;
&lt;li&gt;Mock easy things

&lt;ul&gt;
&lt;li&gt;use a decorator on any test/view that might use redis&lt;/li&gt;
&lt;li&gt;if redis isn't setup, use the mock client&lt;/li&gt;
&lt;li&gt;mock client doesn't support everything,

&lt;ul&gt;
&lt;li&gt;just what I need to get my tests running -&lt;/li&gt;
&lt;li&gt;feel free to extend it if you use it&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Testing Redis&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Setup/Teardown for complicated tools

&lt;ul&gt;
&lt;li&gt;Good for search and APIs&lt;/li&gt;
&lt;li&gt;Raise SkipTest (nose) if the developer doesn't want to run these tests&lt;/li&gt;
&lt;li&gt;Non realtime tools

&lt;ul&gt;
&lt;li&gt;Testing Sphinx search&lt;/li&gt;
&lt;li&gt;SetupClass

&lt;ul&gt;
&lt;li&gt;load fixtures&lt;/li&gt;
&lt;li&gt;run indexer&lt;/li&gt;
&lt;li&gt;run server&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sphinx server now available for all tests in your test case&lt;/li&gt;
&lt;li&gt;Teardown

&lt;ul&gt;
&lt;li&gt;stop server&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Real time tools

&lt;ul&gt;
&lt;li&gt;Nicer, data can be added in post_save signals or elsewhere in your app&lt;/li&gt;
&lt;li&gt;Testing LDAP

&lt;ul&gt;
&lt;li&gt;Setup

&lt;ul&gt;
&lt;li&gt;Remove LDAP files&lt;/li&gt;
&lt;li&gt;Load an ldif&lt;/li&gt;
&lt;li&gt;Start slapd&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Your code can now touch LDAP&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Testing ElasticSearch

&lt;ul&gt;
&lt;li&gt;We leave ES running all the time.&lt;/li&gt;
&lt;li&gt;Setup

&lt;ul&gt;
&lt;li&gt;Checks for ES support or SkipTest&lt;/li&gt;
&lt;li&gt;Deletes index&lt;/li&gt;
&lt;li&gt;Creates index&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;You can now read/write to ES&lt;/li&gt;
&lt;li&gt;Teardown

&lt;ul&gt;
&lt;li&gt;Delete's index&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Fixtures

&lt;ul&gt;
&lt;li&gt;Fixture Magic&lt;/li&gt;
&lt;li&gt;Model Maker&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;pitfalls

&lt;ul&gt;
&lt;li&gt;dates&lt;/li&gt;
&lt;li&gt;using PDB&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

</content>
 </entry>
 
 <entry>
   <title>Test Driven Confidence</title>
   <link href="http://davedash.com/2010/04/20/test-driven-confidence/"/>
   <updated>2010-04-20T00:00:00-07:00</updated>
   <id>http://davedash.com/2010/04/20/test-driven-confidence</id>
   <content type="html">&lt;p&gt;If you're already testing your web applications, you can skip this post.&lt;/p&gt;

&lt;p&gt;One of the bugs I am working for &lt;a href=&quot;https://addons.mozilla.org/&quot;&gt;AMO&lt;/a&gt; on involves porting a small, but moderately complicated checkbox from our PHP site and rewriting it for Django.&lt;/p&gt;

&lt;p&gt;I decided to look at the existing implementation and found it to not work correctly at all.  This was frustrating, especially since I verified that my own code worked, and that QA verified that it worked as well.&lt;/p&gt;

&lt;p&gt;This is frustrating on many levels.  Chances are some minor assumption I made changed, and thus broke this functionality.  Discovering regressions is never fun, and fixing them is can be long and tedious if you can't automatically verify that everything is working correctly.&lt;/p&gt;

&lt;p&gt;Lucky for me, coming up with tests is easy, you just do what you would do to verify the code satisfies the requirements and then code it.  Sometimes the tests can take longer than writing the actual code, but ultimately you can ship with confidence.  You can be confident that your feature won't break in the future without immediate notice, and you can be confident that your new code won't break anything else.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Making our tests run thrice as fast</title>
   <link href="http://davedash.com/2010/03/16/making-our-tests-run-thrice-as-fast/"/>
   <updated>2010-03-16T00:00:00-07:00</updated>
   <id>http://davedash.com/2010/03/16/making-our-tests-run-thrice-as-fast</id>
   <content type="html">&lt;p&gt;I've written a faster version of &lt;a href=&quot;http://github.com/jbalogh/test-utils/blob/c4c31905a95e59dcc8919c1030b23848ad7fbca6/test_utils/__init__.py#L57&quot;&gt;TransactionTestCase&lt;/a&gt; and packaged it with &lt;a href=&quot;http://github.com/jbalogh/test-utils&quot;&gt;test_utils&lt;/a&gt;.  It's mysql specific since it relies on &lt;code&gt;SET FOREIGN_KEY_CHECKS=0&lt;/code&gt; to flush the database.&lt;/p&gt;

&lt;p&gt;The long story...&lt;/p&gt;

&lt;!-- more--&gt;


&lt;h3&gt;Why speed matters&lt;/h3&gt;

&lt;p&gt;We're closing in on 300 tests for &lt;a href=&quot;http://github.com/jbalogh/zamboni/&quot;&gt;Zamboni&lt;/a&gt;.  As of yesterday, to run our entire test suite it would have taken approximately 5 minutes.  If you run tests before code-reviews, during a code-review, and before you push to master - you've spent about 15 minutes doing tests for a single feature or bug-fix.  We have about 5 developers, so this cycle happens many times in a work day.  In that time many sandwiches can be made and consumed.&lt;/p&gt;

&lt;p&gt;Even shortcuts, like running a subset of tests will only go so far, and ultimately we do want to validate that all our tests pass for any code-change.&lt;/p&gt;

&lt;h3&gt;Testing Sphinx search with &lt;code&gt;TransactionTestCase&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Django recently sped up testing by running tests in a transaction.  However, this means that data never gets committed to the database and therefore external tools, like the Sphinx indexer, will never see any of that data.  So we resort to &lt;code&gt;TransactionTestCase&lt;/code&gt; which &lt;em&gt;will&lt;/em&gt; commit the data.&lt;/p&gt;

&lt;p&gt;Unfortunately &lt;code&gt;TransactionTestCase&lt;/code&gt; is painfully slow.  The accepted practice is to only use &lt;code&gt;TestCase&lt;/code&gt; if you want your tests to be fast.  So, I decided to complain to &lt;a href=&quot;http://blog.ianbicking.org/&quot;&gt;one of our new hires&lt;/a&gt; and he and I decided to tinker in mysql to figure out what was slow.  We discovered the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;delete from [table] is slow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;truncate [table] is slow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;... unless you &lt;code&gt;SET FOREIGN_KEY_CHECKS=0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;So we decided we should do our own tear down.  After some tinkering with &lt;code&gt;cProfiler&lt;/code&gt; I discovered that &lt;code&gt;TransactionTestCase&lt;/code&gt; does a (slow) database &lt;code&gt;flush&lt;/code&gt; on setup for a test case.  This wouldn't do.&lt;/p&gt;

&lt;h3&gt;Making our own &lt;code&gt;TransactionTestCase&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;I decided to make our own &lt;code&gt;TransactionTestCase&lt;/code&gt; and it would just run &lt;code&gt;SET FOREIGN_KEY_CHECKS=0&lt;/code&gt; and &lt;code&gt;TRUNCATE&lt;/code&gt; on each table at tear down time.  It would also not do a &lt;code&gt;flush&lt;/code&gt; on set up.&lt;/p&gt;

&lt;p&gt;We write our tests with the idea that they clean up after themselves.  Rather than having them cleanup after the last test.  This is a requirement for us since &lt;code&gt;django-nose&lt;/code&gt; doesn't reorder tests (nor should it) and a standard &lt;code&gt;django.test.TestCase&lt;/code&gt; assumes a clean database.&lt;/p&gt;

&lt;p&gt;Looking at a single test &lt;code&gt;test_sphinx_indexer&lt;/code&gt;, using &lt;code&gt;django.test.TransactionTestCase&lt;/code&gt; took ~30 seconds.  Using our new &lt;code&gt;TransactionTestCase&lt;/code&gt; it takes ~4 seconds!&lt;/p&gt;

&lt;h3&gt;Fast tests are good&lt;/h3&gt;

&lt;p&gt;We can now run our 275 tests in ~100 seconds versus the ~300 seconds it used to take.  Furthermore, skipping our sphinx tests (which are the only tests that use &lt;code&gt;TransactionTestCase&lt;/code&gt;) only saves us ~10seconds.  That's not a lot of overhead for better coverage.&lt;/p&gt;

&lt;p&gt;This took me the better part of a day, but solving this now, means we're going to more often than not run our sphinx tests all the time rather than skip them.  Our QA team will assure you that search is probably the most regression prone part of our site, so running these tests are vital to quality.&lt;/p&gt;

&lt;p&gt;If you need to use &lt;code&gt;TransactionTestCase&lt;/code&gt; in mysql, &lt;a href=&quot;http://github.com/jbalogh/test-utils&quot;&gt;give ours a try&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>django-fixture-magic: Testing issues with real data.</title>
   <link href="http://davedash.com/2010/03/05/django-fixture-magic-testing-issues-with-real-data/"/>
   <updated>2010-03-05T00:00:00-08:00</updated>
   <id>http://davedash.com/2010/03/05/django-fixture-magic-testing-issues-with-real-data</id>
   <content type="html">&lt;p&gt;I just released &lt;a href=&quot;http://github.com/davedash/django-fixture-magic&quot;&gt;Fixture Magic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When dealing with legacy data, you'll run into all kinds of edge cases.  Perhaps, an object might not display correctly unless it has the right parameters, or if it has null parameters it might not display at all.  So when testing &lt;a href=&quot;http://djangoproject.com/&quot;&gt;Django&lt;/a&gt;, it's nice to actually use non-dummy data.&lt;/p&gt;

&lt;p&gt;Luckily Django has a way of pulling real data out of your database using &lt;code&gt;django.core.serializers&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from addons.models import Addon
a = Addon.objects.get(id=3615)
from django.core.serializers import serialize
jsonize = lambda a: serialize(&quot;json&quot;, a, indent=4)
jsonize([a])
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This solution runs well in a Django shell and can be lots of fun for the whole family... until things get complicated.&lt;/p&gt;

&lt;!--more--&gt;


&lt;h3&gt;Serializing alone isn't enough.&lt;/h3&gt;

&lt;p&gt;Serializing a fixture with foreign keys means you'll have an un-loadable fixture unless you serialize the dependent fixtures.  Even for one or two foreign keys, this can be a pain.  For &lt;a href=&quot;http://addons.mozilla.org/&quot;&gt;addons.mozilla.org&lt;/a&gt;, we have a spidery-web of dependencies: &lt;code&gt;File&lt;/code&gt;s need a &lt;code&gt;Version&lt;/code&gt; which needs an &lt;code&gt;Addon&lt;/code&gt; which need &lt;code&gt;Translation&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;Thus begat the &lt;code&gt;dump_object&lt;/code&gt; management command.  Give it an app, model name and a &lt;code&gt;pk&lt;/code&gt; and it will give you not only a serialized JSON of that object, but all the objects that it requires.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;./manage.py dump_object files.file 64874 64876 &amp;gt; my_new_fixture.json
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This looks for the &lt;code&gt;File&lt;/code&gt; model in the &lt;code&gt;files&lt;/code&gt; app and pulls out of the database &lt;code&gt;File&lt;/code&gt;s instances with &lt;code&gt;pk&lt;/code&gt;s of &lt;code&gt;64874&lt;/code&gt; and &lt;code&gt;64876&lt;/code&gt;.  It then recursively searches for any required objects.&lt;/p&gt;

&lt;h3&gt;Too much serial&lt;/h3&gt;

&lt;p&gt;If you create a lot of fixtures, you'll eventually have overlapping serialized objects.  In &lt;code&gt;addons.mozilla.org&lt;/code&gt; we have &lt;code&gt;Addon&lt;/code&gt;s, &lt;code&gt;Version&lt;/code&gt;s (which depend on &lt;code&gt;Addon&lt;/code&gt;s) and &lt;code&gt;AddonCategory&lt;/code&gt;s (which depend on &lt;code&gt;Addon&lt;/code&gt;s and &lt;code&gt;Category&lt;/code&gt;s).  If we wanted to get serialize a specific &lt;code&gt;Addon&lt;/code&gt;, it's dependent &lt;code&gt;Version&lt;/code&gt;s and &lt;code&gt;AddonCategory&lt;/code&gt;s it makes sense to start with &lt;code&gt;dump_object&lt;/code&gt;ing the related &lt;code&gt;Version&lt;/code&gt; and then &lt;code&gt;dump_objecting&lt;/code&gt; the &lt;code&gt;AddonCategory&lt;/code&gt;.  Both &lt;code&gt;dump_object&lt;/code&gt; commands will fetch the &lt;code&gt;Addon&lt;/code&gt; in question, resulting in duplicated data.&lt;/p&gt;

&lt;p&gt;To combat this we can use &lt;code&gt;merge_fixtures&lt;/code&gt; to dedupe our fixtures:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;./manage.py dump_object versions.version 64874 &amp;gt; 1.json
./manage.py dump_object categories.addoncategory &amp;gt; 2.json
./manage.py merge_json 1.json 2.json &amp;gt; happy_fixture.json
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This should make creating test data slightly less painful.  So &lt;a href=&quot;http://github.com/davedash/django-fixture-magic&quot;&gt;give it a try&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Resolving Django dumpdata errors</title>
   <link href="http://davedash.com/2009/09/13/resolving-django-dumpdata-errors/"/>
   <updated>2009-09-13T00:00:00-07:00</updated>
   <id>http://davedash.com/2009/09/13/resolving-django-dumpdata-errors</id>
   <content type="html">&lt;p&gt;Recently I recieved this wonderful piece of news when I ran &lt;code&gt;./manage.py dumpdata&lt;/code&gt; for the first time:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Error: Unable to serialize database: User matching query does not exist.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I knew this might not work out since I was dealing with a legacy database, but the resolution is quite simple.  First I had to narrow it down to which app was causing this.  Naturally I assumed it was one of the two apps I had, either &lt;code&gt;common&lt;/code&gt; or &lt;code&gt;restaurant&lt;/code&gt;.  So I ran: &lt;code&gt;./manage.py dumpdata common&lt;/code&gt; and &lt;code&gt;./manage.py dumpdata restaurant&lt;/code&gt;.  The latter had no problem whatsoever.&lt;/p&gt;

&lt;p&gt;This made sense, since my &lt;code&gt;common&lt;/code&gt; application was the only one that made any reference to a &lt;code&gt;User&lt;/code&gt;.  By looking in my &lt;code&gt;models.py&lt;/code&gt; for that application, I narrowed it down to my &lt;code&gt;Profile&lt;/code&gt; object.  Sure enough, commenting it out meant I could get my data.&lt;/p&gt;

&lt;p&gt;It ended up being a foreign key mismatch between the &lt;code&gt;profile&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; tables.  Since this is legacy data, this mismatch made sense.  A simple &lt;code&gt;SELECT id,userid FROM profile WHERE userid NOT IN (SELECT id FROM auth_user)&lt;/code&gt; gave me a list of bad profiles.  Removing them allowed me to create my Django fixtures.&lt;/p&gt;
</content>
 </entry>
 

</feed>

