Django: Testing

This post is part of my Django series. You can see an overview of the series along with instruction on how to get all the source code here.

This article assumes you are comfortable creating a Django project, can create apps and register them into the INSTALLED_APPS list of the settings file. If not please read my Django HelloWorld article.

Django uses the Python package unittest. This article assumes that you have a basic knowledge of creating unittests with this package. If this is not the case please see my blog post on unittest.

This article assumes you have a project called DjangoSandBox in an application called testintroduction.

This article tests the various parts we have built up over the series; please see here for any explanations of the code we are testing.

Testing Models

Models are pretty easy to test; our first example is using the HelloWorld class we have created previously. Nothing special, a name and date field, a __str()__ function and a Django get_absolute_url function.

#helloworld/models.py
from django.db import models
from django.core.urlresolvers import reverse

class HelloWorld(models.Model):
    first_name = models.CharField(max_length=20)
    now = models.DateTimeField()

    def __str__(self):
        return "{0} {1}".format(self.first_name, self.now)

    @staticmethod
    def get_absolute_url():
        return reverse('helloworld:model')

Testing these functions is nothing more than creating an instance of the class, calling the require function or property and asserting that our result is as required.

#test_models.py

from datetime import date
from django.test import TestCase
from helloworld.models import HelloWorld

class TestHelloWorld(TestCase):
    def setUp(self):
        self.first_name = "Luke"
        self.now = date.today()

        self.a_helloworld = HelloWorld(first_name=self.first_name, now=self.now)

    def test_str(self):
        self.assertEqual("{0} {1}".format(self.first_name, self.now), str(self.a_helloworld))

    def test_get_absolute_url(self):
        self.assertEqual("/helloworld/model/", self.a_helloworld.get_absolute_url())

Our model could contain validation. We can test validation by creating an instance of the model in an invalid state and calling full_clean. If the model is invalid an exception is raised with all the errors contained within a dictionary upon the message_dict property.

# test_models.py
from datetime import date
from django.core.exceptions import ValidationError
from django.test import TestCase
from modelsadvanced.models import ContactDetails, ProxyChild

class TestModelValidation(TestCase):

    def test_model_validation(self):

        try:
            a_contact = ContactDetails(name="Luke", age=101, contactDate=date(year=2015, month=7, day=2))
            a_contact.full_clean()
        except ValidationError as e:

            self.assertEqual({'name': ['Ensure this value has at least 10 characters (it has 4).', 'Luke is barred'],
                              'age': ['Ensure this value is less than or equal to 100.'],
                              'contactDate': ['2015-07-02 is not a future date.']}, e.message_dict)

If we want to test class meta data we can access this via the _meta property:

#test_models.py
class TestModelMeta(TestCase):
    def test_model_meta(self):
        self.assertEqual(True, ProxyChild()._meta.proxy)

We can use list and map to get a list of all field names contained within the fields property of the meta class.:

#test_models.py
class TestModelFields(TestCase):
    def test_model_meta(self):
        field_names = list(map((lambda x: x.name), ContactDetails._meta.fields))

        self.assertEqual(4, len(ContactDetails._meta.fields))
        self.assertEqual(['id', 'age', 'name', 'contactDate'], field_names)

Testing Views

When we test views we can use the test client; it acts as a dummy Web browser. We access it via the client property of our unit test class.

We call get upon the test client along with the URL.

response = self.client.get("/index")

We can use reverse to access the URL via our routing config. Don’t worry we will explicitly test the routing config later/

response = self.client.get(reverse("home:home"))

The response.status_code can be used to assure our response code is as expected.

The response.resolver_match can be use to ensure that the view function used was as expected.

The function self.assertTemplateUsed can be used to assure that the correct template was used.

We can assert that the html is as expected by calling self.assertContains along with sections of html or strings which should be contained within the rendered HTML.

#test_views.py

from django.core.urlresolvers import reverse
from django.test import TestCase
from home import views as home_views

class TestFunctionView(TestCase):
    def test_function_view(self):
        response = self.client.get(reverse("home:home"))

        self.assertEqual(200, response.status_code)
        self.assertEqual(home_views.home, response.resolver_match.func)
        self.assertTemplateUsed(response, template_name='home/home.html')

        self.assertContains(response, '<a href="/helloworld/index/">Hello World</a>')
        self.assertContains(response, '<a href="/templatesintroduction/index">Templates</a>')
        self.assertContains(response, '<a href="/formsintroduction/basic/">Forms</a>')
        self.assertContains(response, '<a href="/viewsintroduction/contact/list">Views</a>')

We can access the context data which was passed to the view via the context property of the response.

#test_views.py

from django.core.urlresolvers import reverse
from django.test import TestCase
from helloworld import views as helloworld_views

class TestContextData(TestCase):
    def test_context_data(self):
        response = self.client.get(reverse("helloworld:template"))

        self.assertEqual('Obelix', response.context["who"])
        self.assertEqual(False, response.context["islong"])

Testing Class Based Views

In the example two up we used the following code to ensure the correct view function was used:

self.assertEqual(home_views.home, response.resolver_match.func)

If we want to assure that the correct class view was used we need to compare the name of the class against that which was used:

self.assertEqual(PhoneAddressListView.as_view().__name__, response.resolver_match.func.__name__)

Where a base template is used we can simply call the function assertTemplateUsed with the base template name.

In the example below we test the list view with and without records to ensure our “no records here” message is displayed to the user.

When records are returned to our template we have a few ways we can test the results along with the assertContains function we have seen in the previous examples.

  • Check the record count of the object list in the contect view
  • Compare elements
  • Compare the query set. To do this we need to convert the expected query set to a list of strings from the repr function as this is what is contained in the response.
#test_views.py

class TestClassView(TestCase):
    def test_with_no_records(self):
        response = self.client.get(reverse("viewsintroduction:address_list"))

        self.assertEqual(200, response.status_code)
        self.assertEqual(PhoneAddressListView.as_view().__name__, response.resolver_match.func.__name__)

        self.assertTemplateUsed(response, template_name='viewsintroduction/base.html')
        self.assertTemplateUsed(response, template_name='viewsintroduction/phoneaddresslist.html')

        self.assertEqual(0, len(response.context["phoneaddress_list"]))
        self.assertContains(response, '<tr><td colspan="6">There are no addresses</td></tr>')

    def test_with_one_record(self):
        an_address = PhoneAddress(number=1, street_name="A", city="A City")
        an_address.save()

        response = self.client.get(reverse("viewsintroduction:address_list"))

        self.assertEqual(200, response.status_code)
        self.assertEqual(PhoneAddressListView.as_view().__name__, response.resolver_match.func.__name__)

        self.assertTemplateUsed(response, template_name='viewsintroduction/base.html')
        self.assertTemplateUsed(response, template_name='viewsintroduction/phoneaddresslist.html')

        context_addresses = response.context['phoneaddress_list']
        expected_addresses = [repr(r) for r in PhoneAddress.objects.all()]

        self.assertEqual(1, len(context_addresses))
        self.assertEqual(an_address, context_addresses.first())

        self.assertQuerysetEqual(context_addresses, expected_addresses, ordered=False)

        self.assertContains(response, an_address.city)
        self.assertContains(response, an_address.number)
        self.assertContains(response, an_address.street_name)

        self.assertNotContains(response, '<tr><td colspan="6">There are no addresses</td></tr>')

Testing Errors

We now move on to test some of our error views; by comparing the status code.

#test_views.py

class Test404Error(TestCase):
    def test_404_error_is_raised(self):
        response = self.client.get(reverse("helloworld:error_as_404"))
        self.assertEqual(404, response.status_code)

class TestNotAllowed(TestCase):
    def test_not_allowed(self):
        response = self.client.get(reverse("helloworld:not_allowed"))

        self.assertEqual(405, response.status_code)


class TestOnlyPost(TestCase):
    def test_raises_error_on_get(self):
        response = self.client.get(reverse("helloworld:error_if_not_post"))

        self.assertEqual(405, response.status_code)

    def test_all_ok_on_post(self):
        response = self.client.post(reverse("helloworld:error_if_not_post"), {'name': 'Jim'})

        self.assertEqual(200, response.status_code)
        self.assertContains(response, "{0}, this can only be called with a post".format("Jim"))

When we expect a redirect is made we can use the assertRedirects function to validate this. The function takes the status codes of our page load and also the page we direct to. 301 indicates that this is a page which has moved permanently.

#test_views.py

class TestRedirect(TestCase):
    def test_redirect(self):
        response = self.client.post(reverse("viewsintroduction:redirect_view"))

        self.assertTrue(301, response.status_code)
        self.assertRedirects(response, "http://djangoproject.com", 301, 200)  # Status codes Us/Them

Test URLS

When we test URLs we should test two things;

  • The reverse function returns the correct URL string
  • The resolve function returns the correct view function. This does not only include the function and URL name but the app name of the view, the URL namespace and any arguments which will be passed into the view function.
#test_urls.py

from django.core.urlresolvers import reverse, resolve
from django.test import TestCase

from home import views

class TestHomeUrls(TestCase):
    urls = 'home.urls'

    def test_reverse(self):
        self.assertEqual('/', reverse('home'))

    def test_resolve(self):
        resolver = resolve(reverse('home'))

        self.assertEqual(views.home, resolver.func)
        self.assertEqual('home', resolver.url_name)

        self.assertEqual((), resolver.args)
        self.assertEqual({}, resolver.kwargs)
        self.assertEqual(None, resolver.app_name)
        self.assertEqual('', resolver.namespace)

Note: Setting urls = ‘home.urls’ upon the test class above defines the URL routing config we are using. Here we test only an apps config which explains why we reference the URL by their name only without the namespace which we set in the project’s urls.py

When our URL config contains get query parameters we can pass them into the reverse function with the kwargs parameter which is a dictionary.

#test_urls.py

class TestViewsIntroductionUrls(TestCase):
    urls = 'viewsintroduction.urls'

    def test_reverse(self):
        self.assertEqual('/address/1/update/', reverse('address_update', kwargs={'pk': 1}))

Testing Forms

When testing forms we pass the input data as a dictionary into the constructor.

We can access the form fields via the fields property; below we ensure that we have the correct fields and that they contain the right configuration.

We can assert that the form validates as expected with form.is_valid(). Any errors raised will be found within the property errors on the form which is a dictionary of errors. Field errors will be found under a key named after the field name; it returns a list of errors.

#test_forms.py

from django.core.urlresolvers import reverse
from django.forms import DecimalField
from django.test import TestCase

from formsintroduction.forms.bastic_form_example import BasicFormExample
from formsintroduction.forms.class_form_example import ClassBasedForm
from viewsintroduction.models import PhoneAddress


class TestBasicForm(TestCase):
    def test_fields(self):
        form = BasicFormExample({})

        self.assertEqual(2, len(form.fields))
        self.assertTrue("height" in form.fields.keys())
        self.assertTrue("name" in form.fields.keys())
        self.assertIsInstance(form.fields['height'], DecimalField)
        self.assertEqual(2, form.fields['height'].max_value)

    def test_valid_data(self):
        form = BasicFormExample({"name": "Luke", "height": 1.11})

        self.assertTrue(form.is_valid())

    def test_invalid_data(self):
        form = BasicFormExample({"name": "Luke"})

        self.assertFalse(form.is_valid())
        self.assertTrue("name" not in form.errors.keys())
        self.assertTrue("height" in form.errors.keys())

        self.assertEqual(form.errors["height"], ['This field is required.'])

Testing Class Derived Forms

Class based views are tested in pretty much the same way. However when we call save we get an instance of the record created which we can test against.

In the example below we test the form meta data to ensure that the correct fields are set along with the right model class.

#test_forms.py

class TestClassBasedForm(TestCase):
    def test_valid_data(self):
        form = ClassBasedForm({"number": 123, "street_name": "The high street", "city": "Bristol"})

        self.assertTrue(form.is_valid())

        a_record = form.save()

        self.assertEqual(123, a_record.number)
        self.assertEqual("The high street", a_record.street_name)
        self.assertEqual("Bristol", a_record.city)

    def test_invalid_data(self):
        form = ClassBasedForm({})

        self.assertEqual(form.errors["number"],
                         ['We need a number of the house!', 'None of the fields have been set!!!'])

    def test_meta(self):
        self.assertEqual(PhoneAddress, ClassBasedForm.Meta.model)
        self.assertEqual(("city", "street_name", "number"), ClassBasedForm.Meta.fields)

Posting Forms

We use the test client to test posting a form to the server. We pass the form data as a dictionary along with the URL.

#test_forms.py

    def test_post_invalid(self):
        response = self.client.post(reverse("formsintroduction:form_class_create"),
                                    {"city": "Word_Word", "street_name": "street", "number": 1},
                                    follow=True)

        self.assertEqual(200, response.status_code)
        self.assertFormError(response, 'form', 'city', ["Name must be a capitalised single word"])

    def test_post_valid(self):
        response = self.client.post(reverse("formsintroduction:form_class_create"),
                                    {"city": "Foo", "street_name": "Foo", "number": 1},
                                    follow=True)

        self.assertEqual(200, response.status_code)
        self.assertRedirects(response, "http://testserver/viewsintroduction/address/1/", 302)

Testing An Authorised View

Unsuccessful Authentication

The Django authorisation system will automatically redirect a user to the registered login page when they try to access a view which requires authentication. Our first test case assures that we are redirected successfully.

from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.test import TestCase


class TestAuthedView(TestCase):
    def test_redirect_to_loging(self):
        response = self.client.post(reverse("auth:users"))

        self.assertTrue(302, response.status_code)
        self.assertRedirects(response, "auth/login/?next=/auth/users/", 302, 200)

In the above example the 302 and 200 parameters passed into assertRedirects are status codes of the HTTP for the initial page and then the redirect page. 302 is a redirect while 200 is a successful request.

Successful Authentication

The Django test web client comes with the ability to log a user in:

self.client.login(username='username', password='password')

Note: don’t forget that when we are running tests we start with a blank database so you will need to create a login first. The example below creates a new user and activates it in line; this should be moved to a test fixture set-up function.

from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.test import TestCase

class TestAuthedView(TestCase):

    def test_can_login(self):
        user = User.objects.create_user(username='FOOTESTFOO', password='pwd')
        user.is_active = True
        user.save()

        login = self.client.login(username='FOOTESTFOO', password='pwd')

        self.assertTrue(login)

        response = self.client.post(reverse("auth:users"))

        self.assertTrue(200, response.status_code)
        self.assertContains(response, "Registered Users")
        self.assertContains(response, "First Name")
        self.assertContains(response, "Last Name")
        self.assertContains(response, "FOOTESTFOO")

Testing Cookies

We can access cookies from the cookies property of the Django test web client once we have called post or get. It is a dictionary keyed upon the cookie name:

self.client.cookies
self.client.cookies["hits"]

The cookie is returned as a http.cookies.Morsel. We can access the cookie value with the value property. The class responds as a dictionary for other data, in the following example we ensure the expiry has been set correctly with the max-age key.

from django.core.urlresolvers import reverse
from django.test import TestCase

class TestSessions(TestCase):
    def test_cookie(self):
        response = self.client.post(reverse("sessionsandcookies:cookies"))

        self.assertTrue(200, response.status_code)

        self.assertTrue("hits" in self.client.cookies)
        self.assertEqual('1', self.client.cookies["hits"].value)
        self.assertEqual(30, self.client.cookies["hits"]["max-age"])

Testing Session Data

We can access session data upon the Django test web client after we have called post or get. It returns a SessionBase which responds as a dictionary to retrieve the session data from their key names. All functionality of the SessionBase class is available; below we call get_expiry_age to ensure our session expiry policy has worked as expected:

from django.core.urlresolvers import reverse
from django.test import TestCase


class TestSessions(TestCase): 

    def test_sessions(self):
        response = self.client.post(reverse("sessionsandcookies:sessions"))

        self.assertTrue(200, response.status_code)

        self.assertTrue("has_visited" in self.client.session)
        self.assertEqual('True', self.client.session["has_visited"])
        self.assertEqual(10, self.client.session.get_expiry_age())

Additional Assertions

As mentioned previously, Django provides some additional assert functions to the standard Python unittest. class We have covered many of them above though there are two others which I think will be quite useful.

The assertFieldOutput assertion allows us to validate against good and bad field input data. I tried to get this to work against the defined fields or a model or form however it seems to only work with the class type.

The assertNumQueries assertion will ensure that the database has been touched the correct number of times.

#test_additionl_assertions.py

from django.forms import EmailField, CharField
from django.test import TestCase

from viewsintroduction.models import PhoneAddress


class TestAdditionalAssertions(TestCase):
    def test_assertFieldOutput(self):
        self.assertFieldOutput(EmailField, {'a@a.com': 'a@a.com'}, {'aaa': ['Enter a valid email address.']})

        self.assertFieldOutput(CharField, {'aa': 'aa'},
                               {'word': ['Ensure this value has at most 3 characters (it has 4).'],
                                'a': ['Ensure this value has at least 2 characters (it has 1).']}, [],
                               {"max_length": 3, "min_length": 2})

    def test_assert_num_queries(self):
        with self.assertNumQueries(1):
            PhoneAddress.objects.create(city="Foo", street_name="Foo", number=1)

        self.assertNumQueries(1, lambda: PhoneAddress.objects.create(city="Foo", street_name="Foo", number=1))

References

Python: Unit Testing

Unit Testing

This is part of my Python & Django Series which can be found here including information on how to download all the source code.

The ability for software to test and diagnose itself is a powerful feature.

A Simple Example

Lets take a simple function which adds two numbers together.

Code:

def add_two_numbers(a, b):
    """
    A simple method to test
    """

    return a + b

We can create a test to ensure that add_two_numbers works as expected by comparing the result of a call to the function with our expected result.

Code:

from unittest import TestCase, main

class MyTestClass(TestCase):
    """
    A simple unit test example
    """

    def test_add_two_numbers(self):
        self.assertEqual(add_two_numbers(1, 2), 3)

A test class inherits from unittest.TestCase. All functions which are prefixed with test_ will be determined as tests which are required to be run.

Above we call add_two_numbers with parameters 1 and 2. We then use the returned value as a parameter to the assertEqual function along with our expected result of 3.

If the assertion validates as expected the assertion returns allowing control to carry on, otherwise an error is raised and the test is marked as failed.

A test function can have any number of assertions called.

We can run our test function with the main function from unittest.

Code:

if __name__ == '__main__':
    main()

Output:

.py::MyTestClass true
Testing started at 12:42 …

Process finished with exit code 0

If a bug appeared in our code we would see a result similar to the following.

Output:

.py::MyTestClass true
Testing started at 12:44 …

Process finished with exit code 0

Failure
Traceback (most recent call last):
File “/data/data/Dropbox/Development/SandBox/Git/ThePythonPit/PythonSandBox/Testing/unittest_examples/simple_example.py”, line 26, in test_add_two_numbers
self.assertEqual(add_two_numbers(1, 2), 4)
AssertionError: 3 != 4

Assertions

In the previous section we saw the assertEqual assertion. The unittest module provides many assertion functions to cater for a range of possible test criteria.

Equals Assertions

Equality assertion can be made with the assertEqual and inequality assertion can be made with the assertNotEqual function. Both functions take two parameters; the result and the expected result.

Code:

self.assertEqual(1, 1)
self.assertNotEqual(1, 2)

For numerical results the assertAlmostEqual and assertNotAlmostEqual functions allow equality assertion within a tolerance of error. The tolerance is passed in as the third parameter and represents the number of decimal places to be used when determining equality.

The call to assertAlmostEqual takes 1.1 and 1.11 with a tolerance of 1 d.p. This would fail if we used assertEqual but as 1.11 becomes 1.1 when rounding to 1 d.p and therefore the assertion passes.

Code:

self.assertAlmostEqual(1.1, 1.11, 1)  # 3rd argument is the precession
self.assertNotAlmostEqual(1.1, 1.11, 2)  # 3rd argument is the precession

The assertEqual function can take most types. All of the following asserts for lists, tuples, sets, dictionaries and multi-line strings pass assertion.

When being called for collections, the test requires both collections to be of the same type, contain the same number of elements and the elements at the same ordinal position to be equal.

Code:

self.assertEqual([1, 2, 3], [1, 2, 3]) # list
self.assertEqual((1, 2, 3), (1, 2, 3)) # tuple
self.assertEqual({1, 2, 3}, {1, 2, 3}) # set
self.assertEqual({'a': 1}, {'a': 1})  # dictionary
self.assertEqual("onentwo", "onentwo") # multi-line string

Unittest does provide specific assert equal functions for each type though these are implicitly called via the assertEquals function. You should favour using the assertEquals functions.

Code:

self.assertListEqual([1, 2, 3], [1, 2, 3])
self.assertTupleEqual((1, 2, 3), (1, 2, 3))
self.assertSetEqual({1, 2, 3}, {1, 2, 3})
self.assertDictEqual({'a': 1}, {'a': 1})
self.assertMultiLineEqual("onentwo", "onentwo")

Code:

The assertEqual function works upon equality; as such an integer of value 1 and a float of value 1.0 will pass an assertion check together.

self.assertEqual(1.0, 1)

Booleans Assertions

The assertFalse and assertTrue functions for ensuring that a boolean type is either false or true respectively.

Code:

self.assertFalse(False)
self.assertTrue(True)

Collections Assertions

A number of assertions specifically for collections are provided.

We have already seen the assertEqual function which determines if two parameters are equal.

When working with collections this performs the following checks

  • The collection types are equal
  • The collections contain the same number of elements
  • Each element at the same ordinal position equals that in the other collection.

The elements can be of another type as long as their values are equal. In the example below one list contains integers and the other floats but the assertion passes as the elements are equal.

Code:

self.assertEqual([1.0, 2.0, 3.0], [1, 2, 3]) 

The assertSequenceEqual function works the same as assertEqual though it will not fail if the collections are of different types. Below we ensure that the contents of a list and a tuple are equal.

Code:

self.assertSequenceEqual((1, 2, 3), [1, 2, 3])  # Checks only the sequence

The assertIn and assetNotIn funcitons allows checks to see if an element is contained or not contained within a collection. The check is based upon equality.

Here we check that 1 is in 1,2,3 and that 4 is not in 1, 2, 3.

Code:

self.assertIn(1, (1, 2, 3))
self.assertNotIn(4, (1, 2, 3))

The assertCountEqual function has to be a contender for the worst named function in history. This function ensures that two collections contain exactly the same elements though their order is not important.

Code:

self.assertCountEqual((1, 2, 3), (3, 2, 1))  # Badly named. This checked elements and not their order

Comparison Assertions

Python provides the comparison checks in the form of less than, less than or equal to, greater than and greater than or equal to.

Code:

self.assertLess(1, 10)
self.assertLessEqual(1, 1)
self.assertGreater(10, 1)
self.assertGreaterEqual(1, 1)

Identity Assertions

Identity ensures that two parameters point to the same object instance.

In Python each type instance is assigned it’s own object id upon creation. More information can be found here .

The assertIs and assertIsNot can ensure that two objects are and are not the same instance respectively.

Code:

self.assertIs(1, 1)
self.assertIsNot(1, 2)

For parameters which are not referencing any data or have not been initialised they will point to the None type. Here we can check to see if a parameter is pointing to or not pointing to None with the assertIsNone and assertIsNotNone functions.

Code:

self.assertIsNone(None)
self.assertIsNotNone(1)

The assertIsInstance and assertNotIsInstance functions can be used to see if a parameter holds a specific type. Here we pass a parameter holding an instance of a type along with the class name of the type that we want to insure it references or does not reference.

Code:

self.assertIsInstance((), tuple)
self.assertNotIsInstance((), set)

Regular Expressions Assertions

Code:

We can use regular expressions to ensure the format of a string is as expected with the assertRegex and assertNotRegex functions

self.assertRegex('Luke', "^[a-zA-Z]{3,4}$")
self.assertNotRegex('Lukey', "^[a-zA-Z]{3,4}$")

Exceptions Assertions

Code should throw exceptions when we want it to or when it is called incorrectly. We can use the assertRaises function to assert that not only an exception is raised but it is of a certain type.

Below we ensure that a ZeroDivisionError error is raised.

Code:

with self.assertRaises(ZeroDivisionError) as ex:
    result = 1 / 0

self.assertEqual(str(ex.exception), "division by zero")

In the above example we assign the raised exception to a variable ex, we can then run assertions upon the exception to make sure it is as expected. We check the string representation of the object is as expected. The latter check can be enforced with the assertRaisesRegex function.

Code:

with self.assertRaisesRegex(ZeroDivisionError, "^division by [a-zA-z]{4}$"):
    result = 1 / 0

We can also annotate a test with the @expectedFailure attribute. Here the test will fail if an error is not raised.

Output:

  @expectedFailure
    def test_expectedFailure(self):
        self.fail("This is an expected failure")

Warnings Assertions

Python provides the same functions for warnings as it does for exceptions; they work in exactly the same way

Code:

with self.assertWarns(DeprecationWarning) as wn:
    warn("deprecated", DeprecationWarning)

self.assertEqual(str(wn.warning), "deprecated")

with self.assertWarnsRegex(DeprecationWarning, "^deprecate[a-z]$"):
    warn("deprecated", DeprecationWarning)

Assertions Messages

Each assertion can optionally take a string to be used as an error message when the test fails.

Code:

self.assertFalse(False, "False is not false!")

Would report as the following:

Output:

AssertionError: True is not false : False is not false

The following would be reported if the error message had not been provided.

Output:

AssertionError: True is not false

Failing Tests

We can fail a test in code with the fail method.

Code:

self.fail("Fail!!!")

Test Fixture

If a test class has a function called setUp, it will be run before every test function within it. If an error is raised within the setUp function then no test functions will be run.

If a test class has a function called tearDown, it will be run after every test function within it. This function will always be run after each test function regardless if the test passes or fails.

Code:

from unittest import TestCase


class TestFixtureExample(TestCase):

    def setUp(self):
        # Set up / initialise before a test
        # If this fails then no tests will be run
        print("In the setUp")

    def tearDown(self):
        # Destroy any resources required during the test
        # Will always be run if setUp runs regardless of tests successes
        print("In the tearDown")

    def test_fixture_one(self):
        self.assertTrue(True)

    def test_fixture_two(self):
        self.assertTrue(True)

    def test_fixture_three(self):
        self.assertTrue(True)

Output:

.py::TestFixtureExample true
Testing started at 14:17 …
In the setUp
In the tearDown
In the setUp
In the tearDown
In the setUp
In the tearDown

Test Suite

The TestSuite class can be used to register tests which can then be run with the TextTestRunner.

The addTest can be used to add an individual test method into a TestSuite instance.

The TestLoader().loadTestsFromTestCase() can be used to create a TestSuite with all test functions of a test class.

The TextTestRunner().run() function can then run all TestSuites passed in.

**Code:

from unittest import TestSuite, TextTestRunner, TestLoader

# Test Suite
def my_test_suite():
    suite_one= TestSuite()
    suite_one.addTest(MyTestClass('test_add_two_numbers')) # Adds MyTestClass.test_add_two_numbers()

    suite_two = TestLoader().loadTestsFromTestCase(TestAssertsExample)

    return TestSuite([suite_one, suite_two])

# Run the test suite
if __name__ == '__main__':
    TextTestRunner().run(my_test_suite())

Skipping Tests

Test functions can be annotated with specific unittest attributes.

Skip can be used to stop a test from running. This can also be done in code with the SkipTest function

SkipIf can be used to stop a test from running if a boolean statement evaluates to true.

SkipUnless can be used to stop a test from running unless a boolean statement evaluates to true.

Code:

class TestAttributes(TestCase):

    @skip("Test is not run")
    def test_skip(self):
        self.fail("This should not be run")

    @skipIf(True, "This is not run")
    def test_skipIf(self):
        self.fail("This should not be run")

    @skipUnless(False, "This is not run")
    def test_skipUnless(self):
        self.fail("This should not be run")

    def test_skipTest(self):
        SkipTest("This should not be run")

Rhino Mocks Cheat Sheet

A cheat sheet for Rhino Mocks v 3.6 in C#

All source code can be found on GitHub here.

This post is part of my cheat sheet series.

You can see my HowTo here.

// *** STUB ****
// A stub is an object which is required to pass the SUT.

mocks.CreateStub<T>();

// The simple stub
var sut = MockRepository.GenerateStub<ISimpleModel>();
sut.Stub(x => x.Do()).Return(1);

// Stub Property
sut.Stub(x => x.AReadonlyPropery).Return(1); // Reaonly ( only get )
sut.AProperty = 2; // Stub Get

// Repeat
mock.Stub(x => x.Do()).Return(1).Repeat.Once();
mock.Stub(x => x.Do()).Return(2).Repeat.Twice();
mock.Stub(x => x.Do()).Return(3).Repeat.Times(3);

// Events
mock.Raise(x => x.Load += null, this, EventArgs.Empty);

// **** Arguments Conditional ****
// These can be used for Stub, Expect, AssertWasCalled and AssertWasNotCalled

// Ignore Arguments Conditional
sut.Stub(x => x.Do(Arg<int>.Is.Equal(1))).IgnoreArguments().Return(1);

// Is Conditionals
sut.Stub(x => x.Do(Arg<int>.Is.Anything)).Return(1);
sut.Stub(x => x.Do(Arg<int>.Is.Equal(1))).Return(1);
sut.Stub(x => x.Do(Arg<int>.Is.NotEqual(1))).Return(10);
sut.Stub(x => x.DoIFoo(Arg<Foo>.Is.Null)).Return(1);
sut.Stub(x => x.DoIFoo(Arg<Foo>.Is.NotNull)).Return(2);
sut.Stub(x => x.Do(Arg<int>.Is.LessThanOrEqual(10))).Return(1);
sut.Stub(x => x.Do(Arg<int>.Is.GreaterThan(10))).Return(2);
sut.Stub(x => x.DoIFoo(Arg<Foo>.Is.Same(foo))).Return(1);
sut.Stub(x => x.DoIFoo(Arg<Foo>.Is.NotSame(foo))).Return(2);
sut.Stub(x => x.DoIFoo(Arg<Foo>.Is.TypeOf)).Return(1);

// Matches Conditional
sut.Stub(x => x.Do(Arg<int>.Matches(y => y > 5))).Return(1);

// List Conditionals
sut.Stub(x => x.Do(Arg<List<int>>.List.Count(RIS.Equal(0)))).Return(0);
sut.Stub(x => x.Do(Arg<List<int>>.List.Element(0, RIS.Equal(1)))).Return(1);
sut.Stub(x => x.Do(Arg<List<int>>.List.Equal(new int[] { 4, 5, 6 }))).Return(2);
sut.Stub(x => x.Do(Arg<List<int>>.List.IsIn(1))).Return(1);
sut.Stub(x => x.Do(Arg<int>.List.OneOf(new int[] { 4, 5, 6 }))).Return(2);

// ByRef and Out parameters
sut.Stub(x => x.Do(Arg<int>.Is.Equal(1), ref Arg<int>.Ref(RIS.Equal(0), 10).Dummy)).Return(1);
sut.Stub(x => x.Do(Arg<int>.Is.Equal(1), Arg<string>.Is.Equal("Hello"), out Arg<int>.Out(10).Dummy)).Return(1);

// **** DYNAMIC MOCKS ***
// A mock is an object which we can set expectations on and will assert that those expectations have been
// Dynamic Mock provides easier syntax and does not require stubbing/expecting all methods

mocks.CreateMock<T>();

// Assert Was Called
mock.AssertWasCalled(p => p.Add(Arg<AnotherModel>.Is.Anything));
mock.AssertWasCalled(p => p.Add(Arg<AnotherModel>.Is.NotNull));
mock.AssertWasCalled(p => p.Add(Arg<AnotherModel>.Is.Equal(theModel)));
mock.AssertWasNotCalled(p => p.Add(Arg<AnotherModel>.Is.Null));
mock.AssertWasCalled(x => x.AReadonlyPropery);
mock.AssertWasCalled(x => x.AProperty);
mock.AssertWasCalled(x => x.AProperty = 9);
mock.AssertWasCalled(x => x.EventHandler += Arg<AnEvent>.Is.Anything); // Event was registered

// Expect & Verify
mock.Expect(p => p.Add(Arg<AnotherModel>.Is.Anything));
mock.Expect(p => p.Add(Arg<AnotherModel>.Is.NotNull));
mock.Expect(p => p.Add(Arg<AnotherModel>.Is.Equal(theModel)));
mock.Expect(x => x.AReadonlyPropery).Return(9);
mock.Expect(x => x.AProperty).Return(9);
mock.Expect(x => x.AProperty).SetPropertyAndIgnoreArgument();
mock.Expect(x => x.AProperty).SetPropertyWithArgument(11);

mock.VerifyAllExpectations()

HowTo – Rhino Mocks

Source Code

All source code can be found on GitHub here.

My cheat sheet in generics can be found here while some code examples in generics can be found in my post here.

This is part of my HowTo in .NET seriies. An overview can be seen here,

Intro

Rhino Mocks is a mocking framework for .Net. In essence a mocking framework allows you to stub a complex expensive resource such as a database or webserver. However mocking frameworks can allow you to completely isolate a SUT during testing. Mocking frameworks also allow the creation of expectations of methods calls between a mock and the SUT; the unit test will then fail if the expected method calls were not made or made but with the wrong parameter values. Testing can then be behavioural as well as state based. This how to looks at the syntax for version 3.6 and above which does not require the record and playback syntax.

Stubs

Stub A Method

The simplest explanation of when to stub is when a resource such as a database or web service is encountered; something which is expensive to consume when unit testing. Providing a stub for a class which consumes a database can allow us to substitute this at run time making unit testing quicker and easier but also it can allow us to very easily create complex unit tests with a wealth of data which is very easy to set up without having to create test data within a database. The following simple example mocks an interface called method called Do() on interface ISimpleModel which takes no parameters and when called returns 1 as an integer. MockRepository is used to create a fake or stubbed instance of ISimpleModel, it is upon this that we are required to call our functions. If we were stubbing a class and not an interface then one thing to note is that you would not get any functionality of the class. You can not mix and match functionality of the class and stubbed functions.

var sut = MockRepository.GenerateStub();
sut.Stub(x => x.Do()).Return(1);

Stub A Property

Get functions of properties can be stubbed. The following syntax allows stubbing of a readonly property (get only).

var sut = MockRepository.GenerateStub();
sut.Stub(x => x.AReadonlyPropery).Return(1);

The following syntax allows stubbing of a get method of a property.

var sut = MockRepository.GenerateStub();
sut.AProperty = 2;  // Stubs a Get_AProperty as 2
Assert.That(sut.AProperty.Equals(2));

It is not required to stub set methods. Any methods can be consumed automatically without stubbing them, only if we need a return value other than default(T) for value types and null for reference types do you need to stub the method. Set methods return void so stubbing this is not required.

Arguments

If our stubbed methods takes arguments then we need to provide for them.

Ignoring Arguments

In the simplest form we might not care what values the parameters take and can simply state that they can be anything. Allow the argument to be Anything.

sut.Stub(x => x.Do(Arg.Is.Anything)).Return(1);

Call IgnoreArguments() will make any constrained arguments parameters have their constraints ignored.

sut.Stub(x => x.Do(Arg.Is.Equal(1))).IgnoreArguments().Return(1);

Constrained Arguments

Constrained arguments allow us to return different data based upon the method parameter values which it is called upon. When all parameters are matched the return is made. If multiple stubs are matched then the first registered stub instance is used to determine the return value.

Is

Is constants provide predicates for literal and reference types. The following is a quick run through of the Is argument constraints. Equal and NotEqual for equality based comparison.

sut.Stub(x => x.Do(Arg.Is.Equal(1))).Return(1);
sut.Stub(x => x.Do(Arg.Is.NotEqual(1))).Return(10);

Comparison between two literals.

sut.Stub(x => x.Do(Arg.Is.GreaterThanOrEqual(10))).Return(1);
sut.Stub(x => x.Do(Arg.Is.GreaterThan(10))).Return(2);
sut.Stub(x => x.Do(Arg.Is.LessThanOrEqual(10))).Return(1);
sut.Stub(x => x.Do(Arg.Is.LessThan(10))).Return(2);

Comparison to null with Is.Null and Is.NotNull.

sut.Stub(x => x.DoIFoo(Arg.Is.Null)).Return(1);
sut.Stub(x => x.DoIFoo(Arg.Is.NotNull)).Return(2);

IsTypeOf to determine if the type is of the required type. In this example Moo and Foo both implement IFoo Whig is the parameter type. Based upon whether Foo or Moo is passed in we can alter our return value.

sut.Stub(x => x.DoIFoo(Arg.Is.TypeOf)).Return(1);
sut.Stub(x => x.DoIFoo(Arg.Is.TypeOf)).Return(2);

Identity comparison with Same and NotSame functions.

sut.Stub(x => x.DoIFoo(Arg.Is.Same(foo))).Return(1);
sut.Stub(x => x.DoIFoo(Arg.Is.NotSame(foo))).Return(2);

List

The List functions allow us to provide predicates upon elements which implement IEnumerable and IEnumerable. I aliased RIS as ‘using RIS = Rhino.Mocks.Constraints.Is;’ to make a distinction between NUnit and Rhino Mocks Is. The simplest form of List is the count which allows us to compare the element count against an Is constraint. Any of the Is constraints which predicate numbers can be used; Equal IsLessThan etc. Below we stub Do(IList t) twice once for when the parameter contains no elements and another for one element.

sut.Stub(x => x.Do(Arg>.List.Count(RIS.Equal(0)))).Return(0);
sut.Stub(x => x.Do(Arg>.List.Count(RIS.Equal(1)))).Return(1);

Element allows comparison of element at a position to an Is constraint. The first parameter is the element position and the second parameter is an Is constraint.

sut.Stub(x => x.Do(Arg>.List.Element(0, RIS.Equal(1)))).Return(1);
sut.Stub(x => x.Do(Arg>.List.Element(1, RIS.GreaterThanOrEqual(2)))).Return(2);

Equal allows comparison of a collection to another.

sut.Stub(x => x.Do(Arg>.List.Equal(new int[] { 1, 2, 3 }))).Return(1);
sut.Stub(x => x.Do(Arg>.List.Equal(new int[] { 4, 5, 6 }))).Return(2);

IsIn allow predicating if an element is contained within the collection.

sut.Stub(x => x.Do(Arg>.List.IsIn(1))).Return(1);
sut.Stub(x => x.Do(Arg>.List.IsIn(4))).Return(2);

OneOf allows predicate for any elements in both collections;

if one or more elements are found in both collections then the predicate is deemed as passed.

sut.Stub(x => x.Do(Arg.List.OneOf(new int[] { 1, 2, 3 }))).Return(1);
sut.Stub(x => x.Do(Arg.List.OneOf(new int[] { 4, 5, 6 }))).Return(2);

Matches

When the predicate is more complex or the built in functionality is not sufficient the Matches function provides more scope by taking a Predicate delegate; if you can write it you can predicate it!

sut.Stub(x => x.Do(Arg.Matches(y => y > 5))).Return(1);

Repeat

It is possible to set up the stubbing for x number of repetition; thus we can return different results dependant upon the call iteration. The methods Once(), Twice() and Times(int x) are provided.

mock = MockRepository.GenerateStub();
mock.Stub(x => x.Do()).Return(1).Repeat.Once();
mock.Stub(x => x.Do()).Return(2).Repeat.Twice();
mock.Stub(x => x.Do()).Return(3).Repeat.Times(3);

ByRef and Out Parameters

By Ref parameters can be implemented by utilising the ref keyword along with the Ref and Dummy methods.

sut.Stub(x => x.Do(Arg.Is.Equal(1), ref Arg.Ref(RIS.Equal(0), 10).Dummy)).Return(1);

Out parameters can be implemented by utilising the out keyword along with the Out and Dummy methods.

sut.Stub(x => x.Do(Arg.Is.Equal(1), Arg.Is.Equal("Hello"), out Arg.Out(10).Dummy)).Return(1);

By Literal

Though highly discouraged stubbing by literals can also be utilised.

sut.Stub(x => x.Do(1).Return(1);
sut.Stub(x => x.Do("Foo").Return(2);

Mocks

A Mock is similar to a Stub, in fact it contains all the functionality mentioned above however mocks can record required behaviour and fail a unit test if the required behaviour is not called upon by the SUT. For example If we have a class A which requires to call two methods on class B with certain parameters we can ensure that all of this behaviour is called or fail the unit test. We can either place expectation calls on the mock prior to the unit test act or we can make assertions of function calls during the assert.

Assert Was Called

AssertWasCalled provides a way asserting that a function call with required parameters was called. If a return value is required then this method can not be used.

Argument Constraints

All argument constraints mentioned previously in the stubs section can and should be utilised along with the AssertWasCalled function. If the function was not called with the parameter constraints then an exception is thrown and as such the unit test fails. The following example shows how to ensure that a function is called ignoring the parameter values.

[Test]
public void WhenArgumentsIsAnything()
{
	IModelRepository mock = MockRepository.GenerateMock();

 	var modelCreator = new ModelCreator(mock);

 	var theModel = new AnotherModel() { FieldA = 1, FieldB = "Test" };

 	modelCreator.CreateModel(theModel);

 	mock.AssertWasCalled(p => p.Add(Arg.Is.Anything));
 }

The following example shows how to ensure that a function is called ensuring that the parameter is not null.


[Test]
public void WhenArgumentsIsNotNull()
{
	IModelRepository mock = MockRepository.GenerateMock();

 	var modelCreator = new ModelCreator(mock);

 	var theModel = new AnotherModel() { FieldA = 1, FieldB = "Test" };

 	modelCreator.CreateModel(theModel);

 	mock.AssertWasCalled(p => p.Add(Arg.Is.NotNull));

 }

The following example shows how to ensure that a function is called ensuring that the parameter is equal to a reference type.

[Test]
public void WhenArgumentsIsEqual()
{
	IModelRepository mock = MockRepository.GenerateMock();

 	var modelCreator = new ModelCreator(mock);

 	var theModel = new AnotherModel() { FieldA = 1, FieldB = "Test" };

 	modelCreator.CreateModel(theModel);

 	mock.AssertWasCalled(p => p.Add(Arg.Is.Equal(theModel)));

 }

Assert Was Not Called

AssertWasNotCalled works exactly the same as AssertWasCalled however it throws an exception and fails the unit test if the method with argument constraints was called.

[Test]
public void WhenNotCalled()
{
	IModelRepository mock = MockRepository.GenerateMock();

 	var modelCreator = new ModelCreator(mock);

 	var theModel = new AnotherModel() { FieldA = 1, FieldB = "Test" };

 	modelCreator.CreateModel(theModel);

 	mock.AssertWasNotCalled(p => p.Add(Arg.Is.Null));

}

Properties

The syntax for asserting that a readonly property or get method of a property is exactly the same as each other (unlike when we look at Expect and Verify!).


[Test] public void WhenReadonlyProperty()
{
	var sut = MockRepository.GenerateMock();

 	var foo = sut.AReadonlyPropery;

 	Assert.That(foo.Equals(0));

 	sut.AssertWasCalled(x => x.AReadonlyPropery);

 }

 

[Test]
public void WhenProperyGetCalled()
{
	var sut = MockRepository.GenerateMock();

 	Assert.That(sut.AProperty, Is.EqualTo(0));

 	sut.AssertWasCalled(x => x.AProperty);

 }

Asserting that a set method of a property is called with a literal value.

[Test]
public void WhenProperySetCalled()
{
	var sut = MockRepository.GenerateMock();

 	sut.AProperty = 9;

 	Assert.That(sut.AProperty, Is.EqualTo(0));

 	sut.AssertWasCalled(x => x.AProperty = 9);

 }

Expect and Verify

Expect and Verify has two advantages over the AssertWasCalled style syntax. Firstly it allows stubbing inline with the Expect function call and secondly it allows setting the expectations as part of the assign stage along with other Stub functionality which might be required. The Expect() method allows all argument constraints as shown in the stub section above and works exactly the same as stub however if when VerifyAllExpectations is called as part of the assert section of the unit test any configured expects which have not been called or called with the required parameters, an exception is thrown and the unit test fails. Unlike AssertWasCalled syntax no equivalent functionality for AssertWasNotCalled is provided.

Argument Constraints

All argument constraints are available. Below are a few examples The following example shows how to ensure that a function when it does not matter what parameters are used.

[Test]
public void WhenArgumentIsAnything()
{
	IModelRepository mock = MockRepository.GenerateMock();

	var modelCreator = new ModelCreator(mock);

 	var theModel = new AnotherModel() { FieldA = 1, FieldB = "Test" };

 	mock.Expect(p => p.Add(Arg.Is.Anything));

 	modelCreator.CreateModel(theModel);

 	mock.VerifyAllExpectations();

 }

The following example shows how to ensure that a function only requires that the function is called without a reference type being null.

[Test]
public void WhenArgumentIsNotNull()
{
	IModelRepository mock = MockRepository.GenerateMock();

 	var modelCreator = new ModelCreator(mock);

 	var theModel = new AnotherModel() { FieldA = 1, FieldB = "Test" };

 	mock.Expect(p => p.Add(Arg.Is.NotNull));

 	modelCreator.CreateModel(theModel);

 	mock.VerifyAllExpectations();

 }

The following example shows how to ensure that a function with equality check for a reference type.

[Test] public void WhenArgumentIsEqualTo()
{
	IModelRepository mock = MockRepository.GenerateMock();

 	var modelCreator = new ModelCreator(mock);

 	var theModel = new AnotherModel() { FieldA = 1, FieldB = "Test" };

 	mock.Expect(p => p.Add(Arg.Is.Equal(theModel)));

 	modelCreator.CreateModel(theModel);

 	mock.VerifyAllExpectations();

 }

The following example shows how to ensure that a function is called with a number type parameter equal to 9.

[Test]
public void WhenReadonlyPropertyCalled()
{
	var sut = MockRepository.GenerateMock();

	sut.Expect(x => x.AReadonlyPropery).Return(9);

 	Assert.That(sut.AReadonlyPropery, Is.EqualTo(9));

 	sut.VerifyAllExpectations();
}

Properties

The first example shows how to place an expectation on a get method of a property.

[Test]
public void WhenPropertyGetCalled()
{
	var sut = MockRepository.GenerateMock();

 	sut.Expect(x => x.AProperty).Return(9);

 	Assert.That(sut.AProperty, Is.EqualTo(9));

 	sut.VerifyAllExpectations();

 }

This example shows how to place an expectation on a set method while ignoring the parameter value.

[Test]
public void WhenPropertySetCalledWithIgnoreArguments()
{
	var sut = MockRepository.GenerateMock();

 	sut.Expect(x => x.AProperty).SetPropertyAndIgnoreArgument();

 	sut.AProperty = 1;

 	sut.VerifyAllExpectations();

 }

This example shows how to place an expectation on a set method with an explicit value.

[Test]
public void WhenPropertySetCalledWithArguments()
{
	var sut = MockRepository.GenerateMock();

 	sut.Expect(x => x.AProperty).SetPropertyWithArgument(11);

 	sut.AProperty = 11;

 	sut.VerifyAllExpectations();
 }

Day 13 NUnit

 

The Source Code

This post is part of my Dot Net On Linux posts. All the source code is available on GitHub here

Intro

NUnit is a unit test framework which integrates nicely into monodevelop.

For setting up and inital how to see my inital post here.

I have a HowTo on NUnit which can be found here.

You can also find my cheat sheet here.

This post is just a placeholder for my .NET on Linux series.

 

HowTo – XUnit

Source Code

All source code can be found on GitHub here.

My cheat sheet for XUnit can be found here.

This is part of my HowTo in .NET seriies. An overview can be seen here,

Intro

XUnit is a unit test framework. The features appear to be somewhat lacking when compared to NUnit but it aims to only provide the functionality which is intended to be common and supports good unit testing structure. For example in NUnit [TestFixureSetUp] does not promote test isolation so an equivalent functionality have been purposefully omitted.

There is also no fluent assertions but there are 3rd party dlls available instead.

XUnit also differs from other unit test frameworks by not relying on attributes as much. For example constructor and dispose methods are used for test set up and tear down functions.

Basic Test Creation

The following examples consists of a simple class with a parameter-less constructor that implements IDisposable. There is also one public method decorated with [Fact] attribute.

The constructor is our test setup function.

The Dispose is our test tear down function.

The Method with [Fact] is our test.

public class AccountTests : IDisposable		// Allows Test TearDown via Dispose()
{

	public AccountTests()
	{
	}

	[Fact]
	public void SimpleExample()
	{
	    Assert.True(true);
	}

	public void Dispose()
	{
	}

}

IUseFixture

Common set up code can be refactored out to a helper class and injected in automatically via the IUseFixture interface and implementing its only method SetFixture(T t)

public class AccountTests : IUseFixture<AccountTests.TestSetUpClass> // Allows Test Set Up via injected class
{

	public void SetFixture(TestSetUpClass setupClass)
	{
	    // Can inject a class to provide any set up here.
	    setupClass.AnyFunction();
	}

	public class TestSetUpClass
	{
	    public void AnyFunction()
	    {
		// Set up code goes in here.
	    }
	}
}

InLineData allows multiple traversals of the same test with different parameters. It can take any number and types of parameters though they have to match the method signature which they are decorating.

The following example will result in 3 tests with the parameters, {1, true}, {2, true}, {3, false}

[Theory,
InlineData(1, true),
InlineData(2, true),
InlineData(3, false)]
public void InlineDataExample(int number, bool expectedResult)
{
    Console.WriteLine("AccountTests.DemoSimpleTestWithInLineData(int {0}, bool {1})", number, expectedResult);
    Assert.Equal(number < 3, expectedResult);
}

PropertyData

Similar to InLineData though the test data can be injected in from a property.

The property should returns IEnumerable where the object array contains the same matching parameters to the method signature of the test method.

public static IEnumerable<object[]> SamplePropertyDataProperty
{
    get
    {
        yield return new object[] { 1, true };
        yield return new object[] { 2, true };
        yield return new object[] { 3, false };
    }
}

[Theory]
[PropertyData("SamplePropertyDataProperty")]
public void PropertyDataExample(int number, bool expectedResult)
{
    Console.WriteLine("AccountTests.PropertyDataExample(int {0}, bool {1})", number, expectedResult);
    Assert.Equal(number < 3, expectedResult);
}

Assertions

Assertions are conditions which will cause our test to fail or pass. Each assertion will cause an exception to be thrown when the condition is not met. The test runner will handle the exception, fail the test and start running the next test. If a unit test runs to the end without an exception then the test is considered as passed.

If an exception is thrown which is not part of an Assert but inside the test it is considered as failed.

If an exception is thrown inside the code under test which is not expected the test is considered as failed. If an exception being thrown is the expected result then special exception asserts must be used.

Contains asserts that something is found within a reference type which implements IEnumerable or IEnumerable ( collection ).

A string is a collection of chars and implements IEnumerable.

Assert.Contains("n", "FNZ", StringComparison.CurrentCultureIgnoreCase);
Assert.Contains("a", new List<String> { "A", "B" }, StringComparer.CurrentCultureIgnoreCase);

Does not contain ensures that something is not within a collection or IEnumerable.

Assert.DoesNotContain("n", "FNZ", StringComparison.CurrentCulture);
Assert.DoesNotContain("a", new List<String> { "A", "B" }, StringComparer.CurrentCulture);

Empty and not empty ensures that a collection of IEnumerable is or is not empty.

Assert.Empty(new List<String>());
Assert.NotEmpty(new List<String> { "A", "B" });

Equality checks with the ability to pass in a IComparer and also a precision for number of dp; equality up to x decimal places.

The ability to compare all elements within a collection against another collection is also catered for with an overloaded method for IComparer

Assert.Equal(1, 1);
Assert.Equal(1.13, 1.12, 1); // Precsions Num DP
Assert.Equal(new List<String> { "A", "B" }, new List<String> { "a", "b" }, StringComparer.CurrentCultureIgnoreCase);

Assert.NotEqual(1, 2);
Assert.NotEqual(new List<String> { "A", "B" }, new List<String> { "a", "b" }, StringComparer.CurrentCulture);

Conditions for asserting boolean logic, is and is not null.

Assert.False(false);
Assert.NotNull(false);
Assert.Null(null);
Assert.True(true);

Range comparer ensures that a value is greater than and equal to a number and also less than or equal to another.

Assert.InRange(1, 0, 10);
Assert.NotInRange(-1, 0, 10);

Assertions to ensure a memory allocation is of a required type.

Assert.IsType(Type.GetType("System.Int32"), 1);
Assert.IsNotType(Type.GetType("System.Double"), 1);

Identity ( is the same object ) assertions are performed with Same and NotSame functions.

var foo = new object();
var moo = new object();
Assert.Same(foo, foo);
Assert.NotSame(foo, moo);

Exceptions can be tested for with the Throws function. The code under test needs to be provided inside a delegate otherwise the exception is not expected and will cause the unit test to fail.

Assert.Throws<Exception>(() => { throw new Exception(); });

Custom IEqualityComparer

Custom comparers can be written to provide bespoke equality comparison. This is not part of XUnit though we can use this functionality with our assertions.

In the example below we have a class called Foo which has an Id and a Name. Id is an int and Name is a string. We want equality to be based upon the Id and the Name case insensitive.

We create a class implementing IEqualityComparer and implement the Equals and GetHasCode functions. Equals is our main function here as this is what our unit test will be using for the equals assertions.

public class Foo
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public class FooComparer : IEqualityComparer<Foo>
{
    public bool Equals(Foo x, Foo y)
    {
        return x.ID == y.ID && x.Name.Equals(y.Name, StringComparison.CurrentCultureIgnoreCase);
    }

    public int GetHashCode(Foo obj)
    {
        return obj.ID.GetHashCode();
    }
}

We can pass in an instance of our custom comparer to the Assert.Equal

Assert.Equal(new Foo { ID = 1, Name = "A Name"), new Foo { ID = 1, Name = "a name"), new FooComparer());