Getting error when running automated for login apiview with DRF

Hello friends. I’m having receiving error messages when I run automated tests for my login APIView in the console. Here is the full traceback, it shows two tests that ran here

show_sunset_warning()
System check identified no issues (0 silenced).
F.
======================================================================
FAIL: test_login_user (arborfindr.test.test_login.UserLoginTests.test_login_user)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/coreyj/Documents/ArborHub/MyProject/arborfindr/test/test_login.py", line 13, in test_login_user
    self.assertTrue(self.client.login(username='username', password='Password!87'))
    ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: False is not true

----------------------------------------------------------------------
Ran 2 tests in 0.732s

FAILED (failures=1)
Destroying test database for alias 'default'...

Here is my login serializer class:

class LoginSerializers(serializers.ModelSerializer):
    email = serializers.CharField(max_length=255)
    password = serializers.CharField(
        label =("Password"),
        style={'input_type': 'password'},
        trim_whitespace=False,
        max_length=128,
        write_only=True
    )

    def validate(self, data):
        email = data.get('email')
        password = data.get('password')

        if email and password:
            user = authenticate(request=self.context.get('request'),
                                email=email, password=password)

            if not user:
                msg = ('Invalid email/password')
                raise serializers.ValidationError(msg, code = 'Authorization')
        else:
            msg = ('Must include valid "email" and "password".')
            raise serializers.ValidationError(msg, code = 'Authorization')

        data['user'] = user
        return data

Here is my automated test for login APIView

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from ..models import User
from rest_framework.test import APIClient


client = APIClient()
class UserLoginTests(APITestCase):

    def test_login_user(self):
        user = User.objects.create_user('username', 'Password!87')
        self.assertTrue(self.client.login(username='username', password='Password!87'))
        response = self.client.get(reverse('login'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

Finally here is my Login API endpoint in views.py

class LoginView(APIView):
    def post(self, request, *args, **kwargs):
        serializer = LoginSerializers(data=request.data, context={'request': request})
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        login(None, user)
        token = Token.objects.create(user=user)
        return Response({"status": status.HTTP_200_OK, "Token": token.key})

        if user is not None:

            # Generate token
            refresh = RefreshToken.for_user(user)
            access_token = str(refresh.access_token)
            return Response({
                'message': 'Successful login',
                'access_token': access_token,
                'refresh_token': str(refresh),
            }, status=status.HTTP_200_OK)
        return Response({'error': 'Invalid email/password'}, status=status.HTTP_401_UNAUTHORIZED)

I modified my LoginTestCase, so it doesn’t resemble the RegisterTestCase much. I’m just trying to wrap my head around why I got this error, looks different than the Traceback I got my for my Register API endpoint. I even used APIClient as per the documentation. Please any kind of solutions, I’ll really appreciate it

I believe this is incorrect:

The signature for django.contrib.auth.models.UserManager.create_user is:

def create_user(self, username, email=None, password=None, **extra_fields):

The second positional parameter is email, not password. I think you need to either pass password as a keyword parameter or as the third positional parameter.

Here is my changed test

client = APIClient()
class UserLoginTests(APITestCase):

    def create_user(self, username, email=None, password=None, **extra_fields):
        user = User.objects.create_user('username', 'Password!87')
        self.assertTrue(self.client.login(username='username', password='Password!87'))
        response = self.client.get(reverse('login'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

So the first part of your response I understand, the second part I’m rather confused. What do you mean by " The second positional parameter is email, not password. I think you need to either pass password as a keyword parameter or as the third positional parameter."?

You are passing two parameters (positionally) to the create_user method.

However, that method is defined as:

That means that your call:

You are not supplying a password to create_user.

I think I see what’s going on. How do you suppose that I can fix this? Should I fix this statement user = User.objects.create_user('username', 'Password!87') by adding or removing the positional parameters?

I may have fixed it, I’m not seeing anymore errors

client = APIClient()
class UserLoginTests(APITestCase):

    def create_user(self, username, email=None, password=None, **extra_fields):
        user = User.objects.create_user('username', 'email', 'Password!87')
        self.assertTrue(self.client.login(username='username', password='Password!87'))
        response = self.client.get(reverse('login'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

And here are the test results:

System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.471s

OK
Destroying test database for alias 'default'...

So this seems like a case of incorrect number of positional arguments, leading to a conflict possibly? Also when I ran the test for the login api, it only shows 1 test that was ran, when in fact I have two tests created, one for register and another for login

Yes, that’s one way to fix it. The other was to convert the password to use a keyword parameter.

We’d need to see the complete code for this to offer any thoughts.

1 Like

By complete code, do you mean the serializers, TestCases and APIViews for register API? I can supply all three of those if that’s what you’re talking about

Just the test cases. We don’t need to see the code being tested (yet).

1 Like

Here is my register test case, it was in initial post on this help forum:

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from ..models import User


class UserRegisterTests(APITestCase):
    def test_register_user(self):
        url = reverse('arborfindr:register')
        data = {'email': 'myemail@mail.com', 'username': 'jonedoe12', 'password': 'mypassword123'}
        response = self.client.post(url, data, format='json')
        self.assertTrue(User.objects.filter(username='jonedoe12').exists())
        user = User.objects.get(username='jonedoe12')
        self.assertEqual(user.email, 'myemail@mail.com')
        self.assertTrue(user.check_password('mypassword123'))
        ```

That’s only one test case.

I know it’s long and chained, but it’s one of my earlier comments. Here it is again

client = APIClient()
class UserLoginTests(APITestCase):

    def create_user(self, username, email=None, password=None, **extra_fields):
        user = User.objects.create_user('username', 'email', 'Password!87')
        self.assertTrue(self.client.login(username='username', password='Password!87'))
        response = self.client.get(reverse('login'))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

So these are two separate files? Both part of the same file?

Separate test files, I do apologize. The first code I pasted belongs in test_register.py and the one I just pasted now is in test_login.py

So those are the complete contents of both files?

What directories are they both in?

What are the command(s) you’re using to run these?

Not sure what you mean by directories? But they are both in my Django app directory inside the test folders.

Yes this is the complete code and the command I ran is python manage.py test

Basically my project directory path is ArborHub/MyProject/arborfindr/tests. Both the login/register test files are inside the path

But are those the complete files? (In other words, is there anything else in either of those files?)

Affirmative, no there isn’t anything else.

I don’t see how this file:

can execute without the proper import statements.

I’m guessing the problem here may be that it can’t