Ahmed Wagdi
Ahmed Wagdi

Reputation: 4391

TypeError: 'NoneType' object is not subscriptable in Django unittests for message

Am trying to do a unit test for a view that returns a message when some input is duplicated in the database.it gives me the error TypeError: 'NoneType' object is not subscriptable

Here is the view

@login_required
def worker_create(request):
    worker_create_form = WorkerCreateForm(request.POST)
    if request.method == 'POST':
        if worker_create_form.is_valid():
            form = worker_create_form.save(commit=False)
            phone = form.phone
            check_phone = Worker.objects.filter(phone=phone)
            if check_phone.count() != 0:
                messages.error(request, 'رقم الهاتف مستخدم من قبل')
            else:
                form.save()
                return redirect('worker_list')
    else:
        worker_create_form = WorkerCreateForm(request.POST)
    context = {
        'worker_create_form': worker_create_form,
    }
    return render(request, 'erp_system/worker/create.html', context)

and here are the tests I've created for it

class WorkerCreateTest(TestCase):
    def setUp(self):
        User.objects.create_user(username='test_user', email='[email protected]', password='test_password')
        branch = Branch.objects.create(name='test branch')
        Worker.objects.create(name="ay btngan ", phone='01207199086', branch=branch)

    def test_get_request_unauthenticated(self):
        response = self.client.get(reverse('worker_create'))
        url = reverse('worker_create')
        self.assertRedirects(response, '/login/?next=' + url)

    def test_get_request_authenticated(self):
        self.client.login(username='test_user', password='test_password')
        response = self.client.get(reverse('worker_create'))
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed('erp_system/worker/create.html')

    def test_post_request_empty_data(self):
        self.client.login(username='test_user', password='test_password')
        response = self.client.post(reverse('worker_create'), {})
        self.assertFormError(response, 'worker_create_form', 'name', 'This field is required.')
        self.assertFormError(response, 'worker_create_form', 'phone', 'This field is required.')
        self.assertFormError(response, 'worker_create_form', 'branch', 'This field is required.')

    def test_post_request_invalid_data(self):
        self.client.login(username='test_user', password='test_password')
        branch = Branch.objects.create(name='test again ')
        name = 'just a name'
        for i in range(300):
            name += 'h'
        response = self.client.post(reverse('worker_create'),
                                    {'name': name, 'phone': '01207199086', 'branch': branch.id})
        self.assertEqual(response.status_code, 200)

    def test_post_request_duplicated_phone(self):
        self.client.login(username='test_user', password='test_password')
        branch = Branch.objects.create(name='test again ')
        response = self.client.post(reverse('worker_create'),
                                    {'name': 'test worker', 'phone': '01207199086', 'branch': branch.id})
        print(response)
        messages = list(response.context['messages'])
        self.assertEqual(len(messages), 1)
        self.assertEqual(str(messages[0]), 'رقم الهاتف مستخدم من قبل')

    def test_post_request_valid_data(self):
        self.client.login(username='test_user', password='test_password')
        branch = Branch.objects.create(name='test branch1234')
        name = 'new valid name'
        response = self.client.post(reverse('worker_create'),
                                    {'name': name, 'branch': branch.id, 'phone': '0151951115'})
        self.assertEqual(response.status_code, 302)

Important I noticed when I added print(response) that it gives me HttpResponseRedirect not just HttpResponse what means there is no context given. Why it using redirect in here!?

Upvotes: 1

Views: 1354

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476729

In your test test_post_request_duplicated_phone, you make a POST request to the worker_create, and you expect to retrieve an error, because there exists already a record for the given phone.

The documentation on tests [Django-doc] however mentions that:

A TestCase, on the other hand, does not truncate tables after a test. Instead, it encloses the test code in a database transaction that is rolled back at the end of the test. This guarantees that the rollback at the end of the test restores the database to its initial state.

So that means that, unless you implement some "tricks" to prevent this, the side-effects that one test has (on the database) will be gone when you enter a second test. This makes sense, since reordering the tests should not result in a different outcome.

You can however create such Worker object in your test in advance, and thus make sure that the test will indeed error:

def test_post_request_duplicated_phone(self):
    self.client.login(username='test_user', password='test_password')
    branch = Branch.objects.create(name='test again ')
    Worker.objects.create(name=" just a name ", phone='01207199086', branch=branch)
    response = self.client.post(reverse('worker_create'),
                                {'name': 'test worker', 'phone': '01207199086', 'branch': branch.id})
    print(response)
    messages = list(response.context['messages'])
    self.assertEqual(len(messages), 1)
    self.assertEqual(str(messages[0]), 'رقم الهاتف مستخدم من قبل')

Upvotes: 1

Related Questions