Skip to content

Instantly share code, notes, and snippets.

@szeitlin
Last active November 18, 2015 01:36
Show Gist options
  • Save szeitlin/f60c444fb451e518ee3a to your computer and use it in GitHub Desktop.
Save szeitlin/f60c444fb451e518ee3a to your computer and use it in GitHub Desktop.
django testing

What to Test, How to Test It, and Gotchas

Django tests are based on regular python unit testing, so start there

Do:

• use setUpClass() for anything you're going to re-use in multiple tests, like importing files or creating data structures

• use tearDownClass() if you've created any files

Gotcha: setUpClass() should automatically tearDown anything it did at the end of your tests, but this functionality wasn't available in Django 1.7 (it is there in Django 1.8)

• Write tests with human-readable names. Be specific about what you're testing!

• Use built-in unittest assertions for human-readable errors (and more useful output than plain assertions)

• It's perfectly ok to pull chunks of code from your methods and put them directly in your tests

• Write a test any time you find a bug

• Think of tests as showing how your code should be used, and write them accordingly. The next person who's going to edit your code, or use your code, should be able to refer to your tests to see what it's doing.

• Think of tests as the way you'll make sure things don't break later when you have to refactor

• It's perfectly okay to write the name of the test first, and then figure out how to test it later.

def test_whether_examples_return_likes(self):
    pass 

• Measure something. Keep it simple.

    def test_profile1_E6_has_three_schedule_intervals(self):
           self.assertEqual(len(schedule_intervals.values()), 3)
          

It's okay (and faster) to group assertions together in one test if they all refer to the same object:

    for name, lookup in get_interval_elements(e6.schedule_intervals):
            self.assertTrue(isinstance(name, str))
            self.assertTrue(isinstance(lookup, dict))
            self.assertTrue(isinstance(lookup[name], pd.DataFrame))
            self.assertIn('season_id', lookup[name].columns)

What to Test

• Start with user stories. What do they expect to see? Test for that.

    def test_user_visits_landing_page(self): 
    	self.assertIn(title, landing_page)

• Confirm expected data types

	def test_threshold_dict_creation(self):
    	self.assertTrue(isinstance(named, dict))

• Confirm expected attributes on objects

	def test_profile1_has_no_intervals(self):
        self.assertEqual(len(profile1.intervals), 0)
    
    def test_profile2_has_three_intervals(self):
        self.assertEqual(len(profile2.intervals), 3)

• Features you haven't finished writing yet (Test-Driven Development)

	def test_user_utility_bill_is_accurate(self):
        self.assertEqual(actual_bill, bill_from_usage)
        

• Test pipelines sequentially and swap in positive controls for all variables except the one you're testing. Then in the final tests, test the whole pipeline and use only test data. I do this particularly for complicated features. It ends up looking something like this:

	def test_query_returns_objects(self):
        actual_obj_list = query()
        self.assertEqual(set(expected_obj_list), set(actual_obj_list))
    
    def test_make_dataframes_from_query_objects(self):
        expected_obj_list = query()
        query_obj_dataframe= \
             pd.DataFrame.from_records(expected_obj_list.values())
        self.assertTrue(isinstance(query_obj_dataframe, pd.Dataframe))
   
    def test_query_returns_objects_and_makes_dataframes(self):
		actual_obj_list = query()
        query_obj_dataframe= \
             pd.DataFrame.from_records(actual_obj_list.values())
		self.assertTrue(isinstance(query_obj_dataframe, pd.Dataframe))
        self.assertIn('total success', query_obj_dataframe.columns)

Don't

• Don't test too many things in one test. The whole point of unittests is to help isolate the causes of problems, particularly when you change things later, i.e. to help speed up refactoring and adding new features.

• Don't write useless tests. If the test fails and tells you nothing about why it failed, you did it wrong.

Don't do this:

    def assertNoErrors(self, resp, msg=''):
        return self.assertEqual(len(resp['messages']['error']), 0, msg)

Better:

    def assertNoErrors(self, resp, msg=''):
    	try:
	        self.assertEqual(len(resp['messages']['error']), 0)
		except AssertionError:
            return resp['messages']['error'] 
            
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment