Проверьте что запрос к несуществующей странице вернет ошибку 404

Unittest в Django:

Тестирование Models

Протестировал модели приложения posts в Yatube.

Добавил в классы Post и Group метод str (если его ещё нет):

  • для класса Post — первые пятнадцать символов поста: post.text[:15];
  • для класса Group — название группы.

Протестировал, правильно ли отображается значение поля str в объектах моделей.

Тестирование URLs

Проверка доступности страниц и названия шаблонов приложения Posts проекта Yatube.

Проверка должна учитывать права доступа.

Проверьте, что запрос к несуществующей странице вернёт ошибку 404.

img.png

Проверка namespase:name и шаблонов

Тесты, проверяющие, что во view-функциях используются правильные html-шаблоны.

img_1.png

Тестирование контекста

Проверка, соответствует ли ожиданиям словарь context, передаваемый в шаблон при вызове.

img_2.png

Дополнительная проверка при создании поста

Проверил, что если при создании поста указать группу, то этот пост появляется:

  • на главной странице сайта,
  • на странице выбранной группы,
  • в профайле пользователя.

Проверил, что этот пост не попал в группу, для которой не был предназначен.

Тестирование Forms

Написал тесты, которые проверяют, что:

  • при отправке валидной формы со страницы создания поста reverse('posts:create_post') создаётся новая запись в базе данных;
  • при отправке валидной формы со страницы редактирования поста reverse('posts:post_edit', args=('post_id',)) происходит изменение поста с post_id в базе данных.

Запуск тестов через терминал «bash»

  1. Cклонировать репозиторий на свой ПК

git clone https://github.com/DamirShamsutdinov/Unittest.git

  1. Перейти в папку с проектом

cd hw04_tests

  1. Создать папку «venv»

python -m venv venv

  1. Активировать вирт.окружение

source venv/Scripts/activate

  1. Установить все зависимости

pip install -r requirements.txt

  1. Перейти в папку yatube

cd yatybe

  1. Ввести команду — запустятся тесты

python manage.py test

I am trying to automate 404 pages testing using Django 1.4’s testing framework.

If I print 127.0.0.1:8000/something/really/weird/ in browser address bar with development server running, I see a 404 page, with correct «404 NOT FOUND» status (as firebug shows).

But if I try to use this code for testing:

from django.test import TestCase
class Sample404TestCase(TestCase):
    def test_wrong_uri_returns_404(self):
        response = self.client.get('something/really/weird/')
        self.assertEqual(response.status_code, 404)

the test fails with this output:

$./manage.py test main
Creating test database for alias 'default'...
.F
======================================================================
FAIL: test_wrong_uri_returns_404 (main.tests.Sample404TestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../main/tests.py", line 12, in test_wrong_uri_returns_404
    self.assertEqual(response.status_code, 404)
*AssertionError: 200 != 404*

----------------------------------------------------------------------
Ran 2 tests in 0.031s

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

I’m seriously surprised with getting 200 code here. Anyone have any idea why on earth this is happening?

updated:

here lies urls.py: http://pastebin.com/DikAVa8T
and actual failing test is:

def test_wrong_uri_returns_404(self):
    response = self.client.get('/something/really/weird/')
    self.assertEqual(response.status_code, 404)

everything is happening in project https://github.com/gbezyuk/django-app-skeleton

Unittest в Django:

Тестирование Models

Протестировал модели приложения posts в Yatube.

Добавил в классы Post и Group метод str (если его ещё нет):

  • для класса Post — первые пятнадцать символов поста: post.text[:15];
  • для класса Group — название группы.

Протестировал, правильно ли отображается значение поля str в объектах моделей.

Тестирование URLs

Проверка доступности страниц и названия шаблонов приложения Posts проекта Yatube.

Проверка должна учитывать права доступа.

Проверьте, что запрос к несуществующей странице вернёт ошибку 404.

img.png

Проверка namespase:name и шаблонов

Тесты, проверяющие, что во view-функциях используются правильные html-шаблоны.

img_1.png

Тестирование контекста

Проверка, соответствует ли ожиданиям словарь context, передаваемый в шаблон при вызове.

img_2.png

Дополнительная проверка при создании поста

Проверил, что если при создании поста указать группу, то этот пост появляется:

  • на главной странице сайта,
  • на странице выбранной группы,
  • в профайле пользователя.

Проверил, что этот пост не попал в группу, для которой не был предназначен.

Тестирование Forms

Написал тесты, которые проверяют, что:

  • при отправке валидной формы со страницы создания поста reverse('posts:create_post') создаётся новая запись в базе данных;
  • при отправке валидной формы со страницы редактирования поста reverse('posts:post_edit', args=('post_id',)) происходит изменение поста с post_id в базе данных.

Запуск тестов через терминал «bash»

  1. Cклонировать репозиторий на свой ПК

git clone https://github.com/DamirShamsutdinov/Unittest.git

  1. Перейти в папку с проектом

cd hw04_tests

  1. Создать папку «venv»

python -m venv venv

  1. Активировать вирт.окружение

source venv/Scripts/activate

  1. Установить все зависимости

pip install -r requirements.txt

  1. Перейти в папку yatube

cd yatybe

  1. Ввести команду — запустятся тесты

python manage.py test

Архив с файлами готового проекта:

Протестируйте проект Yatube

Большую часть заданий вы выполнили в течение уроков, все они будут проверены. Если вы что-то не успели сделать — самое время этим заняться.Ниже перечислены все тесты, которые нужно было сделать в уроках и отмечено, какие ещё тесты нужно написать.

Тестирование Models: «Unittest в Django: тестирование моделей»

Протестируйте модели приложения posts в Yatube.

Добавьте в классы Post и Group метод __str__ (если его ещё нет):

  • для класса Post — первые пятнадцать символов поста: **post.text[:15];
  • для класса Group — название группы.

Протестируйте, правильно ли отображается значение поля __str__ в объектах моделей.

Тестирование URLs: «Unittest в Django: тестирование URLs»

Проверьте доступность страниц и названия шаблонов приложения Posts проекта Yatube. Проверка должна учитывать права доступа.

Проверьте, что запрос к несуществующей странице вернёт ошибку 404.

Подсказки

  • Если заранее соберёте страницы в списки и прогоните однотипные тесты через subTest — будете совсем молодец.
  • Подумайте перед составлением словаря с шаблонами и адресами. Некоторые шаблоны использованы дважды для разных адресов. Возможно, в качестве ключей словаря будет выгоднее использовать адреса страниц, а не названия шаблонов, как описано в теории.

Проверка namespase:name и шаблонов: «Unittest в Django: тестирование Views»

Напишите тесты, проверяющие, что во view-функциях используются правильные html-шаблоны.

Тестирование контекста: «Unittest в Django: тестирование views»

Проверьте, соответствует ли ожиданиям словарь context, передаваемый в шаблон при вызове.

Дополнительная проверка при создании поста: : «Unittest в Django: тестирование Views»

Проверьте, что если при создании поста указать группу, то этот пост появляется

  • на главной странице сайта,
  • на странице выбранной группы,
  • в профайле пользователя.

Проверьте, что этот пост не попал в группу, для которой не был предназначен.

Тестирование Forms: «Unittest в Django: тестирование Forms»

В проекте Yatube напишите тесты, которые проверяют, что

  • при отправке валидной формы со страницы создания поста reverse('posts:create_post') создаётся новая запись в базе данных;
  • при отправке валидной формы со страницы редактирования поста reverse('posts:create_post', args=('post_id',)) происходит изменение поста с post_id в базе данных.

Рекомендации

Ваша задача — написать работающие и оптимизированные тесты.

По возможности применяйте методы setUp или setUpClass.

Однотипные тесты пишите через subTest.

Необязательные задания:

Models

Добавьте в модель posts.Post атрибуты verbose_name и help_text и протестируйте их:

class Post(models.Model):
    text = models.TextField(
        'Текст поста',
        help_text='Введите текст поста'
    )
    pub_date = models.DateTimeField(
        'Дата публикации',
        auto_now_add=True
    )
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        verbose_name='Автор'
    )
    group = models.ForeignKey(
        Group,
        on_delete=models.SET_NULL,
        related_name='posts',
        blank=True,
        null=True,
        verbose_name='Группа',
        help_text='Выберите группу'
    ) 

Обычно тесты verbose_name и help_text пишут только в приступе сильной coverage-зависимости или в учебных целях. Выбирайте, с кем вы!

URLs

Задание 1

Попробуйте использовать в тестах описания из HTTPStatus вместо кодов состояния.

Задание 2

В проекте Yatube есть приложения users и about. Протестируйте их urls.

Подсказки

В простых случаях (например, в приложении about всего два адреса) нет нужды в сложной структуре папки с тестами. Тесты можно писать в фале tests.py, автоматически сгенерированном при создании приложения.

Views

  1. Проверьте namespase:name в приложениях user и about.
    Адрес reverse('users:signup') должен обрабатывать users/signup.html
  2. Если шаблоны в сценарии восстановления пароля через email не были переопределены — проверять их namespase:name не нужно. Если шаблоны переопределены — проверьте соответствие namespase:name.
  3. Проверьте, что на страницу reverse('users:signup') в контексте передаётся форма для создания нового пользователя.

Forms

Проверьте, что при отправке валидной формы reverse('users:signup') создаётся новый пользователь.

Время на прочтение
6 мин

Количество просмотров 5.4K

Есть у меня один сайт, как сейчас говорят, пет-проект. Был написан в далеком 2013 году, что называется «на коленке» без использования каких-то фреймворков. Только php, только хардкор. Но тем не менее, функции свои выполнял, даже обрел некую популярность в узких кругах и был неплохо проиндексирован.

Недавно было решено начисто переписать его на современном стеке. Выбор пал на Laravel и Vue с серверным рендером. Сказано — сделано. Сайт переписан, развернут на vps, работает. Но есть одно но. В яндекс-метрике остались тысячи ссылок, которые на текущий момент не актуальны, но эти адреса возвращают код 200 и поисковый бот снова и снова их проверяет. Не хорошо.

Итак, проблема обозначена. Посмотрим на используемый стек технологий, чтобы понять что к чему.

Подготовка

Laravel используется исключительно в качестве API, на сервере висит на localhost:81, а nginx проксирует к нему маршруты /api . Здесь ничего не сделать.

Фронтэнд написан с использованием фреймворка quasar. Это невероятно крутая вещь, которая может собрать вам сайт или приложение под несколько платформ. Я использую платформу SSR. В этом случае квазар собирает весь фронт, плюс генерирует nodejs-сервер на базе express. Этот сервер у меня запущен на localhost:3000 и опять же nginx проксирует к нему все остальные запросы (кроме API).

Чтобы говорить более предметно, давайте создадим простенький проект. Будем считать, что с установкой quasar/cli вы справитесь сами/

quasar create q404

В папке q404 будет создана стартовая заготовка проекта. Можно перейти в нее и запустить сервер разработки.

cd q404
quasar dev -m ssr

Не заморачиваясь сильно на этом тестовом проекте, добавим вторую страницу AboutMe:

pages/AboutMe.vue

<script>
export default {
  name: 'AboutMe',
};
</script>
<template>
  <q-page padding>
    <h1>About me</h1>
  </q-page>
</template>

Соответствующий роут

router/routes.js

const routes = [
  {
    path     : '/',
    component: () => import('layouts/MainLayout'),
    children : [
      { path: '', component: () => import('pages/Index') },
      // Added:
      { path: 'about-me', component: () => import('pages/AboutMe') },
    ],
  },

И заменим главное меню

layouts/MainLayout.vue

const linksData = [
  {
    title: 'Homepage',
    icon : 'code',
    link : { path: '/' },
  },
  {
    title: 'About Me',
    icon : 'code',
    link : { path: '/about-me' },
  },
  {
    title: '404 test',
    icon : 'code',
    link : { path: '/404' },
  },
];

Для правильной работы следует еще поменять компонент EssentialLink.vue

EssentialLink.vue

<script>
   ...
   link: {
      type   : Object,
      default: null,
   },
   ...
</script>
<template>
  <q-item
      clickable
      :to="link"
  >
  ...
  </q-item>
</template>

Теперь все готово. Если сейчас мы запустим dev-сервер и откроем сайт, то увидим, что все работает, а заглянув в исходный код страницы убедимся, что и серверный рендер отрабатывает.

Кроме одной проблемы — страница 404 возвращает нам код ответа 200.

Поиск решения

Поиск информации в интернете готовых к использованию решений не дал. В официальном репозитории квазара есть ишью где рекомендуют создать отдельный роут для 404 страницы и редиректить на нее. Это не всегда подходит, мне, например, хотелось бы, чтобы пользователь оставался на той же странице, которую запросил, но с отображением плашки «404 not found», т.е. чтобы url в адресной строке не менялся.

Другой совет заключается в том, чтобы не использовать в приложении роут «*». Да. В этом случае при запросе несуществующей страницы сервер ответит кодом 404, но при навигации внутри приложения у нас теперь не будет красивой плашки 404. Не подходит.

На тостере предлагалось в сервере express делать дополнительные запросы и отправлять при необходимости 404-й код. Но сами понимаете, такое себе решение. В топку.

Встречались и еще советы разной степени полезности, но все они были отброшены как не подходящие по тем или иным причинам.

Но как я люблю отвечать заказчикам на их хотелки — «для программиста нет ничего невозможного, чего бы он не мог сделать с кодом». Это наша вселенная, мы здесь боги.

Решение

Давайте еще раз сформулируем ТЗ. Мы хотим

  • отдавать 404 по несуществующим адресам (тем, что явно не прописаны в нашем роутере)

  • отдавать 404 по несуществующим эндпойнтам API

  • отдавать 404 при отсутствии запрошенной информации. Т.е. эндпойнт верный, но объекта в базе данных нет.

  • также не хотим отказываться от использования роута «*» на стороне клиента

Решение на самом деле находится на поверхности.

Посмотрим на на код сервера, который нам предлагает квазар:

src-ssr/index.js

ssr.renderToString({ req, res }, (err, html) => {
    if (err) {
      if (err.url) {
        res.redirect(err.url)
      }
      else if (err.code === 404) {
        // Should reach here only if no "catch-all" route
        // is defined in /src/routes
        res.status(404).send('404 | Page Not Found')
      }
      else {
        // Render Error Page or
        // create a route (/src/routes) for an error page and redirect to it
        res.status(500).send('500 | Internal Server Error')
        if (ssr.settings.debug) {
          console.error(`500 on ${req.url}`)
          console.error(err)
          console.error(err.stack)
        }
      }
    }
    else {
      res.send(html)
    }
})

Комментарий в ветке условия 404 предупреждает нас, что сюда мы попадем, только если не будем использовать роут «*». А также мы можем понять, что если фреймворк не выбрасывает нас сюда, то мы сами можем бросить ошибку с телом {code:404}.

Квазар предлагает нам дополнительный хук — preFetch. Мы можем им воспользоваться, чтобы на стороне сервера выбросить нужную ошибку.

Для задействования данной фичи, нужно раскомментировать в файле quasar.conf.js строку

preFetch: true,

В компоненте Error404.vue добавим код

export default {
  name: 'Error404',
  preFetch({ ssrContext }) {
    if (ssrContext) {
      return Promise.reject({ code: 404 });
    }
  },
};

Теперь при отображении данного компонента будет выбрасываться ошибка и express сервер сможет поймать её и ответить кодом 404. Причем, ошибка будет выбрасываться только в контексте серверного рендера, на клиенте же, перейдя на не существующий адрес, мы увидим красивую заглушку NotFound.

Первый и четвертый пункты требований мы выполнили.

Теперь займемся обработкой api-вызовов. Подготовим Axios. Создадим инстанс, настроим его и привяжем Vue.

boot/axios.js

import Axios from 'axios';

export default ({ Vue, ssrContext, store }) => {

  let axiosInstance = Axios.create({
    baseURL         : '/api',
    timeout         : 0,
    responseType    : 'json',
    responseEncoding: 'utf8',
    headers         : {
      'X-Requested-With': 'XMLHttpRequest',
      'Accept'          : 'application/json',
    },

    // Reject only if the status code is greater than or equal to specify here
    validateStatus: status => status < 500,
  });
  
  // ...

  Vue.axios = axiosInstance;
}

Здесь все стандартно — обозначаем базовый урл, типы ответов, кодировку, заголовки. Функция validateStatus определяет ответы с какими кодами считать ошибкой. Мы будем считать ошибками все коды 5xx. В этом случае сайт будет возвращать код 500 и соответствующее сообщение.

Чтобы централизованно обрабатывать запросы к несуществующим эндпойнтам, добавим в эту конфигурацию перехватчик (interceptor в axios):

//...
axiosInstance.interceptors.response.use(response => {
  if (response.status >= 400) {
    if (ssrContext) {
      return Promise.reject({ code: response.status });
    } else {
      // store.commit('showErrorPage', response.status);
    }
  }
  return response.data;
});

К закомментированной строке вернемся позднее. Теперь Axios будет отклонять промис при ответах сервера с ошибками 4xx, и мы будем попадать в соответствующую ветку условия в сервере express чтобы вернуть правильный статус-код.

Для примера модифицируем компонент AboutMe.vue, добавив в него запрос к нашему API. Так как апишки у нас сейчас нет, запрос вернет 404 ошибку.

preFetch() {
  return Vue.axios.get('/test.json')
    .then(response => {
      console.log(response);
    });
},

Здесь два важных момента. Мы должны обязательно вернуть промис и мы не должны перехватывать ошибку, оставив это на откуп библиотеке Axios. Если нам нужно выполнить для данной страницы несколько запросов, можно обернуть их в Promise.all.

Теперь, если мы перейдем на адрес /about-me, и обновим страницу, то увидим в панели разработчика браузера, что запрос страницы возвращает ответ с кодом 404. То что нужно поисковым системам! Пункт два выполнен.

Однако при внутреннем переходе на данную страницу пользователь никак не информируется о проблеме. Тут можно применить разные решения для отображения плашки 404. Я использовал следующее.

Добавил в стор флаг

showErrorPage: false,

Мутацию

export const showErrorPage = (state, show) => state.showErrorPage = show;

И условие в компонент основной раскладки

<q-page-container>
  <Error404 v-if="$store.state.example.showErrorPage"/>
  <router-view v-else/>
</q-page-container>

И возвращаясь к загрузчику Axios, раскомментируем там строку

store.commit('showErrorPage', response.status);

Еще в роутере придется добавить хук beforeEach для сброса этого флага (но только при работе в браузере)

router/index.js

export default function ({ store, ssrContext }) {
  const Router = new VueRouter({
    scrollBehavior: () => ({ x: 0, y: 0 }),
    routes,
    mode: process.env.VUE_ROUTER_MODE,
    base: process.env.VUE_ROUTER_BASE,
  });

  if (!ssrContext) {
    Router.beforeEach((to, from, next) => {
      store.commit('showErrorPage', false);
      next();
    });
  }

  return Router;
}

На данный момент мы реализовали 3 из 4-х пунктов технического задания.
Что касается третьего пункта

отдавать 404 при отсутствии запрошенной информации. Т.е. эндпойнт верный, но объекта в базе данных нет.

то тут возможны варианты. Если вы делаете свой API, как положено, RESTful, то такой запрос обязан вернуть статус-код 404, что уже вписывается в построенную систему. Если же вы по каким-то причинам возвращаете объекты типа

{
  "status": false,
  "message": "Object not found"
}

то можно добавить дополнительные проверки в перехватчик Axios.

Еще кое-что

Внимательный читатель заметил, что мы в перехватчике отклоняем промис таким образом:

Promise.reject({ code: response.status });

А значит должны немного доработать express-сервер

else if (err.code >=400 && err.code < 500) {
  res.status(err.code).send(`${err.code} | ${getStatusMessage(err.code)}`);
}

Реализацию функции getStatusMessageрассматривать не будем =)
Таким образом мы получили также возможность корректной обработки любых 4хх кодов ответа от API и в первую очередь нам конечно интересны 401 и 403.

Заключение

Вот, пожалуй, и все, что я хотел написать.

Исходники тестового проекта закинул на гитхаб

Возможно, вам также будет интересно:

  • Проверьте чертеж и найдите 7 ошибок в нанесении размеров
  • Проверьте целостность файлов gta 5 ошибка стим
  • Проверьте файловую систему на наличие ошибок
  • Проверьте текст на стилистические ошибки
  • Проверьте текст на ошибки и знаки препинания онлайн

  • Понравилась статья? Поделить с друзьями:
    0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии