102

In a python unit test (actually Django), what is the correct assert statement that will tell me if my test result contains a string of my choosing?

self.assertContainsTheString(result, {"car" : ["toyota","honda"]})

I want to make sure that my result contains at least the json object (or string) that I specified as the second argument above

{"car" : ["toyota","honda"]}
Soviut
  • 88,194
  • 49
  • 192
  • 260
user798719
  • 9,619
  • 25
  • 84
  • 123
  • would [https://docs.djangoproject.com/en/1.11/topics/testing/tools/#django.test.Response.json] help? – coya Jun 07 '17 at 22:24

5 Answers5

159

To assert if a string is or is not a substring of another, you should use assertIn and assertNotIn:

# Passes
self.assertIn('bcd', 'abcde')

# AssertionError: 'bcd' unexpectedly found in 'abcde'
self.assertNotIn('bcd', 'abcde')

These are new since Python 2.7 and Python 3.1

Ed I
  • 7,008
  • 3
  • 41
  • 50
76
self.assertContains(result, "abcd")

You can modify it to work with json.

Use self.assertContains only for HttpResponse objects. For other objects, use self.assertIn.

GabLeRoux
  • 16,715
  • 16
  • 63
  • 81
Akshar Raaj
  • 14,231
  • 7
  • 51
  • 45
  • 2
    yes but due to the json structure, it could have some whitespace added which are not problematic in json but problematic if you want to compare with a python string. – francois Aug 13 '13 at 09:29
  • 37
    assertContains is not intended to by used for other object than HttpReponse, prefer using assertTrue with "in" python keyword instead (see my answser below). – Pierre Criulanscy Mar 23 '15 at 08:07
  • 12
    This is wrong. Assertconains is for http responses. – kagronick Apr 28 '16 at 15:23
  • 4
    This answer should clarify that it is tailored for HttpResponse only – rtindru Aug 07 '17 at 07:05
30

You can write assertion about expected part of string in another string with a simple assertTrue + in python keyword :

self.assertTrue("expected_part_of_string" in my_longer_string)
Pierre Criulanscy
  • 8,726
  • 3
  • 24
  • 38
  • 14
    The issue with this strategy is that is can give poor failure messages of the form "AssertionError: False is not true" – jamesc Mar 25 '16 at 12:58
  • 1
    @jamesc agree with you, test should show the error details, any workaround to this issue if using assertTrue? – Luk Aron Nov 08 '19 at 04:05
  • @LukAron If you have to use assertTrue, then you could pass a message which you build beforehand to contain more details: assertTrue(expr, msg=message). If the message gets complicated, then you can extract the message building and assertTrue check into a separate assertion helper, which may have its own tests to ensure it behaves as expected. – jamesc Nov 10 '19 at 13:54
11

Build a JSON object using json.dumps().

Then compare them using assertEqual(result, your_json_dict)

import json

expected_dict = {"car":["toyota", "honda"]}
expected_dict_json = json.dumps(expected_dict)

self.assertEqual(result, expected_dict_json)
Vincent Audebert
  • 1,846
  • 2
  • 15
  • 28
  • 1
    Why using `assertTrue()` instead of [`assertEqual()`](http://docs.python.org/3/library/unittest.html#unittest.TestCase.assertEqual)? At least with `assertEqual()`, the module will print both the result and the expected values. – Denilson Sá Maia Mar 11 '14 at 03:40
  • You are right, assertEqual() is better. I can't find the link but I was pretty sure to have read somewhere to use assertTrue instead of assertEqual. Obviously, I was wrong :) I'll fix the example above. – Vincent Audebert Mar 11 '14 at 04:00
  • 13
    Note that this will be unreliable when you have more than one key in any dictionary because the `dumps()` uses arbitrary ordering and we don't know the ordering of keys in `result`. You'd be much better off using `self.assertEqual(json.loads(result), expected_dict)`. – André Caron Sep 26 '14 at 13:19
10

As mentioned by Ed I, assertIn is probably the simplest answer to finding one string in another. However, the question states:

I want to make sure that my result contains at least the json object (or string) that I specified as the second argument above,i.e., {"car" : ["toyota","honda"]}

Therefore I would use multiple assertions so that helpful messages are received on failure - tests will have to be understood and maintained in the future, potentially by someone that didn't write them originally. Therefore assuming we're inside a django.test.TestCase:

# Check that `car` is a key in `result`
self.assertIn('car', result)
# Compare the `car` to what's expected (assuming that order matters)
self.assertEqual(result['car'], ['toyota', 'honda'])

Which gives helpful messages as follows:

# If 'car' isn't in the result:
AssertionError: 'car' not found in {'context': ..., 'etc':... }
# If 'car' entry doesn't match:
AssertionError: Lists differ: ['toyota', 'honda'] != ['honda', 'volvo']

First differing element 0:
toyota
honda

- ['toyota', 'honda']
+ ['honda', 'volvo']
Community
  • 1
  • 1
jamesc
  • 5,852
  • 3
  • 32
  • 42