Fakultas Ilmu Komputer UI

Commit 00dd7ce1 authored by Dwi Nanda Susanto's avatar Dwi Nanda Susanto
Browse files

Merge branch 'exercise8' into 'master'

Exercise8

See merge request !12
parents e89753da 49d7adbc
Pipeline #25766 failed with stages
in 6 minutes and 19 seconds
...@@ -33,4 +33,53 @@ Dalam exercise kali ini saya membuat mutant yang bertujuan untuk membuat functio ...@@ -33,4 +33,53 @@ Dalam exercise kali ini saya membuat mutant yang bertujuan untuk membuat functio
Dalam exercise kali ini mempelajari tentang ```Spiking```. ```Spiking``` adalah sebuah aktivitas explore program ketika membuat sebuah fitur tanpa membuat TDD functional dan unittest. Pada exercise kali ini spiking diterapkan ketika membuat fitur *auth* tanpa tanpa menggunakan passworrd. Dalam exercise kali ini mempelajari tentang ```Spiking```. ```Spiking``` adalah sebuah aktivitas explore program ketika membuat sebuah fitur tanpa membuat TDD functional dan unittest. Pada exercise kali ini spiking diterapkan ketika membuat fitur *auth* tanpa tanpa menggunakan passworrd.
Sedangkan De-```Spiking``` adalah menghapus keseluruhan implementasi spiking lalu memulai membuat implementasi fitur barusan, dari awal lagi hanya saja lengkap dengan TDD nya. Sedangkan De-```Spiking``` adalah menghapus keseluruhan implementasi spiking lalu memulai membuat implementasi fitur barusan, dari awal lagi hanya saja lengkap dengan TDD nya.
\ No newline at end of file
# Exercise 8
Mocking adalah sebuah cara untuk melakukan testing dengan membuat fungsi `fake` yang menerima input dan mengeluarkan hasil yang sama seperti fungsi yang ingin di test dengan tujuan untuk menghindari pemanggilan fungsi yang berlebihan dan untuk menghindari fungsi lain terpengaruh oleh fungsi yang tadi dipanggil
Pada exercise ini dipelajari 2 cara melakukan mocking yaitu `manual mocking` dan mocking dengan `library`.
Pada manual mocking dilakukan dengan mengganti fungsi `send_mail` dengan versi fake nya yang bernama `fake_send_mail` :
```
def test_sends_mail_to_address_from_post(self):
self.send_mail_called = False
def fake_send_mail(subject, body, from_email, to_list):
self.send_mail_called = True
self.subject = subject
self.body = body
self.from_email = from_email
self.to_list = to_list
accounts.views.send_mail = fake_send_mail
self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
self.assertTrue(self.send_mail_called)
self.assertEqual(self.subject, 'Your login link for Superlists')
self.assertEqual(self.from_email, 'noreply@superlists')
self.assertEqual(self.to_list, ['edith@example.com'])
```
Sedangkan mocking dengan `library`, dilakukan dengan menggunakan library dari Python yang sudah tersedia bernama `Mock`. Library tersebut menyediakan decorator bernama `patch` yang digunakan pada fungsi yang membutuhkan mock
```
@patch('accounts.views.send_mail')
def test_sends_mail_to_address_from_post(self, mock_send_mail):
self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
self.assertEqual(mock_send_mail.called, True)
(subject, body, from_email, to_list), kwargs = mock_send_mail.call_args
self.assertEqual(subject, 'Your login link for Superlists')
self.assertEqual(from_email, 'noreply@superlists')
self.assertEqual(to_list, ['edith@example.com'])
```
Dengan memberikan anotasi `@Patch` fungsi yang berada dibawahnya akan secara otomatis dibuatkan versi Mock-nya oleh library tersebut.
\ No newline at end of file
import sys import sys
from accounts.models import ListUser, Token from accounts.models import ListUser, Token
from accounts.models import User, Token
class PasswordlessAuthenticationBackend(object): class PasswordlessAuthenticationBackend(object):
def authenticate(self, uid): def authenticate(self, uid):
print('uid', uid, file=sys.stderr)
if not Token.objects.filter(uid=uid).exists():
print('no token found', file=sys.stderr)
return None
token = Token.objects.get(uid=uid)
print('got token', file=sys.stderr)
try: try:
user = ListUser.objects.get(email=token.email) token = Token.objects.get(uid=uid)
print('got user', file=sys.stderr) return User.objects.get(email=token.email)
return user except User.DoesNotExist:
except ListUser.DoesNotExist: return User.objects.create(email=token.email)
print('new user', file=sys.stderr) except Token.DoesNotExist:
return ListUser.objects.create(email=token.email) return None
def get_user(self, email): def get_user(self, email):
return ListUser.objects.get(email=email) try:
\ No newline at end of file return User.objects.get(email=email)
except User.DoesNotExist:
return None
from django.db import models from django.db import models
import uuid import uuid
import uuid
from django.contrib import auth
from django.db import models
auth.signals.user_logged_in.disconnect(auth.models.update_last_login)
from django.contrib.auth.models import ( from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager, PermissionsMixin AbstractBaseUser, BaseUserManager, PermissionsMixin
) )
...@@ -33,4 +37,4 @@ class User(models.Model): ...@@ -33,4 +37,4 @@ class User(models.Model):
REQUIRED_FIELDS = [] REQUIRED_FIELDS = []
USERNAME_FIELD = 'email' USERNAME_FIELD = 'email'
is_anonymous = False is_anonymous = False
is_authenticated = True is_authenticated = True
\ No newline at end of file
from django.test import TestCase
from django.contrib.auth import get_user_model
from accounts.authentication import PasswordlessAuthenticationBackend
from accounts.models import Token
User = get_user_model()
class AuthenticateTest(TestCase):
def test_returns_None_if_no_such_token(self):
result = PasswordlessAuthenticationBackend().authenticate(
'no-such-token'
)
self.assertIsNone(result)
def test_returns_new_user_with_correct_email_if_token_exists(self):
email = 'edith@example.com'
token = Token.objects.create(email=email)
user = PasswordlessAuthenticationBackend().authenticate(token.uid)
new_user = User.objects.get(email=email)
self.assertEqual(user, new_user)
def test_returns_existing_user_with_correct_email_if_token_exists(self):
email = 'edith@example.com'
existing_user = User.objects.create(email=email)
token = Token.objects.create(email=email)
user = PasswordlessAuthenticationBackend().authenticate(token.uid)
self.assertEqual(user, existing_user)
class GetUserTest(TestCase):
def test_gets_user_by_email(self):
User.objects.create(email='another@example.com')
desired_user = User.objects.create(email='edith@example.com')
found_user = PasswordlessAuthenticationBackend().get_user(
'edith@example.com'
)
self.assertEqual(found_user, desired_user)
def test_returns_None_if_no_user_with_that_email(self):
self.assertIsNone(
PasswordlessAuthenticationBackend().get_user('edith@example.com')
)
from django.test import TestCase
from unittest.mock import patch, call
import accounts.views
from accounts.models import Token
from django.test import TestCase
from unittest.mock import patch
class SendLoginEmailViewTest(TestCase):
def test_redirects_to_home_page(self):
response = self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
self.assertRedirects(response, '/')
def test_sends_mail_to_address_from_post(self):
self.send_mail_called = False
def fake_send_mail(subject, body, from_email, to_list):
self.send_mail_called = True
self.subject = subject
self.body = body
self.from_email = from_email
self.to_list = to_list
accounts.views.send_mail = fake_send_mail
self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
self.assertTrue(self.send_mail_called)
self.assertEqual(self.subject, 'Your login link for Superlists')
self.assertEqual(self.from_email, 'noreply@superlists')
self.assertEqual(self.to_list, ['edith@example.com'])
@patch('accounts.views.send_mail')
def test_sends_mail_to_address_from_post(self, mock_send_mail):
self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
self.assertEqual(mock_send_mail.called, True)
(subject, body, from_email, to_list), kwargs = mock_send_mail.call_args
self.assertEqual(subject, 'Your login link for Superlists')
self.assertEqual(from_email, 'noreply@superlists')
self.assertEqual(to_list, ['edith@example.com'])
self.assertEqual(to_list, ['schmedith@example.com'])
@patch('accounts.views.send_mail')
def test_sends_mail_to_address_from_post(self, mock_send_mail):
self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
self.assertEqual(mock_send_mail.called, True)
(subject, body, from_email, to_list), kwargs = mock_send_mail.call_args
self.assertEqual(subject, 'Your login link for Superlists')
self.assertEqual(from_email, 'noreply@superlists')
self.assertEqual(to_list, ['edith@example.com'])
def test_adds_success_message(self):
response = self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
}, follow=True)
message = list(response.context['messages'])[0]
self.assertEqual(
message.message,
"Check your email, we've sent you a link you can use to log in."
)
self.assertEqual(message.tags, "success")
def test_creates_token_associated_with_email(self):
self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
token = Token.objects.first()
self.assertEqual(token.email, 'edith@example.com')
@patch('accounts.views.send_mail')
def test_sends_link_to_login_using_token_uid(self, mock_send_mail):
self.client.post('/accounts/send_login_email', data={
'email': 'edith@example.com'
})
token = Token.objects.first()
expected_url = f'http://testserver/accounts/login?token={token.uid}'
(subject, body, from_email, to_list), kwargs = mock_send_mail.call_args
self.assertIn(expected_url, body)
@patch('accounts.views.auth')
def test_calls_authenticate_with_uid_from_get_request(self, mock_auth):
self.client.get('/accounts/login?token=abcd123')
self.assertEqual(
mock_auth.authenticate.call_args,
call(uid='abcd123')
)
@patch('accounts.views.auth')
def test_calls_auth_login_with_user_if_there_is_one(self, mock_auth):
response = self.client.get('/accounts/login?token=abcd123')
self.assertEqual(
mock_auth.login.call_args,
call(response.wsgi_request, mock_auth.authenticate.return_value)
)
class LoginViewTest(TestCase):
def test_redirects_to_home_page(self):
response = self.client.get('/accounts/login?token=abcd123')
self.assertRedirects(response, '/')
def test_does_not_login_if_user_is_not_authenticated(self, mock_auth):
mock_auth.authenticate.return_value = None
self.client.get('/accounts/login?token=abcd123')
self.assertEqual(mock_auth.login.called, False)
from django.test import TestCase from django.test import TestCase
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from accounts.models import Token from accounts.models import Token
from django.test import TestCase
from django.contrib import auth
from accounts.models import Token
User = auth.get_user_model()
User = get_user_model() User = get_user_model()
class TokenModelTest(TestCase): class TokenModelTest(TestCase):
...@@ -13,4 +16,10 @@ class TokenModelTest(TestCase): ...@@ -13,4 +16,10 @@ class TokenModelTest(TestCase):
class UserModelTest(TestCase): class UserModelTest(TestCase):
def test_email_is_primary_key(self): def test_email_is_primary_key(self):
user = User(email='a@b.com') user = User(email='a@b.com')
self.assertEqual(user.pk, 'a@b.com') self.assertEqual(user.pk, 'a@b.com')
\ No newline at end of file
def test_no_problem_with_auth_login(self):
user = User.objects.create(email='edith@example.com')
user.backend = ''
request = self.client.request().wsgi_request
auth.login(request, user)
from django.conf.urls import url from django.conf.urls import url
from accounts import views from accounts import views
from django.contrib.auth.views import logout
urlpatterns = [ urlpatterns = [
url(r'^send_email$', views.send_login_email, name='send_login_email'), url(r'^send_login_email$', views.send_login_email, name='send_login_email'),
url(r'^login$', views.login, name='login'), url(r'^login$', views.login, name='login'),
url(r'^logout$', views.logout, name='logout'), url(r'^logout$', logout, {'next_page': '/'}, name='logout'),
] ]
\ No newline at end of file
import uuid import uuid
import sys import sys
from django.contrib import messages
from django.contrib import auth, messages
from accounts.models import Token from accounts.models import Token
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.contrib.auth import login as auth_login from django.contrib.auth import login as auth_login
from django.core.mail import send_mail from django.core.mail import send_mail
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.contrib.auth import login as auth_login, logout as auth_logout from django.contrib.auth import login as auth_login, logout as auth_logout
from django.core.mail import send_mail
from django.shortcuts import redirect
# def send_login_email(request):
# email = request.POST['email']
# uid = str(uuid.uuid4())
# Token.objects.create(email=email, uid=uid)
# print('saving uid', uid, 'for email', email, file=sys.stderr)
# url = request.build_absolute_uri(f'/accounts/login?uid={uid}')
# send_mail(
# 'Your login link for Superlists',
# f'Use this link to log in:\n\n{url}',
# 'noreply@superlists',
# [email],
# )
# return render(request, 'login_email_sent.html')
def send_login_email(request): def send_login_email(request):
email = request.POST['email'] email = request.POST['email']
uid = str(uuid.uuid4()) token = Token.objects.create(email=email)
Token.objects.create(email=email, uid=uid) url = request.build_absolute_uri(
print('saving uid', uid, 'for email', email, file=sys.stderr) reverse('login') + '?token=' + str(token.uid)
url = request.build_absolute_uri(f'/accounts/login?uid={uid}') )
message_body = f'Use this link to log in:\n\n{url}'
send_mail( send_mail(
'Your login link for Superlists', 'Your login link for Superlists',
f'Use this link to log in:\n\n{url}', message_body,
'noreply@superlists', 'noreply@superlists',
[email], [email]
) )
return render(request, 'login_email_sent.html') messages.success( request,
"Check your email, we've sent you a link you can use to log in."
)
return redirect('/')
def login(request): def login(request):
print('login view', file=sys.stderr) user = auth.authenticate(uid=request.GET.get('token'))
uid = request.GET.get('uid') if user:
user = authenticate(uid=uid) auth.login(request, user)
if user is not None:
auth_login(request, user)
return redirect('/') return redirect('/')
def logout(request): def logout(request):
auth_logout(request) auth_logout(request)
return redirect('/') return redirect('/')
\ No newline at end of file
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>To-Do</title> <title>To-Do</title>
<link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <link href="/static/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/base.css" rel="stylesheet"> <link href="/static/base.css" rel="stylesheet">
</head> </head>
<body> <body>
<div class="navbar"> <nav class="navbar navbar-default" role="navigation">
{% if user.is_authenticated %} <div class="container-fluid">
<p>Logged in as {{ user.email}}</p> <a class="navbar-brand" href="/">Superlists</a> {% if user.email %}
<p><a id="id_logout" href="{% url 'logout' %}">Log out</a></p> <ul class="nav navbar-nav navbar-right">
{% else %} <li class="navbar-text">Logged in as {{ user.email }}</li>
<form method="POST" action ="{% url 'send_login_email' %}"> <li><a href="{% url 'logout' %}">Log out</a></li>
Enter email to log in: <input name="email" type="text" /> </ul>
{% csrf_token %} {% else %}
</form> <form class="navbar-form navbar-right" method="POST" action="{% url 'send_login_email' %}">
{% endif %} <span>Enter email to log in:</span>
<input class="form-control" name="email" type="text" /> {% csrf_token %}
</form>
{% endif %}
</div> </div>
</nav>
<div class="container"> <div class="navbar">
<div class="row"> {% if user.is_authenticated %}
<div class="col-md-6 col-md-offset-3 jumbotron"> <p>Logged in as {{ user.email}}</p>
<div class="text-center"> <p><a id="id_logout" href="{% url 'logout' %}">Log out</a></p>
{% else %}
<form method="POST" action="{% url 'send_login_email' %}">
Enter email to log in:
<input name="email" type="text" /> {% csrf_token %}
</form>
{% endif %}
</div>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3 jumbotron">
<div class="text-center">
<h1>{% block header_text %}{% endblock %}</h1> <h1>{% block header_text %}{% endblock %}</h1>
<form method="POST" action="{% block form_action %}{% endblock %}"> <form method="POST" action="{% url 'send_login_email' %}">
<input name="item_text" id="id_new_item" class="form-control input-lg" <input name="item_text" id="id_new_item" class="form-control input-lg" placeholder="Enter a to-do item" /> {% csrf_token %}
placeholder="Enter a to-do item" /> </form>
{% csrf_token %}
</form>
</div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-md-6 col-md-offset-3">
{% block table %}
{% endblock %}
</div>
</div>
</div> </div>
<div class="row">
<div class="col-md-6 col-md-offset-3">
{% block table %} {% endblock %}
</div>
</div>
</div>
</body> </body>
</html>
</html>
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment