CINXE.COM

Django Tutorial Teil 10: Testen einer Django-Webanwendung - Webentwicklung lernen | MDN

<!doctype html><html lang="en-US" prefix="og: https://ogp.me/ns#"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="icon" href="https://developer.mozilla.org/favicon-48x48.bc390275e955dacb2e65.png"/><link rel="apple-touch-icon" href="https://developer.mozilla.org/apple-touch-icon.528534bba673c38049c2.png"/><meta name="theme-color" content="#ffffff"/><link rel="manifest" href="https://developer.mozilla.org/manifest.f42880861b394dd4dc9b.json"/><link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="MDN Web Docs"/><title>Django Tutorial Teil 10: Testen einer Django-Webanwendung - Webentwicklung lernen | MDN</title><link rel="alternate" title="Django Tutorial Part 10: Testing a Django web application" href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Testing" hrefLang="en"/><link rel="alternate" title="Tutorial de Django Parte 10: Probando una aplicación web Django" href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Testing" hrefLang="es"/><link rel="alternate" title="Django Tutorial Part 10: Testing a Django web application" href="https://developer.mozilla.org/fr/docs/Learn/Server-side/Django/Testing" hrefLang="fr"/><link rel="alternate" title="Django 튜토리얼 파트 10: Django 웹 어플리케이션 테스트하기" href="https://developer.mozilla.org/ko/docs/Learn/Server-side/Django/Testing" hrefLang="ko"/><link rel="alternate" title="Tutorial Django Parte 10: Testando uma aplicação web Django" href="https://developer.mozilla.org/pt-BR/docs/Learn/Server-side/Django/Testing" hrefLang="pt"/><link rel="alternate" title="Руководство часть 10: Тестирование приложений Django" href="https://developer.mozilla.org/ru/docs/Learn/Server-side/Django/Testing" hrefLang="ru"/><link rel="alternate" title="Django 教程 10: 测试 Django 网页应用" href="https://developer.mozilla.org/zh-CN/docs/Learn/Server-side/Django/Testing" hrefLang="zh"/><link rel="alternate" title="Django Tutorial Teil 10: Testen einer Django-Webanwendung" href="https://developer.mozilla.org/de/docs/Learn/Server-side/Django/Testing" hrefLang="de"/><link rel="preload" as="font" type="font/woff2" href="/static/media/Inter.var.c2fe3cb2b7c746f7966a.woff2" crossorigin=""/><link rel="alternate" type="application/rss+xml" title="MDN Blog RSS Feed" href="https://developer.mozilla.org/en-US/blog/rss.xml" hrefLang="en"/><meta name="description" content="Wenn Websites wachsen, werden sie schwieriger manuell zu testen. Nicht nur gibt es mehr zu testen, sondern auch, da die Interaktionen zwischen den Komponenten komplexer werden, kann eine kleine Änderung in einem Bereich andere Bereiche beeinflussen. Daher sind mehr Änderungen erforderlich, um sicherzustellen, dass alles weiterhin funktioniert und keine Fehler eingeführt werden, während mehr Änderungen vorgenommen werden. Eine Möglichkeit, diese Probleme zu mindern, ist das Schreiben automatisierter Tests, die bei jeder Änderung einfach und zuverlässig ausgeführt werden können. Dieses Tutorial zeigt, wie Sie mit Djangos Testframework das Unittesting Ihrer Website automatisieren können."/><meta property="og:url" content="https://developer.mozilla.org/de/docs/Learn/Server-side/Django/Testing"/><meta property="og:title" content="Django Tutorial Teil 10: Testen einer Django-Webanwendung - Webentwicklung lernen | MDN"/><meta property="og:type" content="website"/><meta property="og:locale" content="de"/><meta property="og:description" content="Wenn Websites wachsen, werden sie schwieriger manuell zu testen. Nicht nur gibt es mehr zu testen, sondern auch, da die Interaktionen zwischen den Komponenten komplexer werden, kann eine kleine Änderung in einem Bereich andere Bereiche beeinflussen. Daher sind mehr Änderungen erforderlich, um sicherzustellen, dass alles weiterhin funktioniert und keine Fehler eingeführt werden, während mehr Änderungen vorgenommen werden. Eine Möglichkeit, diese Probleme zu mindern, ist das Schreiben automatisierter Tests, die bei jeder Änderung einfach und zuverlässig ausgeführt werden können. Dieses Tutorial zeigt, wie Sie mit Djangos Testframework das Unittesting Ihrer Website automatisieren können."/><meta property="og:image" content="https://developer.mozilla.org/mdn-social-share.d893525a4fb5fb1f67a2.png"/><meta property="og:image:type" content="image/png"/><meta property="og:image:height" content="1080"/><meta property="og:image:width" content="1920"/><meta property="og:image:alt" content="The MDN Web Docs logo, featuring a blue accent color, displayed on a solid black background."/><meta property="og:site_name" content="MDN Web Docs"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:creator" content="MozDevNet"/><link rel="canonical" href="https://developer.mozilla.org/de/docs/Learn/Server-side/Django/Testing"/><style media="print">.article-actions-container,.document-toc-container,.language-menu,.main-menu-toggle,.on-github,.page-footer,.place,.sidebar,.top-banner,.top-navigation-main,ul.prev-next{display:none!important}.main-page-content,.main-page-content pre{padding:2px}.main-page-content pre{border-left-width:2px}</style><script src="/static/js/gtag.js" defer=""></script><script defer="" src="/static/js/main.9722bf44.js"></script><link href="/static/css/main.27d5b2a8.css" rel="stylesheet"/></head><body><script>if(document.body.addEventListener("load",(t=>{t.target.classList.contains("interactive")&&t.target.setAttribute("data-readystate","complete")}),{capture:!0}),window&&document.documentElement){const t={light:"#ffffff",dark:"#1b1b1b"};try{const e=window.localStorage.getItem("theme");e&&(document.documentElement.className=e,document.documentElement.style.backgroundColor=t[e]);const o=window.localStorage.getItem("nop");o&&(document.documentElement.dataset.nop=o)}catch(t){console.warn("Unable to read theme from localStorage",t)}}</script><div id="root"><ul id="nav-access" class="a11y-nav"><li><a id="skip-main" href="#content">Skip to main content</a></li><li><a id="skip-search" href="#top-nav-search-input">Skip to search</a></li><li><a id="skip-select-language" href="#languages-switcher-button">Skip to select language</a></li></ul><div class="page-wrapper category-learn document-page"><div class="top-banner loading"><section class="place top container"></section></div><div class="sticky-header-container"><header class="top-navigation "><div class="container "><div class="top-navigation-wrap"><a href="/de/" class="logo" aria-label="MDN homepage"><svg id="mdn-docs-logo" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 694.9 104.4" style="enable-background:new 0 0 694.9 104.4" xml:space="preserve" role="img"><title>MDN Web Docs</title><path d="M40.3 0 11.7 92.1H0L28.5 0h11.8zm10.4 0v92.1H40.3V0h10.4zM91 0 62.5 92.1H50.8L79.3 0H91zm10.4 0v92.1H91V0h10.4z" class="logo-m"></path><path d="M627.9 95.6h67v8.8h-67v-8.8z" class="logo-_"></path><path d="M367 42h-4l-10.7 30.8h-5.5l-10.8-26h-.4l-10.5 26h-5.2L308.7 42h-3.8v-5.6H323V42h-6.5l6.8 20.4h.4l10.3-26h4.7l11.2 26h.5l5.7-20.3h-6.2v-5.6H367V42zm34.9 20c-.4 3.2-2 5.9-4.7 8.2-2.8 2.3-6.5 3.4-11.3 3.4-5.4 0-9.7-1.6-13.1-4.7-3.3-3.2-5-7.7-5-13.7 0-5.7 1.6-10.3 4.7-14s7.4-5.5 12.9-5.5c5.1 0 9.1 1.6 11.9 4.7s4.3 6.9 4.3 11.3c0 1.5-.2 3-.5 4.7h-25.6c.3 7.7 4 11.6 10.9 11.6 2.9 0 5.1-.7 6.5-2 1.5-1.4 2.5-3 3-4.9l6 .9zM394 51.3c.2-2.4-.4-4.7-1.8-6.9s-3.8-3.3-7-3.3c-3.1 0-5.3 1-6.9 3-1.5 2-2.5 4.4-2.8 7.2H394zm51 2.4c0 5-1.3 9.5-4 13.7s-6.9 6.2-12.7 6.2c-6 0-10.3-2.2-12.7-6.7-.1.4-.2 1.4-.4 2.9s-.3 2.5-.4 2.9h-7.3c.3-1.7.6-3.5.8-5.3.3-1.8.4-3.7.4-5.5V22.3h-6v-5.6H416v27c1.1-2.2 2.7-4.1 4.7-5.7 2-1.6 4.8-2.4 8.4-2.4 4.6 0 8.4 1.6 11.4 4.7 3 3.2 4.5 7.6 4.5 13.4zm-7.7.6c0-4.2-1-7.4-3-9.5-2-2.2-4.4-3.3-7.4-3.3-3.4 0-6 1.2-8 3.7-1.9 2.4-2.9 5-3 7.7V57c0 3 1 5.6 3 7.7s4.5 3.1 7.6 3.1c3.6 0 6.3-1.3 8.1-3.9 1.8-2.7 2.7-5.9 2.7-9.6zm69.2 18.5h-13.2v-7.2c-1.2 2.2-2.8 4.1-4.9 5.6-2.1 1.6-4.8 2.4-8.3 2.4-4.8 0-8.7-1.6-11.6-4.9-2.9-3.2-4.3-7.7-4.3-13.3 0-5 1.3-9.6 4-13.7 2.6-4.1 6.9-6.2 12.8-6.2 5.7 0 9.8 2.2 12.3 6.5V22.3h-8.6v-5.6h15.8v50.6h6v5.5zM493.2 56v-4.4c-.1-3-1.2-5.5-3.2-7.3s-4.4-2.8-7.2-2.8c-3.6 0-6.3 1.3-8.2 3.9-1.9 2.6-2.8 5.8-2.8 9.6 0 4.1 1 7.3 3 9.5s4.5 3.3 7.4 3.3c3.2 0 5.8-1.3 7.8-3.8 2.1-2.6 3.1-5.3 3.2-8zm53.1-1.4c0 5.6-1.8 10.2-5.3 13.7s-8.2 5.3-13.9 5.3-10.1-1.7-13.4-5.1c-3.3-3.4-5-7.9-5-13.5 0-5.3 1.6-9.9 4.7-13.7 3.2-3.8 7.9-5.7 14.2-5.7s11 1.9 14.1 5.7c3 3.7 4.6 8.1 4.6 13.3zm-7.7-.2c0-4-1-7.2-3-9.5s-4.8-3.5-8.2-3.5c-3.6 0-6.4 1.2-8.3 3.7s-2.9 5.6-2.9 9.5c0 3.7.9 6.8 2.8 9.4 1.9 2.6 4.6 3.9 8.3 3.9 3.6 0 6.4-1.3 8.4-3.8 1.9-2.6 2.9-5.8 2.9-9.7zm45 5.8c-.4 3.2-1.9 6.3-4.4 9.1-2.5 2.9-6.4 4.3-11.8 4.3-5.2 0-9.4-1.6-12.6-4.8-3.2-3.2-4.8-7.7-4.8-13.7 0-5.5 1.6-10.1 4.7-13.9 3.2-3.8 7.6-5.7 13.2-5.7 2.3 0 4.6.3 6.7.8 2.2.5 4.2 1.5 6.2 2.9l1.5 9.5-5.9.7-1.3-6.1c-2.1-1.2-4.5-1.8-7.2-1.8-3.5 0-6.1 1.2-7.7 3.7-1.7 2.5-2.5 5.7-2.5 9.6 0 4.1.9 7.3 2.7 9.5 1.8 2.3 4.4 3.4 7.8 3.4 5.2 0 8.2-2.9 9.2-8.8l6.2 1.3zm34.7 1.9c0 3.6-1.5 6.5-4.6 8.5s-7 3-11.7 3c-5.7 0-10.6-1.2-14.6-3.6l1.2-8.8 5.7.6-.2 4.7c1.1.5 2.3.9 3.6 1.1s2.6.3 3.9.3c2.4 0 4.5-.4 6.5-1.3 1.9-.9 2.9-2.2 2.9-4.1 0-1.8-.8-3.1-2.3-3.8s-3.5-1.3-5.8-1.7-4.6-.9-6.9-1.4c-2.3-.6-4.2-1.6-5.7-2.9-1.6-1.4-2.3-3.5-2.3-6.3 0-4.1 1.5-6.9 4.6-8.5s6.4-2.4 9.9-2.4c2.6 0 5 .3 7.2.9 2.2.6 4.3 1.4 6.1 2.4l.8 8.8-5.8.7-.8-5.7c-2.3-1-4.7-1.6-7.2-1.6-2.1 0-3.7.4-5.1 1.1-1.3.8-2 2-2 3.8 0 1.7.8 2.9 2.3 3.6 1.5.7 3.4 1.2 5.7 1.6 2.2.4 4.5.8 6.7 1.4 2.2.6 4.1 1.6 5.7 3 1.4 1.6 2.2 3.7 2.2 6.6zM197.6 73.2h-17.1v-5.5h3.8V51.9c0-3.7-.7-6.3-2.1-7.9-1.4-1.6-3.3-2.3-5.7-2.3-3.2 0-5.6 1.1-7.2 3.4s-2.4 4.6-2.5 6.9v15.6h6v5.5h-17.1v-5.5h3.8V51.9c0-3.8-.7-6.4-2.1-7.9-1.4-1.5-3.3-2.3-5.6-2.3-3.2 0-5.5 1.1-7.2 3.3-1.6 2.2-2.4 4.5-2.5 6.9v15.8h6.9v5.5h-20.2v-5.5h6V42.4h-6.1v-5.6h13.4v6.4c1.2-2.1 2.7-3.8 4.7-5.2 2-1.3 4.4-2 7.3-2s5.3.7 7.5 2.1c2.2 1.4 3.7 3.5 4.5 6.4 1.1-2.5 2.7-4.5 4.9-6.1s4.8-2.4 7.9-2.4c3.5 0 6.5 1.1 8.9 3.3s3.7 5.6 3.7 10.2v18.2h6.1v5.5zm42.5 0h-13.2V66c-1.2 2.2-2.8 4.1-4.9 5.6-2.1 1.6-4.8 2.4-8.3 2.4-4.8 0-8.7-1.6-11.6-4.9-2.9-3.2-4.3-7.7-4.3-13.3 0-5 1.3-9.6 4-13.7 2.6-4.1 6.9-6.2 12.8-6.2s9.8 2.2 12.3 6.5V22.7h-8.6v-5.6h15.8v50.6h6v5.5zm-13.3-16.8V52c-.1-3-1.2-5.5-3.2-7.3s-4.4-2.8-7.2-2.8c-3.6 0-6.3 1.3-8.2 3.9-1.9 2.6-2.8 5.8-2.8 9.6 0 4.1 1 7.3 3 9.5s4.5 3.3 7.4 3.3c3.2 0 5.8-1.3 7.8-3.8 2.1-2.6 3.1-5.3 3.2-8zm61.5 16.8H269v-5.5h6V51.9c0-3.7-.7-6.3-2.2-7.9-1.4-1.6-3.4-2.3-5.7-2.3-3.1 0-5.6 1-7.4 3s-2.8 4.4-2.9 7v15.9h6v5.5h-19.3v-5.5h6V42.4h-6.2v-5.6h13.6V43c2.6-4.6 6.8-6.9 12.7-6.9 3.6 0 6.7 1.1 9.2 3.3s3.7 5.6 3.7 10.2v18.2h6v5.4h-.2z" class="logo-text"></path></svg></a><button title="Open main menu" type="button" class="button action has-icon main-menu-toggle" aria-haspopup="menu" aria-label="Open main menu" aria-expanded="false"><span class="button-wrap"><span class="icon icon-menu "></span><span class="visually-hidden">Open main menu</span></span></button></div><div class="top-navigation-main"><nav class="main-nav" aria-label="Main menu"><ul class="main-menu nojs"><li class="top-level-entry-container "><button type="button" id="references-button" class="top-level-entry menu-toggle" aria-controls="references-menu" aria-expanded="false">References</button><a href="/de/docs/Web" class="top-level-entry">References</a><ul id="references-menu" class="submenu references hidden inline-submenu-lg" aria-labelledby="references-button"><li class="apis-link-container mobile-only "><a href="/de/docs/Web" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview / Web Technology</div><p class="submenu-item-description">Web technology reference for developers</p></div></a></li><li class="html-link-container "><a href="/de/docs/Web/HTML" class="submenu-item "><div class="submenu-icon html"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTML</div><p class="submenu-item-description">Structure of content on the web</p></div></a></li><li class="css-link-container "><a href="/de/docs/Web/CSS" class="submenu-item "><div class="submenu-icon css"></div><div class="submenu-content-container"><div class="submenu-item-heading">CSS</div><p class="submenu-item-description">Code used to describe document style</p></div></a></li><li class="javascript-link-container "><a href="/de/docs/Web/JavaScript" class="submenu-item "><div class="submenu-icon javascript"></div><div class="submenu-content-container"><div class="submenu-item-heading">JavaScript</div><p class="submenu-item-description">General-purpose scripting language</p></div></a></li><li class="http-link-container "><a href="/de/docs/Web/HTTP" class="submenu-item "><div class="submenu-icon http"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTTP</div><p class="submenu-item-description">Protocol for transmitting web resources</p></div></a></li><li class="apis-link-container "><a href="/de/docs/Web/API" class="submenu-item "><div class="submenu-icon apis"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web APIs</div><p class="submenu-item-description">Interfaces for building web applications</p></div></a></li><li class="apis-link-container "><a href="/de/docs/Mozilla/Add-ons/WebExtensions" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web Extensions</div><p class="submenu-item-description">Developing extensions for web browsers</p></div></a></li><li class="apis-link-container desktop-only "><a href="/de/docs/Web" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Web Technology</div><p class="submenu-item-description">Web technology reference for developers</p></div></a></li></ul></li><li class="top-level-entry-container active"><button type="button" id="guides-button" class="top-level-entry menu-toggle" aria-controls="guides-menu" aria-expanded="false">Guides</button><a href="/de/docs/Learn" class="top-level-entry">Guides</a><ul id="guides-menu" class="submenu guides hidden inline-submenu-lg" aria-labelledby="guides-button"><li class="apis-link-container mobile-only "><a href="/de/docs/Learn" class="submenu-item "><div class="submenu-icon learn"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview / MDN Learning Area</div><p class="submenu-item-description">Learn web development</p></div></a></li><li class="apis-link-container desktop-only "><a href="/de/docs/Learn" class="submenu-item "><div class="submenu-icon learn"></div><div class="submenu-content-container"><div class="submenu-item-heading">MDN Learning Area</div><p class="submenu-item-description">Learn web development</p></div></a></li><li class="html-link-container "><a href="/de/docs/Learn/HTML" class="submenu-item "><div class="submenu-icon html"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTML</div><p class="submenu-item-description">Learn to structure web content with HTML</p></div></a></li><li class="css-link-container "><a href="/de/docs/Learn/CSS" class="submenu-item "><div class="submenu-icon css"></div><div class="submenu-content-container"><div class="submenu-item-heading">CSS</div><p class="submenu-item-description">Learn to style content using CSS</p></div></a></li><li class="javascript-link-container "><a href="/de/docs/Learn/JavaScript" class="submenu-item "><div class="submenu-icon javascript"></div><div class="submenu-content-container"><div class="submenu-item-heading">JavaScript</div><p class="submenu-item-description">Learn to run scripts in the browser</p></div></a></li><li class=" "><a href="/de/docs/Web/Accessibility" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Accessibility</div><p class="submenu-item-description">Learn to make the web accessible to all</p></div></a></li></ul></li><li class="top-level-entry-container "><button type="button" id="mdn-plus-button" class="top-level-entry menu-toggle" aria-controls="mdn-plus-menu" aria-expanded="false">Plus</button><a href="/de/plus" class="top-level-entry">Plus</a><ul id="mdn-plus-menu" class="submenu mdn-plus hidden inline-submenu-lg" aria-labelledby="mdn-plus-button"><li class=" "><a href="/de/plus" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Overview</div><p class="submenu-item-description">A customized MDN experience</p></div></a></li><li class=" "><a href="/de/plus/ai-help" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">AI Help</div><p class="submenu-item-description">Get real-time assistance and support</p></div></a></li><li class=" "><a href="/de/plus/updates" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Updates</div><p class="submenu-item-description">All browser compatibility updates at a glance</p></div></a></li><li class=" "><a href="/en-US/plus/docs/features/overview" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Documentation</div><p class="submenu-item-description">Learn how to use MDN Plus</p></div></a></li><li class=" "><a href="/en-US/plus/docs/faq" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">FAQ</div><p class="submenu-item-description">Frequently asked questions about MDN Plus</p></div></a></li></ul></li><li class="top-level-entry-container "><a class="top-level-entry menu-link" href="/en-US/curriculum/">Curriculum <sup class="new">New</sup></a></li><li class="top-level-entry-container "><a class="top-level-entry menu-link" href="/en-US/blog/">Blog</a></li><li class="top-level-entry-container "><button type="button" id="tools-button" class="top-level-entry menu-toggle" aria-controls="tools-menu" aria-expanded="false">Tools</button><ul id="tools-menu" class="submenu tools hidden inline-submenu-lg" aria-labelledby="tools-button"><li class=" "><a href="/de/play" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">Playground</div><p class="submenu-item-description">Write, test and share your code</p></div></a></li><li class=" "><a href="/en-US/observatory" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">HTTP Observatory</div><p class="submenu-item-description">Scan a website for free</p></div></a></li><li class=" "><a href="/en-US/plus/ai-help" class="submenu-item "><div class="submenu-icon"></div><div class="submenu-content-container"><div class="submenu-item-heading">AI Help</div><p class="submenu-item-description">Get real-time assistance and support</p></div></a></li></ul></li></ul></nav><div class="header-search"><form action="/de/search" class="search-form search-widget" id="top-nav-search-form" role="search"><label id="top-nav-search-label" for="top-nav-search-input" class="visually-hidden">Search MDN</label><input aria-activedescendant="" aria-autocomplete="list" aria-controls="top-nav-search-menu" aria-expanded="false" aria-labelledby="top-nav-search-label" autoComplete="off" id="top-nav-search-input" role="combobox" type="search" class="search-input-field" name="q" placeholder="   " required="" value=""/><button type="button" class="button action has-icon clear-search-button"><span class="button-wrap"><span class="icon icon-cancel "></span><span class="visually-hidden">Clear search input</span></span></button><button type="submit" class="button action has-icon search-button"><span class="button-wrap"><span class="icon icon-search "></span><span class="visually-hidden">Search</span></span></button><div id="top-nav-search-menu" role="listbox" aria-labelledby="top-nav-search-label"></div></form></div><div class="theme-switcher-menu"><button type="button" class="button action has-icon theme-switcher-menu small" aria-haspopup="menu"><span class="button-wrap"><span class="icon icon-theme-os-default "></span>Theme</span></button></div><ul class="auth-container"><li><a href="/users/fxa/login/authenticate/?next=%2Fde%2Fdocs%2FLearn%2FServer-side%2FDjango%2FTesting" class="login-link" rel="nofollow">Log in</a></li><li><a href="/users/fxa/login/authenticate/?next=%2Fde%2Fdocs%2FLearn%2FServer-side%2FDjango%2FTesting" target="_self" rel="nofollow" class="button primary mdn-plus-subscribe-link"><span class="button-wrap">Sign up for free</span></a></li></ul></div></div></header><div class="article-actions-container"><div class="container"><button type="button" class="button action has-icon sidebar-button" aria-label="Expand sidebar" aria-expanded="false" aria-controls="sidebar-quicklinks"><span class="button-wrap"><span class="icon icon-sidebar "></span></span></button><nav class="breadcrumbs-container" aria-label="Breadcrumb"><ol typeof="BreadcrumbList" vocab="https://schema.org/" aria-label="breadcrumbs"><li property="itemListElement" typeof="ListItem"><a href="/de/docs/Learn" class="breadcrumb" property="item" typeof="WebPage"><span property="name">Webentwicklung lernen</span></a><meta property="position" content="1"/></li><li property="itemListElement" typeof="ListItem"><a href="/de/docs/Learn/Server-side" class="breadcrumb" property="item" typeof="WebPage"><span property="name">Server-side-Website-Programmierung</span></a><meta property="position" content="2"/></li><li property="itemListElement" typeof="ListItem"><a href="/de/docs/Learn/Server-side/Django" class="breadcrumb" property="item" typeof="WebPage"><span property="name">Django Web Framework (Python)</span></a><meta property="position" content="3"/></li><li property="itemListElement" typeof="ListItem"><a href="/de/docs/Learn/Server-side/Django/Testing" class="breadcrumb-current-page" property="item" typeof="WebPage"><span property="name">Django Tutorial Teil 10: Testen einer Django-Webanwendung</span></a><meta property="position" content="4"/></li></ol></nav><div class="article-actions"><button type="button" class="button action has-icon article-actions-toggle" aria-label="Article actions"><span class="button-wrap"><span class="icon icon-ellipses "></span><span class="article-actions-dialog-heading">Article Actions</span></span></button><ul class="article-actions-entries"><li class="article-actions-entry"><div class="languages-switcher-menu open-on-focus-within"><button id="languages-switcher-button" type="button" class="button action small has-icon languages-switcher-menu" aria-haspopup="menu"><span class="button-wrap"><span class="icon icon-language "></span>Deutsch<span title="Diese Übersetzung ist Teil eines Experiments."><span class="icon icon-experimental "></span></span></span></button><div class="hidden"><ul class="submenu language-menu " aria-labelledby="language-menu-button"><li class=" "><form class="submenu-item locale-redirect-setting"><div class="group"><label class="switch"><input type="checkbox" name="locale-redirect"/><span class="slider"></span><span class="label">Remember language</span></label><a href="https://github.com/orgs/mdn/discussions/739" rel="external noopener noreferrer" target="_blank" title="Enable this setting to automatically switch to this language when it&#x27;s available. (Click to learn more.)"><span class="icon icon-question-mark "></span></a></div></form></li><li class=" "><a data-locale="en-US" href="/en-US/docs/Learn/Server-side/Django/Testing" class="button submenu-item"><span>English (US)</span></a></li><li class=" "><a data-locale="es" href="/es/docs/Learn/Server-side/Django/Testing" class="button submenu-item"><span>Español</span></a></li><li class=" "><a data-locale="fr" href="/fr/docs/Learn/Server-side/Django/Testing" class="button submenu-item"><span>Français</span></a></li><li class=" "><a data-locale="ko" href="/ko/docs/Learn/Server-side/Django/Testing" class="button submenu-item"><span>한국어</span></a></li><li class=" "><a data-locale="pt-BR" href="/pt-BR/docs/Learn/Server-side/Django/Testing" class="button submenu-item"><span>Português (do Brasil)</span></a></li><li class=" "><a data-locale="ru" href="/ru/docs/Learn/Server-side/Django/Testing" class="button submenu-item"><span>Русский</span></a></li><li class=" "><a data-locale="zh-CN" href="/zh-CN/docs/Learn/Server-side/Django/Testing" class="button submenu-item"><span>中文 (简体)</span></a></li></ul></div></div></li></ul></div></div></div></div><div class="container"><div class="notecard experimental localized-content-note"><p><a href="https://github.com/orgs/mdn/discussions/741" class="external"><strong>Experiment</strong>: Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten.</a></p></div></div><div class="main-wrapper"><div class="sidebar-container"><aside id="sidebar-quicklinks" class="sidebar" data-macro="LearnSidebar"><button type="button" class="button action backdrop" aria-label="Collapse sidebar"><span class="button-wrap"></span></button><nav aria-label="Related Topics" class="sidebar-inner"><header class="sidebar-actions"><section class="sidebar-filter-container"><div class="sidebar-filter "><label id="sidebar-filter-label" class="sidebar-filter-label" for="sidebar-filter-input"><span class="icon icon-filter"></span><span class="visually-hidden">Filter sidebar</span></label><input id="sidebar-filter-input" autoComplete="off" class="sidebar-filter-input-field false" type="text" placeholder="Filter" value=""/><button type="button" class="button action has-icon clear-sidebar-filter-button"><span class="button-wrap"><span class="icon icon-cancel "></span><span class="visually-hidden">Clear filter input</span></span></button></div></section></header><div class="sidebar-inner-nav"><div class="in-nav-toc"><div class="document-toc-container"><section class="document-toc"><header><h2 class="document-toc-heading">In diesem Artikel</h2></header><ul class="document-toc-list"><li class="document-toc-item "><a class="document-toc-link" href="#überblick">Überblick</a></li><li class="document-toc-item "><a class="document-toc-link" href="#überblick_über_die_teststruktur">Überblick über die Teststruktur</a></li><li class="document-toc-item "><a class="document-toc-link" href="#wie_führt_man_die_tests_aus">Wie führt man die Tests aus</a></li><li class="document-toc-item "><a class="document-toc-link" href="#locallibrary_tests">LocalLibrary Tests</a></li><li class="document-toc-item "><a class="document-toc-link" href="#weitere_empfohlene_testwerkzeuge">Weitere empfohlene Testwerkzeuge</a></li><li class="document-toc-item "><a class="document-toc-link" href="#fordern_sie_sich_selbst_heraus">Fordern Sie sich selbst heraus</a></li><li class="document-toc-item "><a class="document-toc-link" href="#zusammenfassung">Zusammenfassung</a></li><li class="document-toc-item "><a class="document-toc-link" href="#siehe_auch">Siehe auch</a></li></ul></section></div></div><div class="sidebar-body"><ol><li class="section"><a href="/de/docs/Learn/Getting_started_with_the_web">Komplette Anfänger beginnen hier!</a></li><li><details><summary>Erste Schritte mit dem Web</summary><ol><li><a href="/de/docs/Learn/Getting_started_with_the_web">Einführung ins Web</a></li><li><a href="/de/docs/Learn/Getting_started_with_the_web/Installing_basic_software">Installation von grundlegender Software</a></li><li><a href="/de/docs/Learn/Getting_started_with_the_web/What_will_your_website_look_like">Wie wird Ihre Website aussehen?</a></li><li><a href="/de/docs/Learn/Getting_started_with_the_web/Dealing_with_files">Umgang mit Dateien</a></li><li><a href="/de/docs/Learn/Getting_started_with_the_web/HTML_basics">HTML-Grundlagen</a></li><li><a href="/de/docs/Learn/Getting_started_with_the_web/CSS_basics">CSS-Grundlagen</a></li><li><a href="/de/docs/Learn/Getting_started_with_the_web/JavaScript_basics">JavaScript-Grundlagen</a></li><li><a href="/de/docs/Learn/Getting_started_with_the_web/Publishing_your_website">Veröffentlichung Ihrer Website</a></li><li><a href="/de/docs/Learn/Getting_started_with_the_web/How_the_Web_works">Wie das Web funktioniert</a></li></ol></details></li><li class="section"><a href="/de/docs/Learn/HTML">HTML — Strukturierung des Webs</a></li><li><details><summary>Einführung in HTML</summary><ol><li><a href="/de/docs/Learn/HTML/Introduction_to_HTML">Einführung in HTML</a></li><li><a href="/de/docs/Learn/HTML/Introduction_to_HTML/Getting_started">Erste Schritte mit HTML</a></li><li><a href="/de/docs/Learn/HTML/Introduction_to_HTML/The_head_metadata_in_HTML">Was ist im Kopfbereich? Metadaten in HTML</a></li><li><a href="/de/docs/Learn/HTML/Introduction_to_HTML/HTML_text_fundamentals">Grundlagen des HTML-Textes</a></li><li><a href="/de/docs/Learn/HTML/Introduction_to_HTML/Creating_hyperlinks">Erstellen von Hyperlinks</a></li><li><a href="/de/docs/Learn/HTML/Introduction_to_HTML/Advanced_text_formatting">Erweiterte Textformatierung</a></li><li><a href="/de/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure">Struktur eines Dokuments und einer Website</a></li><li><a href="/de/docs/Learn/HTML/Introduction_to_HTML/Debugging_HTML">Debugging HTML</a></li><li><a href="/de/docs/Learn/HTML/Introduction_to_HTML/Marking_up_a_letter">Markierung eines Briefes</a></li><li><a href="/de/docs/Learn/HTML/Introduction_to_HTML/Structuring_a_page_of_content">Die Strukturierung einer Seite mit Inhalt</a></li></ol></details></li><li><details><summary>Multimedia und Einbettung</summary><ol><li><a href="/de/docs/Learn/HTML/Multimedia_and_embedding">Multimedia und Einbettung</a></li><li><a href="/de/docs/Learn/HTML/Multimedia_and_embedding/Images_in_HTML">Bilder in HTML</a></li><li><a href="/de/docs/Learn/HTML/Multimedia_and_embedding/Video_and_audio_content">Video- und Audioinhalte</a></li><li><a href="/de/docs/Learn/HTML/Multimedia_and_embedding/Other_embedding_technologies">Von Objekt zu iframe — andere Einbettungstechnologien</a></li><li><a href="/de/docs/Learn/HTML/Multimedia_and_embedding/Adding_vector_graphics_to_the_Web">Vektorgrafiken zum Web hinzufügen</a></li><li><a href="/de/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images">Responsive Images</a></li><li><a href="/de/docs/Learn/HTML/Multimedia_and_embedding/Mozilla_splash_page">Mozilla Splash-Seite</a></li></ol></details></li><li><details><summary>HTML-Tabellen</summary><ol><li><a href="/de/docs/Learn/HTML/Tables">HTML-Tabellen</a></li><li><a href="/de/docs/Learn/HTML/Tables/Basics">HTML Table Grundlagen</a></li><li><a href="/de/docs/Learn/HTML/Tables/Advanced">Erweiterte Funktionen und Barrierefreiheit von HTML-Tabellen</a></li><li><a href="/de/docs/Learn/HTML/Tables/Structuring_planet_data">Strukturierung von Planeten-Daten</a></li></ol></details></li><li class="section"><a href="/de/docs/Learn/CSS">CSS — Gestaltung des Webs</a></li><li><details><summary>CSS erste Schritte</summary><ol><li><a href="/de/docs/Learn/CSS/First_steps">Erste Schritte mit CSS</a></li><li><a href="/de/docs/Learn/CSS/First_steps/What_is_CSS">Was ist CSS?</a></li><li><a href="/de/docs/Learn/CSS/First_steps/Getting_started">Einstieg in CSS</a></li><li><a href="/de/docs/Learn/CSS/First_steps/How_CSS_is_structured">Wie CSS strukturiert ist</a></li><li><a href="/de/docs/Learn/CSS/First_steps/How_CSS_works">Wie CSS funktioniert</a></li><li><a href="/de/docs/Learn/CSS/First_steps/Styling_a_biography_page">Eine Biografieseite stylen</a></li></ol></details></li><li><details><summary>CSS-Bausteine</summary><ol><li><a href="/de/docs/Learn/CSS/Building_blocks">CSS-Grundbausteine</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Selectors">CSS-Selektoren</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Selectors/Type_Class_and_ID_Selectors">Typ-, Klassen- und ID-Selektoren</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Selectors/Attribute_selectors">Attributselektoren</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Selectors/Pseudo-classes_and_pseudo-elements">Pseudo-Klassen und Pseudo-Elemente</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Selectors/Combinators">Kombinatoren</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Cascade_and_inheritance">Kaskade, Spezifität und Vererbung</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Cascade_layers">Kaskadenschichten</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/The_box_model">Das Boxmodell</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Backgrounds_and_borders">Hintergründe und Rahmen</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Handling_different_text_directions">Umgang mit unterschiedlichen Textausrichtungen</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Overflowing_content">Überlaufender Inhalt</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Values_and_units">CSS-Werte und Einheiten</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Sizing_items_in_CSS">Größe von Elementen in CSS</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Images_media_form_elements">Bilder, Medien und Formularelemente</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Styling_tables">Tabellen stylen</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Advanced_styling_effects">Erweiterte Styling-Effekte</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Debugging_CSS">Debugging CSS</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Organizing">Organisieren Ihres CSS</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Fundamental_CSS_comprehension">Grundlegendes CSS-Verständnis</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/Creating_fancy_letterheaded_paper">Erstellen von stilvollem Briefpapier</a></li><li><a href="/de/docs/Learn/CSS/Building_blocks/A_cool_looking_box">Eine cool aussehende Box</a></li></ol></details></li><li><details><summary>Textgestaltung</summary><ol><li><a href="/de/docs/Learn/CSS/Styling_text">CSS Textgestaltung</a></li><li><a href="/de/docs/Learn/CSS/Styling_text/Fundamentals">Grundlegende Text- und Schriftgestaltung</a></li><li><a href="/de/docs/Learn/CSS/Styling_text/Styling_lists">Listen stilisieren</a></li><li><a href="/de/docs/Learn/CSS/Styling_text/Styling_links">Styling von Links</a></li><li><a href="/de/docs/Learn/CSS/Styling_text/Web_fonts">Web-Fonts</a></li><li><a href="/de/docs/Learn/CSS/Styling_text/Typesetting_a_homepage">Setzen einer Community-Schul-Startseite</a></li></ol></details></li><li><details><summary>CSS-Layout</summary><ol><li><a href="/de/docs/Learn/CSS/CSS_layout">CSS-Layout</a></li><li><a href="/de/docs/Learn/CSS/CSS_layout/Introduction">Einführung in CSS Layout</a></li><li><a href="/de/docs/Learn/CSS/CSS_layout/Normal_Flow">Normaler Fluss</a></li><li><a href="/de/docs/Learn/CSS/CSS_layout/Flexbox">Flexbox</a></li><li><a href="/de/docs/Learn/CSS/CSS_layout/Grids">Raster</a></li><li><a href="/de/docs/Learn/CSS/CSS_layout/Floats">Floats</a></li><li><a href="/de/docs/Learn/CSS/CSS_layout/Positioning">Positioning</a></li><li><a href="/de/docs/Learn/CSS/CSS_layout/Multiple-column_Layout">Mehrspaltiges Layout</a></li><li><a href="/de/docs/Learn/CSS/CSS_layout/Responsive_Design">Responsives Design</a></li><li><a href="/de/docs/Learn/CSS/CSS_layout/Media_queries">Einsteigerleitfaden für Media Queries</a></li><li><a href="/de/docs/Learn/CSS/CSS_layout/Legacy_Layout_Methods">Legacy-Layout-Methoden</a></li><li><a href="/de/docs/Learn/CSS/CSS_layout/Supporting_Older_Browsers">Unterstützung älterer Browser</a></li><li><a href="/de/docs/Learn/CSS/CSS_layout/Fundamental_Layout_Comprehension">Grundlegendes Verständnis von Layouts</a></li></ol></details></li><li class="section"><a href="/de/docs/Learn/JavaScript">JavaScript — Dynamisches clientseitiges Skripting</a></li><li><details><summary>JavaScript erste Schritte</summary><ol><li><a href="/de/docs/Learn/JavaScript/First_steps">JavaScript erste Schritte</a></li><li><a href="/de/docs/Learn/JavaScript/First_steps/What_is_JavaScript">Was ist JavaScript?</a></li><li><a href="/de/docs/Learn/JavaScript/First_steps/A_first_splash">Ein erster Sprung in JavaScript</a></li><li><a href="/de/docs/Learn/JavaScript/First_steps/What_went_wrong">Was ist schiefgelaufen? JavaScript-Fehlerbehebung</a></li><li><a href="/de/docs/Learn/JavaScript/First_steps/Variables">Speichern der benötigten Informationen — Variablen</a></li><li><a href="/de/docs/Learn/JavaScript/First_steps/Math">Grundlegende Mathematik in JavaScript – Zahlen und Operatoren</a></li><li><a href="/de/docs/Learn/JavaScript/First_steps/Strings">Umgang mit Text — Strings in JavaScript</a></li><li><a href="/de/docs/Learn/JavaScript/First_steps/Useful_string_methods">Nützliche String-Methoden</a></li><li><a href="/de/docs/Learn/JavaScript/First_steps/Arrays">Arrays</a></li><li><a href="/de/docs/Learn/JavaScript/First_steps/Silly_story_generator">Silly Story Generator</a></li></ol></details></li><li><details><summary>JavaScript-Bausteine</summary><ol><li><a href="/de/docs/Learn/JavaScript/Building_blocks">JavaScript-Bausteine</a></li><li><a href="/de/docs/Learn/JavaScript/Building_blocks/conditionals">Entscheidungen in Ihrem Code treffen — Bedingte Anweisungen</a></li><li><a href="/de/docs/Learn/JavaScript/Building_blocks/Looping_code">Schleifen-Code</a></li><li><a href="/de/docs/Learn/JavaScript/Building_blocks/Functions">Funktionen — wiederverwendbare Codeblöcke</a></li><li><a href="/de/docs/Learn/JavaScript/Building_blocks/Build_your_own_function">Erstellen Sie Ihre eigene Funktion</a></li><li><a href="/de/docs/Learn/JavaScript/Building_blocks/Return_values">Funktionsrückgabewerte</a></li><li><a href="/de/docs/Learn/JavaScript/Building_blocks/Events">Einführung in Ereignisse</a></li><li><a href="/de/docs/Learn/JavaScript/Building_blocks/Event_bubbling">Event bubbling</a></li><li><a href="/de/docs/Learn/JavaScript/Building_blocks/Image_gallery">Bildgalerie</a></li></ol></details></li><li><details><summary>Einführung in JavaScript-Objekte</summary><ol><li><a href="/de/docs/Learn/JavaScript/Objects">Einführung in JavaScript-Objekte</a></li><li><a href="/de/docs/Learn/JavaScript/Objects/Basics">JavaScript Objekt Grundlagen</a></li><li><a href="/de/docs/Learn/JavaScript/Objects/Object_prototypes">Objektprototypen</a></li><li><a href="/de/docs/Learn/JavaScript/Objects/Object-oriented_programming">Objektorientierte Programmierung</a></li><li><a href="/de/docs/Learn/JavaScript/Objects/Classes_in_JavaScript">Klassen in JavaScript</a></li><li><a href="/de/docs/Learn/JavaScript/Objects/JSON">Arbeiten mit JSON</a></li><li><a href="/de/docs/Learn/JavaScript/Objects/Object_building_practice">Objekt-Baupraxis</a></li><li><a href="/de/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features">Hinzufügen von Funktionen zu unserem hüpfenden Kugeln-Demo</a></li></ol></details></li><li><details><summary>Asynchrones JavaScript</summary><ol><li><a href="/de/docs/Learn/JavaScript/Asynchronous">Asynchrones JavaScript</a></li><li><a href="/de/docs/Learn/JavaScript/Asynchronous/Introducing">Einführung in asynchrones JavaScript</a></li><li><a href="/de/docs/Learn/JavaScript/Asynchronous/Promises">Anleitung zur Verwendung von Promises</a></li><li><a href="/de/docs/Learn/JavaScript/Asynchronous/Implementing_a_promise-based_API">Anleitung zur Implementierung einer Promise-basierten API</a></li><li><a href="/de/docs/Learn/JavaScript/Asynchronous/Introducing_workers">Einführung in Workers</a></li><li><a href="/de/docs/Learn/JavaScript/Asynchronous/Sequencing_animations">Sequenzierung von Animationen</a></li></ol></details></li><li><details><summary>Client-seitige Web-APIs</summary><ol><li><a href="/de/docs/Learn/JavaScript/Client-side_web_APIs">Client-Side-Web-APIs</a></li><li><a href="/de/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">Einführung in Web-APIs</a></li><li><a href="/de/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">Manipulating documents</a></li><li><a href="/de/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">Abrufen von Daten vom Server</a></li><li><a href="/de/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs">Third-party APIs</a></li><li><a href="/de/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Grafiken zeichnen</a></li><li><a href="/de/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs">Video- und Audio-APIs</a></li><li><a href="/de/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">Client-side storage</a></li></ol></details></li><li class="section"><a href="/de/docs/Learn/Forms">Webformulare — Arbeiten mit Benutzerdaten</a></li><li><details><summary>Grundlagen der Webformulare</summary><ol><li><a href="/de/docs/Learn/Forms">Bausteine für Webformulare</a></li><li><a href="/de/docs/Learn/Forms/Your_first_form">Ihr erstes Formular</a></li><li><a href="/de/docs/Learn/Forms/How_to_structure_a_web_form">Wie Sie ein Webformular strukturieren</a></li><li><a href="/de/docs/Learn/Forms/Basic_native_form_controls">Basis-Native Formularelemente</a></li><li><a href="/de/docs/Learn/Forms/HTML5_input_types">Die HTML5 input Typen</a></li><li><a href="/de/docs/Learn/Forms/Other_form_controls">Andere Formularelemente</a></li><li><a href="/de/docs/Learn/Forms/Styling_web_forms">Styling von Webformularen</a></li><li><a href="/de/docs/Learn/Forms/Advanced_form_styling">Erweiterte Formular-Stilgestaltung</a></li><li><a href="/de/docs/Learn/Forms/UI_pseudo-classes">UI-Pseudo-Klassen</a></li><li><a href="/de/docs/Learn/Forms/Form_validation">Client-seitige Formularvalidierung</a></li><li><a href="/de/docs/Learn/Forms/Sending_and_retrieving_form_data">Senden von Formulardaten</a></li></ol></details></li><li><details><summary>Erweiterte Techniken für Webformulare</summary><ol><li><a href="/de/docs/Learn/Forms/How_to_build_custom_form_controls">Anleitung zur Erstellung benutzerdefinierter Formularelemente</a></li><li><a href="/de/docs/Learn/Forms/Sending_forms_through_JavaScript">Versenden von Formularen über JavaScript</a></li><li><a href="/de/docs/Learn/Forms/Property_compatibility_table_for_form_controls">CSS-Eigenschaftskompatibilitätstabelle für Formularelemente</a></li><li><a href="/de/docs/Learn/Forms/HTML_forms_in_legacy_browsers">HTML-Formulare in älteren Browsern</a></li></ol></details></li><li class="section"><a href="/de/docs/Learn/Accessibility">Barrierefreiheit — Das Web für alle nutzbar machen</a></li><li><details><summary>Barrierefreiheitsleitfäden</summary><ol><li><a href="/de/docs/Learn/Accessibility">Barrierefreiheit</a></li><li><a href="/de/docs/Learn/Accessibility/What_is_accessibility">Was ist Barrierefreiheit?</a></li><li><a href="/de/docs/Learn/Accessibility/HTML">HTML: Eine gute Grundlage für Barrierefreiheit</a></li><li><a href="/de/docs/Learn/Accessibility/CSS_and_JavaScript">CSS und JavaScript: Barrierefreiheits-Best Practices</a></li><li><a href="/de/docs/Learn/Accessibility/WAI-ARIA_basics">Grundlagen von WAI-ARIA</a></li><li><a href="/de/docs/Learn/Accessibility/Multimedia">Barrierefreie Multimedia</a></li><li><a href="/de/docs/Learn/Accessibility/Mobile">Barrierefreiheit auf Mobilgeräten</a></li><li><a href="/de/docs/Learn/Accessibility/Accessibility_troubleshooting">Bewertung: Barrierefreiheits-Troubleshooting</a></li></ol></details></li><li class="section"><a href="/de/docs/Learn/Performance">Leistung — Websites schnell und reaktionsschnell machen</a></li><li><details><summary>Leitfäden zur Leistung</summary><ol><li><a href="/de/docs/Learn/Performance">Webleistung</a></li><li><a href="/de/docs/Learn/Performance/why_web_performance">Der 'Warum' der Web-Performance</a></li><li><a href="/de/docs/Learn/Performance/What_is_web_performance">Was ist Web-Performance?</a></li><li><a href="/de/docs/Learn/Performance/Perceived_performance">Wahrgenommene Leistung</a></li><li><a href="/de/docs/Learn/Performance/Measuring_performance">Messung der Performance</a></li><li><a href="/de/docs/Learn/Performance/Multimedia">Multimedia: Bilder</a></li><li><a href="/de/docs/Learn/Performance/video">Multimedia: Video</a></li><li><a href="/de/docs/Learn/Performance/JavaScript">JavaScript-Leistungsoptimierung</a></li><li><a href="/de/docs/Learn/Performance/HTML">HTML-Leistungsoptimierung</a></li><li><a href="/de/docs/Learn/Performance/CSS">CSS-Leistungsoptimierung</a></li><li><a href="/de/docs/Learn/Performance/business_case_for_performance">Der geschäftliche Nutzen von Web-Performance</a></li></ol></details></li><li class="section"><a href="/de/docs/Learn/MathML">MathML — Schreiben von Mathematik mit MathML</a></li><li><details><summary>MathML erste Schritte</summary><ol><li><a href="/de/docs/Learn/MathML/First_steps">Erste Schritte mit MathML</a></li><li><a href="/de/docs/Learn/MathML/First_steps/Getting_started">Erste Schritte mit MathML</a></li><li><a href="/de/docs/Learn/MathML/First_steps/Text_containers">MathML Text-Container</a></li><li><a href="/de/docs/Learn/MathML/First_steps/Fractions_and_roots">MathML-Brüche und -Wurzeln</a></li><li><a href="/de/docs/Learn/MathML/First_steps/Scripts">MathML gescriptete Elemente</a></li><li><a href="/de/docs/Learn/MathML/First_steps/Tables">MathML-Tabellen</a></li><li><a href="/de/docs/Learn/MathML/First_steps/Three_famous_mathematical_formulas">Drei berühmte mathematische Formeln</a></li></ol></details></li><li class="section"><a href="/de/docs/Learn/../Games">Spiele — Entwicklung von Spielen für das Web</a></li><li><details><summary>Anleitungen und Tutorials</summary><ol><li><a href="/de/docs/Games/Introduction">Einführung in die Spieleentwicklung für das Web</a></li><li><a href="/de/docs/Games/Techniques">Techniken für die Spieleentwicklung</a></li><li><a href="/de/docs/Games/Tutorials">Tutorials</a></li><li><a href="/de/docs/Games/Publishing_games">Publishing Games</a></li></ol></details></li><li class="section"><a href="/de/docs/Learn/Tools_and_testing">Werkzeuge und Tests</a></li><li><details><summary>Client-seitige Webentwicklungstools</summary><ol><li><a href="/de/docs/Learn/Tools_and_testing/Understanding_client-side_tools">Verständnis von Client-seitigen Webentwicklungstools</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview">Übersicht über clientseitige Werkzeuge</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Command_line">Crashkurs zur Kommandozeile</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Package_management">Grundlagen des Paketmanagements</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Introducing_complete_toolchain">Einführung in eine vollständige Toolchain</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Deployment">Bereitstellung unserer App</a></li></ol></details></li><li><details><summary>Einführung in client-seitige Frameworks</summary><ol><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Introduction">Einführung in client-seitige Frameworks</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Main_features">Hauptmerkmale von Frameworks</a></li></ol></details></li><li><details><summary>React</summary><ol><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started">Erste Schritte mit React</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_todo_list_beginning">Beginn unserer React-Task-Liste</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components">Komponentisieren unserer React-App</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_events_state">React-Interaktivität: Ereignisse und Status</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_filtering_conditional_rendering">React Interaktivität: Bearbeiten, Filtern, bedingtes Rendering</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_accessibility">Accessibility in React</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_resources">React-Ressourcen</a></li></ol></details></li><li><details><summary>Ember</summary><ol><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_getting_started">Einstieg in Ember</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization">Ember App-Struktur und Komponentisierung</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_interactivity_events_state">Ember-Interaktivität: Events, Klassen und Zustand</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_conditional_footer">Ember Interaktivität: Footer-Funktionalität, bedingtes Rendering</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_routing">Routing in Ember</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_resources">Ember-Ressourcen und Fehlerbehebung</a></li></ol></details></li><li><details><summary>Vue</summary><ol><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_getting_started">Einstieg in Vue</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_first_component">Erstellen unserer ersten Vue-Komponente</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_rendering_lists">Rendering einer Liste von Vue-Komponenten</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_methods_events_models">Hinzufügen eines neuen Todo-Formulars: Vue-Ereignisse, Methoden und Modelle</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_styling">Styling von Vue-Komponenten mit CSS</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_computed_properties">Verwendung von Vue computed properties</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_conditional_rendering">Vue bedingte Darstellung: Bearbeitung bestehender Todos</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_refs_focus_management">Vue-Refs und Lifecycle-Methoden zur Fokusverwaltung</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_resources">Vue-Ressourcen</a></li></ol></details></li><li><details><summary>Svelte</summary><ol><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_getting_started">Erste Schritte mit Svelte</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_Todo_list_beginning">Starten unserer Svelte-Tasklisten-App</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_variables_props">Dynamisches Verhalten in Svelte: Arbeiten mit Variablen und Props</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_components">Komponentisierung unserer Svelte-App</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility">Fortgeschrittenes Svelte: Reaktivität, Lebenszyklus, Barrierefreiheit</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_stores">Arbeiten mit Svelte Stores</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript">TypeScript-Unterstützung in Svelte</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_deployment_next">Bereitstellung und nächste Schritte</a></li></ol></details></li><li><details><summary>Angular</summary><ol><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_getting_started">Erste Schritte mit Angular</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_todo_list_beginning">Anfang unserer Angular-To-Do-Liste-App</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_styling">Styling unserer Angular-Anwendung</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_item_component">Erstellen einer Item-Komponente</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_filtering">Filtern unserer To-Do-Elemente</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_building">Building Angular applications and further resources</a></li></ol></details></li><li><details><summary>Git und GitHub</summary><ol><li><a href="/de/docs/Learn/Tools_and_testing/GitHub">Git und GitHub</a></li></ol></details></li><li><details><summary>Cross-Browser-Tests</summary><ol><li><a href="/de/docs/Learn/Tools_and_testing/Cross_browser_testing">Cross-Browser-Testing</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Introduction">Einführung in das Cross-Browser-Testing</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Testing_strategies">Strategien zur Durchführung von Tests</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS">Umgang mit häufigen HTML- und CSS-Problemen</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript">Umgang mit häufigen JavaScript-Problemen</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility">Umgang mit häufigen Problemen der Barrierefreiheit</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Feature_detection">Implementierung von Feature-Erkennung</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Automated_testing">Einführung in automatisiertes Testen</a></li><li><a href="/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment">Einrichten Ihrer eigenen Testautomatisierungsumgebung</a></li></ol></details></li><li class="section"><a href="/de/docs/Learn/Server-side">Server-seitige Webprogrammierung</a></li><li><details><summary>Erste Schritte</summary><ol><li><a href="/de/docs/Learn/Server-side/First_steps">Server-seitige Website-Programmierung: Erste Schritte</a></li><li><a href="/de/docs/Learn/Server-side/First_steps/Introduction">Einführung in die serverseitige Programmierung</a></li><li><a href="/de/docs/Learn/Server-side/First_steps/Client-Server_overview">Überblick über Client-Server</a></li><li><a href="/de/docs/Learn/Server-side/First_steps/Web_frameworks">Serverseitige Web-Frameworks</a></li><li><a href="/de/docs/Learn/Server-side/First_steps/Website_security">Website-Sicherheit</a></li></ol></details></li><li><details open=""><summary>Django Web-Framework (Python)</summary><ol><li><a href="/de/docs/Learn/Server-side/Django">Django Web Framework (Python)</a></li><li><a href="/de/docs/Learn/Server-side/Django/Introduction">Einführung in Django</a></li><li><a href="/de/docs/Learn/Server-side/Django/development_environment">Einrichten einer Django-Entwicklungsumgebung</a></li><li><a href="/de/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django-Tutorial: Die Local Library Website</a></li><li><a href="/de/docs/Learn/Server-side/Django/skeleton_website">Django-Tutorial Teil 2: Erstellung einer Skelett-Website</a></li><li><a href="/de/docs/Learn/Server-side/Django/Models">Django-Tutorial Teil 3: Verwenden von Modellen</a></li><li><a href="/de/docs/Learn/Server-side/Django/Admin_site">Django Tutorial Teil 4: Django Admin-Seite</a></li><li><a href="/de/docs/Learn/Server-side/Django/Home_page">Django-Tutorial Teil 5: Erstellen unserer Startseite</a></li><li><a href="/de/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Teil 6: Generische Listen- und Detailansichten</a></li><li><a href="/de/docs/Learn/Server-side/Django/Sessions">Django-Tutorial Teil 7: Sessions-Framework</a></li><li><a href="/de/docs/Learn/Server-side/Django/Authentication">Django-Tutorial Teil 8: Benutzer-Authentifizierung und Berechtigungen</a></li><li><a href="/de/docs/Learn/Server-side/Django/Forms">Django Tutorial Teil 9: Arbeiten mit Formularen</a></li><li><em><a href="/de/docs/Learn/Server-side/Django/Testing" aria-current="page">Django Tutorial Teil 10: Testen einer Django-Webanwendung</a></em></li><li><a href="/de/docs/Learn/Server-side/Django/Deployment">Django-Tutorial Teil 11: Django in Produktion bereitstellen</a></li><li><a href="/de/docs/Learn/Server-side/Django/web_application_security">Django-Webanwendungssicherheit</a></li><li><a href="/de/docs/Learn/Server-side/Django/django_assessment_blog">Bewertung: DIY Django Mini-Blog</a></li></ol></details></li><li><details><summary>Express Web-Framework (Node.js/JavaScript)</summary><ol><li><a href="/de/docs/Learn/Server-side/Express_Nodejs">Express-Webframework (Node.js/JavaScript)</a></li><li><a href="/de/docs/Learn/Server-side/Express_Nodejs/Introduction">Einführung in Express/Node</a></li><li><a href="/de/docs/Learn/Server-side/Express_Nodejs/development_environment">Einrichtung einer Node-Entwicklungsumgebung</a></li><li><a href="/de/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express-Tutorial: Die Website der lokalen Bibliothek</a></li><li><a href="/de/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Teil 2: Erstellung einer Grundstruktur für eine Website</a></li><li><a href="/de/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Teil 3: Verwendung einer Datenbank (mit Mongoose)</a></li><li><a href="/de/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Teil 4: Routen und Controller</a></li><li><a href="/de/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Teil 5: Bibliotheksdaten anzeigen</a></li><li><a href="/de/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Teil 6: Arbeiten mit Formularen</a></li><li><a href="/de/docs/Learn/Server-side/Express_Nodejs/deployment">Express-Tutorial Teil 7: Bereitstellung für die Produktion</a></li></ol></details></li><li class="section"><a href="/de/docs/Learn/Common_questions">Weitere Ressourcen</a></li><li><details><summary>Häufige Fragen</summary><ol><li><a href="/de/docs/Learn/Common_questions">Häufige Fragen</a></li><li><a href="/de/docs/Learn/HTML/Howto">HTML verwenden, um häufige Probleme zu lösen</a></li><li><a href="/de/docs/Learn/CSS/Howto">CSS verwenden, um häufige Probleme zu lösen</a></li><li><a href="/de/docs/Learn/JavaScript/Howto">Lösen Sie häufige Probleme in Ihrem JavaScript-Code</a></li><li><a href="/de/docs/Learn/Common_questions/Web_mechanics">Web-Mechanik</a></li><li><a href="/de/docs/Learn/Common_questions/Tools_and_setup">Tools und Einrichtung</a></li><li><a href="/de/docs/Learn/Common_questions/Design_and_accessibility">Design und Barrierefreiheit</a></li></ol></details></li></ol></div></div><section class="place side"></section></nav></aside><div class="toc-container"><aside class="toc"><nav><div class="document-toc-container"><section class="document-toc"><header><h2 class="document-toc-heading">In diesem Artikel</h2></header><ul class="document-toc-list"><li class="document-toc-item "><a class="document-toc-link" href="#überblick">Überblick</a></li><li class="document-toc-item "><a class="document-toc-link" href="#überblick_über_die_teststruktur">Überblick über die Teststruktur</a></li><li class="document-toc-item "><a class="document-toc-link" href="#wie_führt_man_die_tests_aus">Wie führt man die Tests aus</a></li><li class="document-toc-item "><a class="document-toc-link" href="#locallibrary_tests">LocalLibrary Tests</a></li><li class="document-toc-item "><a class="document-toc-link" href="#weitere_empfohlene_testwerkzeuge">Weitere empfohlene Testwerkzeuge</a></li><li class="document-toc-item "><a class="document-toc-link" href="#fordern_sie_sich_selbst_heraus">Fordern Sie sich selbst heraus</a></li><li class="document-toc-item "><a class="document-toc-link" href="#zusammenfassung">Zusammenfassung</a></li><li class="document-toc-item "><a class="document-toc-link" href="#siehe_auch">Siehe auch</a></li></ul></section></div></nav></aside><section class="place side"></section></div></div><main id="content" class="main-content "><article class="main-page-content" lang="de"><header><h1>Django Tutorial Teil 10: Testen einer Django-Webanwendung</h1></header><div class="section-content"><ul class="prev-next"> <li><a class="button secondary" href="/de/docs/Learn/Server-side/Django/Forms"><span class="button-wrap"> Zurück </span></a></li> <li><a class="button secondary" href="/de/docs/Learn/Server-side/Django"><span class="button-wrap"> Übersicht: Django Web Framework (Python)</span></a></li> <li><a class="button secondary" href="/de/docs/Learn/Server-side/Django/Deployment"><span class="button-wrap"> Weiter </span></a></li> </ul> <p>Wenn Websites wachsen, werden sie schwieriger manuell zu testen. Nicht nur gibt es mehr zu testen, sondern auch, da die Interaktionen zwischen den Komponenten komplexer werden, kann eine kleine Änderung in einem Bereich andere Bereiche beeinflussen. Daher sind mehr Änderungen erforderlich, um sicherzustellen, dass alles weiterhin funktioniert und keine Fehler eingeführt werden, während mehr Änderungen vorgenommen werden. Eine Möglichkeit, diese Probleme zu mindern, ist das Schreiben automatisierter Tests, die bei jeder Änderung einfach und zuverlässig ausgeführt werden können. Dieses Tutorial zeigt, wie Sie mit Djangos Testframework das <em>Unittesting</em> Ihrer Website automatisieren können.</p> <figure class="table-container"><table> <tbody> <tr> <th scope="row">Voraussetzungen:</th> <td>Schließen Sie alle vorherigen Tutorial-Themen ab, einschließlich <a href="/de/docs/Learn/Server-side/Django/Forms">Django Tutorial Teil 9: Arbeiten mit Formularen</a>.</td> </tr> <tr> <th scope="row">Ziel:</th> <td>Verstehen, wie man Unittests für auf Django basierende Websites schreibt.</td> </tr> </tbody> </table></figure></div><section aria-labelledby="überblick"><h2 id="überblick"><a href="#überblick">Überblick</a></h2><div class="section-content"><p>Die <a href="/de/docs/Learn/Server-side/Django/Tutorial_local_library_website">Local Library</a> hat derzeit Seiten, um Listen aller Bücher und Autoren anzuzeigen, Detailansichten für <code>Book</code>- und <code>Author</code>-Objekte, eine Seite, um <code>BookInstance</code>-Objekte zu erneuern, und Seiten zum Erstellen, Aktualisieren und Löschen von <code>Author</code>-Objekten (und auch <code>Book</code>-Datensätzen, wenn Sie die <em>Herausforderung</em> im <a href="/de/docs/Learn/Server-side/Django/Forms">Formular-Tutorial</a> abgeschlossen haben). Selbst bei dieser relativ kleinen Seite kann es mehrere Minuten dauern, manuell zu jeder Seite zu navigieren und <em>oberflächlich</em> zu prüfen, ob alles wie erwartet funktioniert. Wenn wir Änderungen vornehmen und die Seite vergrößern, wird die Zeit, die benötigt wird, um manuell zu überprüfen, dass alles "ordnungsgemäß" funktioniert, nur noch zunehmen. Wenn wir so weitermachen würden, würden wir schließlich die meiste Zeit mit Testen verbringen und sehr wenig mit der Verbesserung unseres Codes.</p> <p>Automatisierte Tests können wirklich bei diesem Problem helfen! Die offensichtlichen Vorteile sind, dass sie viel schneller als manuelle Tests ausgeführt werden können, auf einem viel detaillierteren Niveau testen können und jedes Mal genau die gleiche Funktionalität testen (menschliche Tester sind bei weitem nicht so zuverlässig!). Da sie schnell sind, können automatisierte Tests regelmäßiger ausgeführt werden, und wenn ein Test fehlschlägt, zeigen sie genau, wo der Code nicht wie erwartet funktioniert.</p> <p>Darüber hinaus können automatisierte Tests als der erste reale "Benutzer" Ihres Codes fungieren und Sie dazu zwingen, rigoros zu definieren und zu dokumentieren, wie sich Ihre Website verhalten soll. Oft sind sie die Grundlage für Ihre Codebeispiele und Dokumentation. Aus diesen Gründen beginnen einige Softwareentwicklungsprozesse mit der Definition und Implementierung von Tests, nach denen der Code geschrieben wird, um das erforderliche Verhalten zu erfüllen (z. B. <a href="https://en.wikipedia.org/wiki/Test-driven_development" class="external" target="_blank">Testgetriebene</a> und <a href="https://en.wikipedia.org/wiki/Behavior-driven_development" class="external" target="_blank">verhaltensgetriebene</a> Entwicklung).</p> <p>Dieses Tutorial zeigt, wie man automatisierte Tests für Django schreibt, indem eine Reihe von Tests auf der <em>LocalLibrary</em>-Website hinzugefügt wird.</p></div></section><section aria-labelledby="arten_des_testens"><h3 id="arten_des_testens"><a href="#arten_des_testens">Arten des Testens</a></h3><div class="section-content"><p>Es gibt zahlreiche Typen, Ebenen und Klassifikationen von Tests und Testansätzen. Die wichtigsten automatisierten Tests sind:</p> <dl> <dt id="unit-tests"><a href="#unit-tests">Unit-Tests</a></dt> <dd> <p>Überprüfen das funktionale Verhalten einzelner Komponenten, oft auf Klassen- und Funktionsebene.</p> </dd> <dt id="regressionstests"><a href="#regressionstests">Regressionstests</a></dt> <dd> <p>Tests, die historische Fehler reproduzieren. Jeder Test wird zunächst ausgeführt, um zu überprüfen, ob der Fehler behoben wurde, und dann erneut ausgeführt, um sicherzustellen, dass er nach späteren Codeänderungen nicht erneut eingeführt wurde.</p> </dd> <dt id="integrationstests"><a href="#integrationstests">Integrationstests</a></dt> <dd> <p>Überprüfen, wie Gruppierungen von Komponenten funktionieren, wenn sie zusammen verwendet werden. Integrationstests berücksichtigen die erforderlichen Interaktionen zwischen den Komponenten, aber nicht unbedingt die internen Vorgänge jeder Komponente. Sie können einfache Gruppierungen von Komponenten bis hin zur gesamten Website abdecken.</p> </dd> </dl> <div class="notecard note"> <p><strong>Hinweis:</strong> Andere gängige Arten von Tests sind Black-Box-, White-Box-, manuelle, automatisierte, Canary-, Smoke-, Konformitäts-, Akzeptanz-, Funktions-, System-, Leistungs-, Last- und Belastungstests. Schauen Sie sie nach, um weitere Informationen zu erhalten.</p> </div></div></section><section aria-labelledby="was_bietet_django_zum_testen"><h3 id="was_bietet_django_zum_testen"><a href="#was_bietet_django_zum_testen">Was bietet Django zum Testen?</a></h3><div class="section-content"><p>Das Testen einer Website ist eine komplexe Aufgabe, da sie aus mehreren Logikebenen besteht – von der HTTP-Ebene der Anforderungsverarbeitung über Modellabfragen bis zur Formularvalidierung und -verarbeitung und der Rendererstellung von Vorlagen.</p> <p>Django bietet ein Testframework mit einer kleinen Hierarchie von Klassen, die auf der Python-Standardbibliothek <a href="https://docs.python.org/3/library/unittest.html#module-unittest" class="external" target="_blank"><code>unittest</code></a> aufbauen. Trotz des Namens ist dieses Testframework sowohl für Unit- als auch für Integrationstests geeignet. Das Django-Framework fügt API-Methoden und Tools hinzu, die das Testen von Web- und Django-spezifischem Verhalten unterstützen. Diese ermöglichen Ihnen das Simulieren von Anforderungen, das Einfügen von Testdaten und das Überprüfen der Ausgabe Ihrer Anwendung. Django bietet auch eine API (<a href="https://docs.djangoproject.com/en/5.0/topics/testing/tools/#liveservertestcase" class="external" target="_blank">LiveServerTestCase</a>) und Werkzeuge für <a href="https://docs.djangoproject.com/en/5.0/topics/testing/advanced/#other-testing-frameworks" class="external" target="_blank">die Verwendung verschiedener Testframeworks</a>, beispielsweise können Sie das beliebte <a href="/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment">Selenium</a> Framework integrieren, um einem Benutzer zu simulieren, der mit einem Live-Browser interagiert.</p> <p>Um einen Test zu schreiben, erben Sie von einer der Django- (oder <em>unittest</em>-) Test-Basisklassen (<a href="https://docs.djangoproject.com/en/5.0/topics/testing/tools/#simpletestcase" class="external" target="_blank">SimpleTestCase</a>, <a href="https://docs.djangoproject.com/en/5.0/topics/testing/tools/#transactiontestcase" class="external" target="_blank">TransactionTestCase</a>, <a href="https://docs.djangoproject.com/en/5.0/topics/testing/tools/#testcase" class="external" target="_blank">TestCase</a>, <a href="https://docs.djangoproject.com/en/5.0/topics/testing/tools/#liveservertestcase" class="external" target="_blank">LiveServerTestCase</a>) und schreiben Sie dann separate Methoden, um zu überprüfen, dass die spezifische Funktionalität wie erwartet funktioniert (Tests verwenden "assert"-Methoden, um zu testen, dass Ausdrücke in <code>True</code>- oder <code>False</code>-Werten resultieren, oder dass zwei Werte gleich sind usw.). Wenn Sie einen Testrun starten, führt das Framework die ausgewählten Testmethoden in Ihren abgeleiteten Klassen aus. Die Testmethoden werden unabhängig ausgeführt, mit gemeinsamem Setup und/oder Abbauverhalten, wie unten gezeigt.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>class YourTestClass(TestCase): def setUp(self): # Setup run before every test method. pass def tearDown(self): # Clean up run after every test method. pass def test_something_that_will_pass(self): self.assertFalse(False) def test_something_that_will_fail(self): self.assertTrue(False) </code></pre></div> <p>Die beste Basisklasse für die meisten Tests ist <a href="https://docs.djangoproject.com/en/5.0/topics/testing/tools/#testcase" class="external" target="_blank">django.test.TestCase</a>. Diese Testklasse erstellt eine saubere Datenbank, bevor ihre Tests ausgeführt werden, und führt jede Testfunktion in ihrer eigenen Transaktion aus. Die Klasse besitzt auch einen Test-<a href="https://docs.djangoproject.com/en/5.0/topics/testing/tools/#django.test.Client" class="external" target="_blank">Client</a>, den Sie verwenden können, um die Interaktion eines Benutzers mit dem Code auf der View-Ebene zu simulieren. In den folgenden Abschnitten konzentrieren wir uns auf Unit-Tests, die mit dieser <a href="https://docs.djangoproject.com/en/5.0/topics/testing/tools/#testcase" class="external" target="_blank">TestCase</a>-Basisklasse erstellt werden.</p> <div class="notecard note"> <p><strong>Hinweis:</strong> Die <a href="https://docs.djangoproject.com/en/5.0/topics/testing/tools/#testcase" class="external" target="_blank">django.test.TestCase</a>-Klasse ist sehr praktisch, kann jedoch dazu führen, dass einige Tests langsamer sind als nötig (nicht jeder Test muss seine eigene Datenbank einrichten oder die View-Interaktion simulieren). Sobald Sie vertraut sind mit dem, was Sie mit dieser Klasse tun können, möchten Sie möglicherweise einige Ihrer Tests durch die verfügbaren einfacheren Testklassen ersetzen.</p> </div></div></section><section aria-labelledby="was_sollten_sie_testen"><h3 id="was_sollten_sie_testen"><a href="#was_sollten_sie_testen">Was sollten Sie testen?</a></h3><div class="section-content"><p>Sie sollten alle Aspekte Ihres eigenen Codes testen, jedoch keine Bibliotheken oder Funktionalitäten, die von Python oder Django bereitgestellt werden.</p> <p>Betrachten Sie zum Beispiel das unten definierte <code>Author</code>-Modell. Sie müssen nicht explizit testen, ob <code>first_name</code> und <code>last_name</code> ordnungsgemäß als <code>CharField</code> in der Datenbank gespeichert wurden, da dies etwas ist, das von Django definiert ist (obwohl Sie dies in der Praxis natürlich während der Entwicklung unvermeidlich testen werden). Ebenso müssen Sie nicht testen, ob <code>date_of_birth</code> als Datumsfeld validiert wurde, da auch dies in Django implementiert ist.</p> <p>Sie sollten jedoch den Text überprüfen, der für die Etiketten verwendet wird (<em>First name, Last name, Date of birth, Died</em>), und die Größe des für den Text zugewiesenen Feldes (<em>100 Zeichen</em>), da diese Teil Ihres Designs sind und in Zukunft gebrochen/geändert werden könnten.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>class Author(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) date_of_birth = models.DateField(null=True, blank=True) date_of_death = models.DateField('Died', null=True, blank=True) def get_absolute_url(self): return reverse('author-detail', args=[str(self.id)]) def __str__(self): return '%s, %s' % (self.last_name, self.first_name) </code></pre></div> <p>Ebenso sollten Sie überprüfen, ob die benutzerdefinierten Methoden <code>get_absolute_url()</code> und <code>__str__()</code> wie erforderlich funktionieren, da sie Ihr eigener Code bzw. Ihre Geschäftslogik sind. Im Fall von <code>get_absolute_url()</code> können Sie darauf vertrauen, dass die Django-Methode <code>reverse()</code> ordnungsgemäß implementiert wurde. Was Sie testen, ist also, dass die zugehörige View tatsächlich definiert wurde.</p> <div class="notecard note"> <p> <strong>Hinweis:</strong> Aufmerksame Leser werden möglicherweise feststellen, dass wir auch das Geburts- und Sterbedatum auf sinnvolle Werte beschränken und sicherstellen möchten, dass der Tod nach der Geburt erfolgt. In Django würde diese Einschränkung in Ihren Formularklassen hinzugefügt (obwohl Sie Validatoren für Modelfelder und Modellvalidatoren definieren können, werden diese nur auf der Formulardaten-Ebene verwendet, wenn sie durch die <code>clean()</code>-Methode des Modells aufgerufen werden. Dies erfordert ein <code>ModelForm</code>, oder die <code>clean()</code>-Methode des Modells muss explizit aufgerufen werden.) </p> </div> <p>Mit diesem Gedanken im Hinterkopf beginnen wir also damit, wie man Tests definiert und ausführt.</p></div></section><section aria-labelledby="überblick_über_die_teststruktur"><h2 id="überblick_über_die_teststruktur"><a href="#überblick_über_die_teststruktur">Überblick über die Teststruktur</a></h2><div class="section-content"><p>Bevor wir näher darauf eingehen, "was zu testen ist", werfen wir zunächst einen kurzen Blick darauf, <em>wo</em> und <em>wie</em> Tests definiert werden.</p> <p>Django verwendet die <a href="https://docs.python.org/3/library/unittest.html#unittest-test-discovery" class="external" target="_blank">Test Discovery</a> der <code>unittest</code>-Moduls, die Tests im aktuellen Arbeitsverzeichnis in jeder Datei findet, die mit dem Muster <strong>test*.py</strong> benannt ist. Vorausgesetzt, Sie benennen die Dateien entsprechend, können Sie jede beliebige Struktur verwenden. Wir empfehlen, ein Modul für Ihren Testcode zu erstellen und separate Dateien für Modelle, Ansichten, Formulare und alle anderen Arten von Code zu verwenden, die Sie testen müssen. Zum Beispiel:</p> <pre class="brush: plain notranslate">catalog/ /tests/ __init__.py test_models.py test_forms.py test_views.py </pre> <p>Erstellen Sie eine Dateistruktur wie oben gezeigt in Ihrem <em>LocalLibrary</em>-Projekt. Die <strong>__init__.py</strong> sollte eine leere Datei sein (dies sagt Python, dass das Verzeichnis ein Paket ist). Sie können die drei Testdateien erstellen, indem Sie die Skelett-Testdatei <strong>/catalog/tests.py</strong> kopieren und umbenennen.</p> <div class="notecard note"> <p><strong>Hinweis:</strong> Die Skelett-Testdatei <strong>/catalog/tests.py</strong> wurde automatisch erstellt, als wir <a href="/de/docs/Learn/Server-side/Django/skeleton_website">die Django-Skelett-Website erstellten</a>. Es ist vollkommen "legal", alle Ihre Tests darin zu platzieren, aber wenn Sie richtig testen, werden Sie schnell eine sehr große und unübersichtliche Testdatei haben.</p> <p>Löschen Sie die Skelettdatei, da wir sie nicht benötigen.</p> </div> <p>Öffnen Sie <strong>/catalog/tests/test_models.py</strong>. Die Datei sollte <code>django.test.TestCase</code> importieren, wie gezeigt:</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>from django.test import TestCase # Create your tests here. </code></pre></div> <p>Oft werden Sie eine Testklasse für jedes Modell/View/Formular hinzufügen, das Sie testen möchten, mit individuellen Methoden, die bestimmte Funktionalitäten testen. In anderen Fällen möchten Sie möglicherweise eine separate Klasse haben, um einen spezifischen Anwendungsfall zu testen, mit individuellen Testfunktionen, die Aspekte dieses Anwendungsfalls testen (zum Beispiel eine Klasse, um zu testen, ob ein Modelfeld ordnungsgemäß validiert wurde, mit Funktionen, um jeden möglichen Fehlschlag zu testen). Auch hier liegt die Struktur sehr bei Ihnen, aber es ist am besten, wenn Sie konsistent sind.</p> <p>Fügen Sie die untenstehende Testklasse am Ende der Datei hinzu. Die Klasse zeigt, wie Sie eine Testfallklasse durch Ableiten von <code>TestCase</code> konstruieren.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>class YourTestClass(TestCase): @classmethod def setUpTestData(cls): print("setUpTestData: Run once to set up non-modified data for all class methods.") pass def setUp(self): print("setUp: Run once for every test method to set up clean data.") pass def test_false_is_false(self): print("Method: test_false_is_false.") self.assertFalse(False) def test_false_is_true(self): print("Method: test_false_is_true.") self.assertTrue(False) def test_one_plus_one_equals_two(self): print("Method: test_one_plus_one_equals_two.") self.assertEqual(1 + 1, 2) </code></pre></div> <p>Die neue Klasse definiert zwei Methoden, die Sie für die Konfiguration vor dem Test verwenden können (z. B. um Modelle oder andere Objekte zu erstellen, die Sie für den Test benötigen):</p> <ul> <li><code>setUpTestData()</code> wird einmal zu Beginn des Testruns für das Klassen-Setup aufgerufen. Sie verwenden dies, um Objekte zu erstellen, die in keiner der Testmethoden geändert oder verändert werden.</li> <li><code>setUp()</code> wird vor jeder Testfunktion aufgerufen, um Objekte einzurichten, die möglicherweise durch den Test geändert werden (jede Testfunktion erhält eine "frische" Version dieser Objekte).</li> </ul> <div class="notecard note"> <p><strong>Hinweis:</strong> Die Testklassen haben auch eine <code>tearDown()</code>-Methode, die wir nicht verwendet haben. Diese Methode ist für Datenbanktests nicht besonders nützlich, da die <code>TestCase</code>-Basisklasse den Abbau der Datenbank für Sie übernimmt.</p> </div> <p>Unter diesen haben wir eine Reihe von Testmethoden, die <code>Assert</code>-Funktionen verwenden, um zu testen, ob Bedingungen wahr, falsch oder gleich sind (<code>AssertTrue</code>, <code>AssertFalse</code>, <code>AssertEqual</code>). Wenn die Bedingung nicht wie erwartet bewertet wird, wird der Test fehlschlagen und den Fehler an Ihrer Konsole melden.</p> <p>Die <code>AssertTrue</code>, <code>AssertFalse</code>, <code>AssertEqual</code> sind Standardbehauptungen von <strong>unittest</strong>. Es gibt andere Standardbehauptungen im Framework sowie <a href="https://docs.djangoproject.com/en/5.0/topics/testing/tools/#assertions" class="external" target="_blank">Django-spezifische Behauptungen</a>, um zu testen, ob eine View umleitet (<code>assertRedirects</code>), um zu testen, ob eine bestimmte Vorlage verwendet wurde (<code>assertTemplateUsed</code>) usw.</p> <div class="notecard note"> <p><strong>Hinweis:</strong> Normalerweise sollten Sie <strong>keine</strong> <strong>print()</strong>-Funktionen in Ihre Tests einfügen, wie oben gezeigt. Wir tun dies hier nur, damit Sie die Reihenfolge sehen können, in der die Setup-Funktionen in der Konsole aufgerufen werden (im folgenden Abschnitt).</p> </div></div></section><section aria-labelledby="wie_führt_man_die_tests_aus"><h2 id="wie_führt_man_die_tests_aus"><a href="#wie_führt_man_die_tests_aus">Wie führt man die Tests aus</a></h2><div class="section-content"><p>Der einfachste Weg, alle Tests auszuführen, ist die Verwendung des Befehls:</p> <div class="code-example"><div class="example-header"><span class="language-name">bash</span></div><pre class="brush: bash notranslate"><code>python3 manage.py test </code></pre></div> <p>Dieser wird alle Dateien mit dem Muster <strong>test*.py</strong> im aktuellen Verzeichnis entdecken und alle Tests ausführen, die unter Verwendung geeigneter Basisklassen definiert wurden (hier haben wir eine Reihe von Testdateien, aber nur <strong>/catalog/tests/test_models.py</strong> enthält derzeit Tests). Standardmäßig berichten die Tests nur einzeln über Testfehler und geben dann eine Zusammenfassung der Tests aus.</p> <div class="notecard note"> <p><strong>Hinweis:</strong> Wenn Sie Fehler wie: <code>ValueError: Missing staticfiles manifest entry...</code> erhalten, kann dies daran liegen, dass beim Testen <em>collectstatic</em> standardmäßig nicht ausgeführt wird, und Ihre App eine Speicherklasse verwendet, die dies erfordert (siehe <a href="https://docs.djangoproject.com/en/5.0/ref/contrib/staticfiles/#django.contrib.staticfiles.storage.ManifestStaticFilesStorage.manifest_strict" class="external" target="_blank">manifest_strict</a> für weitere Informationen). Es gibt mehrere Möglichkeiten, dieses Problem zu lösen - die einfachste ist, <em>collectstatic</em> vor dem Ausführen der Tests auszuführen:</p> <div class="code-example"><div class="example-header"><span class="language-name">bash</span></div><pre class="brush: bash notranslate"><code>python3 manage.py collectstatic </code></pre></div> </div> <p>Führen Sie die Tests im Stammverzeichnis der <em>LocalLibrary</em> aus. Sie sollten eine Ausgabe ähnlich der unten stehenden sehen.</p> <div class="code-example"><div class="example-header"><span class="language-name">bash</span></div><pre class="brush: bash notranslate"><code>&gt; python3 manage.py test Creating test database for alias 'default'... setUpTestData: Run once to set up non-modified data for all class methods. setUp: Run once for every test method to set up clean data. Method: test_false_is_false. setUp: Run once for every test method to set up clean data. Method: test_false_is_true. setUp: Run once for every test method to set up clean data. Method: test_one_plus_one_equals_two. . ====================================================================== FAIL: test_false_is_true (catalog.tests.tests_models.YourTestClass) ---------------------------------------------------------------------- Traceback (most recent call last): File "D:\GitHub\django_tmp\library_w_t_2\locallibrary\catalog\tests\tests_models.py", line 22, in test_false_is_true self.assertTrue(False) AssertionError: False is not true ---------------------------------------------------------------------- Ran 3 tests in 0.075s FAILED (failures=1) Destroying test database for alias 'default'... </code></pre></div> <p>Hier sehen wir, dass wir einen Testfehler hatten, und wir können genau sehen, welche Funktion fehlgeschlagen ist und warum (dieser Fehler wird erwartet, weil <code>False</code> nicht <code>True</code> ist!).</p> <div class="notecard note"> <p><strong>Hinweis:</strong> Das Wichtigste, was Sie aus der oben genannten Testausgabe lernen sollten, ist, dass es viel wertvoller ist, wenn Sie beschreibende/informative Namen für Ihre Objekte und Methoden verwenden.</p> </div> <p>Die Ausgabe der <code>print()</code>-Funktionen zeigt, wie die <code>setUpTestData()</code>-Methode einmal für die Klasse aufgerufen wird und <code>setUp()</code> vor jeder Methode aufgerufen wird. Denken Sie daran, dass Sie normalerweise diese Art von <code>print()</code> nicht zu Ihren Tests hinzufügen würden.</p> <p>In den nächsten Abschnitten wird gezeigt, wie Sie spezifische Tests ausführen können und wie Sie steuern können, wie viele Informationen die Tests anzeigen.</p></div></section><section aria-labelledby="mehr_testinformationen_anzeigen"><h3 id="mehr_testinformationen_anzeigen"><a href="#mehr_testinformationen_anzeigen">Mehr Testinformationen anzeigen</a></h3><div class="section-content"><p>Wenn Sie mehr Informationen über den Testrun erhalten möchten, können Sie die <em>Ausgabeintensität</em> ändern. Zum Beispiel, um die Testerfolge sowie Fehler aufzulisten (und eine ganze Menge Informationen, wie die Testdatenbank eingerichtet wird), können Sie die Ausgabeintensität auf "2" setzen, wie gezeigt:</p> <div class="code-example"><div class="example-header"><span class="language-name">bash</span></div><pre class="brush: bash notranslate"><code>python3 manage.py test --verbosity 2 </code></pre></div> <p>Die erlaubten Intensitätsstufen sind 0, 1, 2 und 3, wobei der Standard "1" ist.</p></div></section><section aria-labelledby="dinge_beschleunigen"><h3 id="dinge_beschleunigen"><a href="#dinge_beschleunigen">Dinge beschleunigen</a></h3><div class="section-content"><p>Wenn Ihre Tests unabhängig sind, können Sie diese auf einem Mehrprozessormaschine erheblich beschleunigen, indem Sie sie parallel ausführen. Die Verwendung von <code>--parallel auto</code> im Folgenden führt einen Testprozess pro verfügbarem Kern aus. Das <code>auto</code> ist optional, und Sie können auch eine bestimmte Anzahl von Kernen angeben, die verwendet werden sollen.</p> <div class="code-example"><div class="example-header"><span class="language-name">bash</span></div><pre class="brush: bash notranslate"><code>python3 manage.py test --parallel auto </code></pre></div> <p>Weitere Informationen, einschließlich was zu tun ist, wenn Ihre Tests nicht unabhängig sind, finden Sie unter <a href="https://docs.djangoproject.com/en/5.0/ref/django-admin/#envvar-DJANGO_TEST_PROCESSES" class="external" target="_blank">DJANGO_TEST_PROCESSES</a>.</p></div></section><section aria-labelledby="bestimmte_tests_ausführen"><h3 id="bestimmte_tests_ausführen"><a href="#bestimmte_tests_ausführen">Bestimmte Tests ausführen</a></h3><div class="section-content"><p>Wenn Sie eine Teilmenge Ihrer Tests ausführen möchten, können Sie dies tun, indem Sie den vollständigen Punktpfad zum Paket bzw. Modul, zur <code>TestCase</code>-Unterklasse oder zur Methode angeben:</p> <div class="code-example"><div class="example-header"><span class="language-name">bash</span></div><pre class="brush: bash notranslate"><code># Run the specified module python3 manage.py test catalog.tests # Run the specified module python3 manage.py test catalog.tests.test_models # Run the specified class python3 manage.py test catalog.tests.test_models.YourTestClass # Run the specified method python3 manage.py test catalog.tests.test_models.YourTestClass.test_one_plus_one_equals_two </code></pre></div></div></section><section aria-labelledby="andere_test_runner-optionen"><h3 id="andere_test_runner-optionen"><a href="#andere_test_runner-optionen">Andere Test Runner-Optionen</a></h3><div class="section-content"><p>Der Test Runner bietet viele andere Optionen, einschließlich der Möglichkeit, Tests zu mischen (<code>--shuffle</code>), sie im Debug-Modus auszuführen (<code>--debug-mode</code>) und den Python-Logger zu verwenden, um die Ergebnisse aufzuzeichnen. Weitere Informationen finden Sie in der Django-<a href="https://docs.djangoproject.com/en/5.0/ref/django-admin/#test" class="external" target="_blank">Test Runner</a>-Dokumentation.</p></div></section><section aria-labelledby="locallibrary_tests"><h2 id="locallibrary_tests"><a href="#locallibrary_tests">LocalLibrary Tests</a></h2><div class="section-content"><p>Jetzt wissen wir, wie man unsere Tests ausführt und was für Dinge wir testen müssen, schauen wir uns einige praktische Beispiele an.</p> <div class="notecard note"> <p><strong>Hinweis:</strong> Wir werden nicht jeden möglichen Test schreiben, aber dies sollte Ihnen eine Vorstellung davon geben, wie Tests funktionieren und was mehr Sie tun können.</p> </div></div></section><section aria-labelledby="modelle"><h3 id="modelle"><a href="#modelle">Modelle</a></h3><div class="section-content"><p>Wie oben diskutiert, sollten wir alles testen, was Teil unseres Designs ist oder durch Code definiert ist, den wir geschrieben haben, aber nicht die Funktionalität des zugrunde liegenden Frameworks und anderer Drittanbieterbibliotheken.</p> <p>Betrachten Sie zum Beispiel das <code>Author</code>-Modell unten. Hier sollten wir die Etiketten für alle Felder testen, da selbst wenn wir die meisten von ihnen nicht explizit spezifiziert haben, wir ein Design haben, das besagt, welche Werte diese sein sollten. Wenn wir die Werte nicht testen, dann wissen wir nicht, dass die Etiketten der Felder die beabsichtigten Werte haben. Ebenso, obwohl wir darauf vertrauen, dass Django ein Feld mit der angegebenen Länge erstellt, lohnt es sich, einen Test für diese Länge festzulegen, um sicherzustellen, dass sie wie geplant implementiert wurde.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>class Author(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) date_of_birth = models.DateField(null=True, blank=True) date_of_death = models.DateField('Died', null=True, blank=True) def get_absolute_url(self): return reverse('author-detail', args=[str(self.id)]) def __str__(self): return f'{self.last_name}, {self.first_name}' </code></pre></div> <p>Öffnen Sie unsere <strong>/catalog/tests/test_models.py</strong>, und ersetzen Sie eventuellen vorhandenen Code durch den folgenden Testcode für das <code>Author</code>-Modell.</p> <p>Hier sehen Sie, dass wir zuerst <code>TestCase</code> importieren und unsere Testklasse (<code>AuthorModelTest</code>) davon ableiten, mit einem beschreibenden Namen, damit wir fehlerhafte Tests in der Ausgabe leicht identifizieren können. Danach rufen wir <code>setUpTestData()</code> auf, um ein Autorenobjekt zu erstellen, das wir verwenden, aber in keiner der Tests verändern werden.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>from django.test import TestCase from catalog.models import Author class AuthorModelTest(TestCase): @classmethod def setUpTestData(cls): # Set up non-modified objects used by all test methods Author.objects.create(first_name='Big', last_name='Bob') def test_first_name_label(self): author = Author.objects.get(id=1) field_label = author._meta.get_field('first_name').verbose_name self.assertEqual(field_label, 'first name') def test_date_of_death_label(self): author = Author.objects.get(id=1) field_label = author._meta.get_field('date_of_death').verbose_name self.assertEqual(field_label, 'died') def test_first_name_max_length(self): author = Author.objects.get(id=1) max_length = author._meta.get_field('first_name').max_length self.assertEqual(max_length, 100) def test_object_name_is_last_name_comma_first_name(self): author = Author.objects.get(id=1) expected_object_name = f'{author.last_name}, {author.first_name}' self.assertEqual(str(author), expected_object_name) def test_get_absolute_url(self): author = Author.objects.get(id=1) # This will also fail if the urlconf is not defined. self.assertEqual(author.get_absolute_url(), '/catalog/author/1') </code></pre></div> <p>Die Feldtests überprüfen, ob die Werte der Etiketten der Felder (<code>verbose_name</code>) und dass die Größe der Zeichenfelder wie erwartet ist. Diese Methoden haben alle beschreibende Namen und folgen dem gleichen Muster:</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code># Get an author object to test author = Author.objects.get(id=1) # Get the metadata for the required field and use it to query the required field data field_label = author._meta.get_field('first_name').verbose_name # Compare the value to the expected result self.assertEqual(field_label, 'first name') </code></pre></div> <p>Interessante Punkte sind:</p> <ul> <li>Wir können das <code>verbose_name</code> nicht direkt mit <code>author.first_name.verbose_name</code> abrufen, weil <code>author.first_name</code> ein <em>String</em> ist (kein Zugriff auf das <code>first_name</code>-Objekt, mit dem wir auf seine Eigenschaften zugreifen können). Stattdessen müssen wir das Attribut <code>_meta</code> des Autors verwenden, um eine Instanz des Feldes zu erhalten und diese zu verwenden, um nach den zusätzlichen Informationen zu suchen.</li> <li>Wir haben uns entschieden <code>assertEqual(field_label,'first name')</code> anstelle von <code>assertTrue(field_label == 'first name')</code> zu verwenden. Der Grund hierfür ist, dass, wenn der Test fehlschlägt, die Ausgabe bei ersterem Ihnen sagt, was das Label eigentlich war, was die Fehlersuche ein wenig erleichtert.</li> </ul> <div class="notecard note"> <p><strong>Hinweis:</strong> Tests für die <code>last_name</code>- und <code>date_of_birth</code>-Etiketten sowie der Test für die Länge des <code>last_name</code>-Feldes wurden ausgelassen. Fügen Sie jetzt Ihre eigenen Versionen hinzu, indem Sie den oben gezeigten Namenskonventionen und Ansätzen folgen.</p> </div> <p>Wir müssen auch unsere benutzerdefinierten Methoden testen. Diese überprüfen im Wesentlichen nur, ob der Objektname wie erwartet im "Nachname, Vorname"-Format konstruiert wurde und ob die URL, die wir für ein <code>Author</code>-Objekt erhalten, wie erwartet ist.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>def test_object_name_is_last_name_comma_first_name(self): author = Author.objects.get(id=1) expected_object_name = f'{author.last_name}, {author.first_name}' self.assertEqual(str(author), expected_object_name) def test_get_absolute_url(self): author = Author.objects.get(id=1) # This will also fail if the urlconf is not defined. self.assertEqual(author.get_absolute_url(), '/catalog/author/1') </code></pre></div> <p>Führen Sie die Tests jetzt aus. Wenn Sie das Author-Modell so erstellt haben, wie wir es im Modelle-Tutorial beschrieben haben, ist es sehr wahrscheinlich, dass Sie einen Fehler für das <code>date_of_death</code>-Label erhalten, wie unten gezeigt. Der Test schlägt fehl, weil er erwartet, dass die Etikettendefinition dem Django-Konvention folgt, den ersten Buchstaben des Etiketts nicht zu kapitalisieren (Django macht dies für Sie).</p> <div class="code-example"><div class="example-header"><span class="language-name">bash</span></div><pre class="brush: bash notranslate"><code>====================================================================== FAIL: test_date_of_death_label (catalog.tests.test_models.AuthorModelTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "D:\...\locallibrary\catalog\tests\test_models.py", line 32, in test_date_of_death_label self.assertEqual(field_label,'died') AssertionError: 'Died' != 'died' - Died ? ^ + died ? ^ </code></pre></div> <p>Dies ist ein sehr kleiner Fehler, aber er zeigt auf, wie das Schreiben von Tests gründlicher mögliche Annahmen überprüfen kann.</p> <div class="notecard note"> <p><strong>Hinweis:</strong> Ändern Sie das Label für das <code>date_of_death</code>-Feld (<strong>/catalog/models.py</strong>) in "died" und führen Sie die Tests erneut aus.</p> </div> <p>Die Muster für die Prüfung der anderen Modelle sind ähnlich, daher werden wir diese nicht weiter diskutieren. Fühlen Sie sich frei, Ihre eigenen Tests für unsere anderen Modelle zu erstellen.</p></div></section><section aria-labelledby="formulare"><h3 id="formulare"><a href="#formulare">Formulare</a></h3><div class="section-content"><p>Die Philosophie für die Prüfung Ihrer Formulare ist die gleiche wie für die Prüfung Ihrer Modelle; Sie müssen alles testen, was Sie selbst kodiert haben oder was Ihr Design spezifiziert, aber nicht das Verhalten des zugrunde liegenden Frameworks und anderer Drittanbieterbibliotheken.</p> <p>Generell bedeutet dies, dass Sie überprüfen sollten, dass die Formulare die Felder haben, die Sie möchten, und dass diese mit den entsprechenden Etiketten und Hilfetexten angezeigt werden. Sie müssen nicht überprüfen, dass Django den Feldtyp korrekt validiert (es sei denn, Sie haben Ihr eigenes benutzerdefiniertes Feld und die Validierung erstellt) – das heißt, Sie müssen nicht testen, ob ein E-Mail-Feld nur E-Mails akzeptiert. Allerdings müssten Sie jede zusätzliche Validierung testen, die Sie erwarten, die auf den Feldern durchgeführt wird, und alle Nachrichten, die Ihr Code für Fehler generieren wird.</p> <p>Betrachten Sie unser Formular zum Erneuern von Büchern. Dieses hat nur ein Feld für das Erneuerungsdatum, das ein Etikett und einen Hilfetext haben wird, die wir überprüfen müssen.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>class RenewBookForm(forms.Form): """Form for a librarian to renew books.""" renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).") def clean_renewal_date(self): data = self.cleaned_data['renewal_date'] # Check if a date is not in the past. if data &lt; datetime.date.today(): raise ValidationError(_('Invalid date - renewal in past')) # Check if date is in the allowed range (+4 weeks from today). if data &gt; datetime.date.today() + datetime.timedelta(weeks=4): raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead')) # Remember to always return the cleaned data. return data </code></pre></div> <p>Öffnen Sie unsere <strong>/catalog/tests/test_forms.py</strong>-Datei und ersetzen Sie etwaigen vorhandenen Code mit dem folgenden Testcode für das <code>RenewBookForm</code>-Formular. Wir beginnen damit, unser Formular und einige Python- und Django-Bibliotheken zu importieren, die das Testen zeitbezogener Funktionalität unterstützen. Anschließend deklarieren wir unsere Formulartestklasse auf die gleiche Weise wie für Modelle, mit einem beschreibenden Namen für unsere von <code>TestCase</code> abgeleitete Testklasse.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>import datetime from django.test import TestCase from django.utils import timezone from catalog.forms import RenewBookForm class RenewBookFormTest(TestCase): def test_renew_form_date_field_label(self): form = RenewBookForm() self.assertTrue(form.fields['renewal_date'].label is None or form.fields['renewal_date'].label == 'renewal date') def test_renew_form_date_field_help_text(self): form = RenewBookForm() self.assertEqual(form.fields['renewal_date'].help_text, 'Enter a date between now and 4 weeks (default 3).') def test_renew_form_date_in_past(self): date = datetime.date.today() - datetime.timedelta(days=1) form = RenewBookForm(data={'renewal_date': date}) self.assertFalse(form.is_valid()) def test_renew_form_date_too_far_in_future(self): date = datetime.date.today() + datetime.timedelta(weeks=4) + datetime.timedelta(days=1) form = RenewBookForm(data={'renewal_date': date}) self.assertFalse(form.is_valid()) def test_renew_form_date_today(self): date = datetime.date.today() form = RenewBookForm(data={'renewal_date': date}) self.assertTrue(form.is_valid()) def test_renew_form_date_max(self): date = timezone.localtime() + datetime.timedelta(weeks=4) form = RenewBookForm(data={'renewal_date': date}) self.assertTrue(form.is_valid()) </code></pre></div> <p>Die ersten beiden Funktionen testen, ob das <code>label</code> und der <code>help_text</code> des Feldes wie erwartet sind. Wir müssen auf das Feld über das Felderverzeichnis zugreifen (z. B. <code>form.fields['renewal_date']</code>). Beachten Sie hier, dass wir auch testen müssen, ob der Labelwert <code>None</code> ist, da Django, auch wenn es das korrekte Etikett rendert, <code>None</code> zurückgibt, wenn der Wert nicht <em>explizit</em> gesetzt ist.</p> <p>Die restlichen Funktionen testen, ob das Formular für Erneuerungsdaten innerhalb des akzeptablen Bereichs gültig ist und für Werte außerhalb des Bereichs ungültig ist. Beachten Sie, wie wir Testdatumswerte um unser aktuelles Datum (<code>datetime.date.today()</code>) mit <code>datetime.timedelta()</code> konstruieren (in diesem Fall gibt eine Anzahl von Tagen oder Wochen an). Wir erstellen dann einfach das Formular, übergeben unsere Daten und testen, ob es gültig ist.</p> <div class="notecard note"> <p><strong>Hinweis:</strong> Hier verwenden wir tatsächlich nicht die Datenbank oder den Test-Client. Erwägen Sie, diese Tests zu ändern, um <a href="https://docs.djangoproject.com/en/5.0/topics/testing/tools/#django.test.SimpleTestCase" class="external" target="_blank">SimpleTestCase</a> zu verwenden.</p> <p>Wir müssen auch validieren, dass die richtigen Fehler ausgelöst werden, wenn das Formular ungültig ist, aber dies wird normalerweise als Teil der View-Verarbeitung durchgeführt, daher werden wir uns darum im nächsten Abschnitt kümmern.</p> </div> <div class="notecard warning"> <p><strong>Warnung:</strong> Wenn Sie die Klasse <a href="/de/docs/Learn/Server-side/Django/Forms#modelforms">ModelForm</a> <code>RenewBookModelForm(forms.ModelForm)</code> anstelle der Klasse <code>RenewBookForm(forms.Form)</code> verwenden, dann wäre der Formularfeldname <strong>'due_back'</strong> anstelle von <strong>'renewal_date'</strong>.</p> </div> <p>Das ist alles für Formulare; wir haben einige andere, aber sie werden automatisch durch unsere generischen Class-Based-Editing-Views erstellt und sollten dort getestet werden! Führen Sie die Tests aus und bestätigen Sie, dass unser Code immer noch funktioniert!</p></div></section><section aria-labelledby="views"><h3 id="views"><a href="#views">Views</a></h3><div class="section-content"><p>Um unser View-Verhalten zu validieren, verwenden wir den Django-Test-<a href="https://docs.djangoproject.com/en/5.0/topics/testing/tools/#django.test.Client" class="external" target="_blank">Client</a>. Diese Klasse verhält sich wie ein Dummie-Webbrowser, den wir nutzen können, um <code>GET</code>- und <code>POST</code>-Anfragen zu simulieren und die Antwort zu beobachten. Wir können fast alles über die Antwort sehen, von der niedrigen HTTP-Ebene (Ergebnisheader und Statuscodes) über die Vorlage, die wir verwenden, um das HTML zu rendern, bis zu den Kontextdaten, die wir an diese weitergeben. Wir können auch die Kette der Umleitungen (falls vorhanden) sehen und die URL und den Statuscode in jedem Schritt überprüfen. Dies ermöglicht es uns zu überprüfen, dass jede View das tut, was erwartet wird.</p> <p>Beginnen wir mit einer unserer einfachsten Views, die eine Liste aller Autoren bereitstellt. Diese wird unter der URL <strong>/catalog/authors/</strong> angezeigt (eine URL mit dem Namen 'authors' in der URL-Konfiguration).</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>class AuthorListView(generic.ListView): model = Author paginate_by = 10 </code></pre></div> <p>Da dies eine generische Listenansicht ist, wird fast alles von Django für uns erledigt. Wenn Sie Django vertrauen, müssen Sie im Grunde nur testen, dass die View unter der richtigen URL zugänglich ist und mit ihrem Namen aufgerufen werden kann. Wenn Sie jedoch einen testgetriebenen Entwicklungsprozess verwenden, beginnen Sie mit dem Schreiben von Tests, die bestätigen, dass die View alle Autoren anzeigt und sie in Listen von jeweils 10 Seiten paginiert.</p> <p>Öffnen Sie die Datei <strong>/catalog/tests/test_views.py</strong> und ersetzen Sie eventuellen vorhandenen Text mit dem folgenden Testcode für <code>AuthorListView</code>. Wie zuvor importieren wir unser Modell und einige nützliche Klassen. In der <code>setUpTestData()</code>-Methode richten wir eine Reihe von <code>Author</code>-Objekten ein, damit wir unsere Paginierung testen können.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>from django.test import TestCase from django.urls import reverse from catalog.models import Author class AuthorListViewTest(TestCase): @classmethod def setUpTestData(cls): # Create 13 authors for pagination tests number_of_authors = 13 for author_id in range(number_of_authors): Author.objects.create( first_name=f'Dominique {author_id}', last_name=f'Surname {author_id}', ) def test_view_url_exists_at_desired_location(self): response = self.client.get('/catalog/authors/') self.assertEqual(response.status_code, 200) def test_view_url_accessible_by_name(self): response = self.client.get(reverse('authors')) self.assertEqual(response.status_code, 200) def test_view_uses_correct_template(self): response = self.client.get(reverse('authors')) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'catalog/author_list.html') def test_pagination_is_ten(self): response = self.client.get(reverse('authors')) self.assertEqual(response.status_code, 200) self.assertTrue('is_paginated' in response.context) self.assertTrue(response.context['is_paginated'] == True) self.assertEqual(len(response.context['author_list']), 10) def test_lists_all_authors(self): # Get second page and confirm it has (exactly) remaining 3 items response = self.client.get(reverse('authors')+'?page=2') self.assertEqual(response.status_code, 200) self.assertTrue('is_paginated' in response.context) self.assertTrue(response.context['is_paginated'] == True) self.assertEqual(len(response.context['author_list']), 3) </code></pre></div> <p>Alle Tests nutzen den Client (der zur <code>TestCase</code>-abgeleiteten Klasse gehört), um eine <code>GET</code>-Anfrage zu simulieren und eine Antwort zu erhalten. Die erste Version überprüft eine spezifische URL (beachten Sie, nur den spezifischen Pfad ohne die Domain), während die zweite die URL aus ihrem Namen in der URL-Konfiguration generiert.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>response = self.client.get('/catalog/authors/') response = self.client.get(reverse('authors')) </code></pre></div> <p>Sobald wir die Antwort haben, fragen wir sie nach ihrem Statuscode, der verwendeten Vorlage, ob die Antwort paginiert ist, der Anzahl der zurückgegebenen Elemente und der Gesamtanzahl der Elemente ab.</p> <div class="notecard note"> <p><strong>Hinweis:</strong> Wenn Sie die Variable <code>paginate_by</code> in Ihrer Datei <strong>/catalog/views.py</strong> auf eine andere Zahl als 10 gesetzt haben, stellen Sie sicher, dass Sie die Zeilen, die testen, ob die korrekte Anzahl von Elementen in paginierten Vorlagen angezeigt wird, im oben genannten und in den folgenden Abschnitten aktualisieren. Zum Beispiel, wenn Sie die Variable für die Autorenlisten-Seite auf 5 gesetzt haben, aktualisieren Sie die Zeile oben auf:</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>self.assertTrue(len(response.context['author_list']) == 5) </code></pre></div> </div> <p> Die interessanteste Variable, die wir oben demonstrieren, ist <code>response.context</code>, welche die Kontextvariable ist, die von der View an die Vorlage übergeben wird. Dies ist unglaublich nützlich beim Testen, da es uns ermöglicht zu bestätigen, dass unsere Vorlage alle Daten erhält, die sie benötigt. Mit anderen Worten, wir können überprüfen, dass wir die beabsichtigte Vorlage verwenden und welche Daten die Vorlage erhält, was viel dazu beiträgt, zu bestätigen, dass alle Renderfehler ausschließlich der Vorlage zuzuschreiben sind. </p> <h4 id="views_die_auf_eingeloggte_benutzer_beschränkt_sind">Views, die auf eingeloggte Benutzer beschränkt sind</h4> <p>In einigen Fällen möchten Sie eine View testen, die nur für eingeloggte Benutzer beschränkt ist. Zum Beispiel ist unsere <code>LoanedBooksByUserListView</code> sehr ähnlich unserer vorherigen Ansicht, aber nur für eingeloggte Benutzer verfügbar und zeigt nur <code>BookInstance</code>-Einträge an, die vom aktuellen Benutzer entliehen wurden, den Status "on loan" haben und "älteste zuerst" geordnet sind.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>from django.contrib.auth.mixins import LoginRequiredMixin class LoanedBooksByUserListView(LoginRequiredMixin, generic.ListView): """Generic class-based view listing books on loan to current user.""" model = BookInstance template_name ='catalog/bookinstance_list_borrowed_user.html' paginate_by = 10 def get_queryset(self): return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back') </code></pre></div> <p>Fügen Sie den folgenden Testcode zu <strong>/catalog/tests/test_views.py</strong> hinzu. Hier verwenden wir zuerst <code>SetUp()</code>, um einige Benutzerkonten und <code>BookInstance</code>-Objekte (zusammen mit ihren zugehörigen Büchern und anderen Einträgen) zu erstellen, die wir später in den Tests verwenden werden. Die Hälfte der Bücher wird von jedem Testbenutzer ausgeliehen, aber wir haben den Status aller Bücher zunächst auf "maintenance" gesetzt. Wir haben <code>SetUp()</code> anstelle von <code>setUpTestData()</code> verwendet, da wir einige dieser Objekte später ändern werden.</p> <div class="notecard note"> <p><strong>Hinweis:</strong> Der <code>setUp()</code>-Code unten erstellt ein Buch mit einer bestimmten <code>Language</code>, aber <em>Ihr</em> Code enthält möglicherweise nicht das <code>Language</code>-Modell, da dies als <em>Herausforderung</em> erstellt wurde. Wenn dies der Fall ist, kommentieren Sie die Teile des Codes aus, die Language-Objekte erzeugen oder importieren. Dies sollten Sie auch im Abschnitt <code>RenewBookInstancesViewTest</code> tun, der folgt.</p> </div> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>import datetime from django.utils import timezone # Get user model from settings from django.contrib.auth import get_user_model User = get_user_model() from catalog.models import BookInstance, Book, Genre, Language class LoanedBookInstancesByUserListViewTest(TestCase): def setUp(self): # Create two users test_user1 = User.objects.create_user(username='testuser1', password='1X&lt;ISRUkw+tuK') test_user2 = User.objects.create_user(username='testuser2', password='2HJ1vRV0Z&amp;3iD') test_user1.save() test_user2.save() # Create a book test_author = Author.objects.create(first_name='John', last_name='Smith') test_genre = Genre.objects.create(name='Fantasy') test_language = Language.objects.create(name='English') test_book = Book.objects.create( title='Book Title', summary='My book summary', isbn='ABCDEFG', author=test_author, language=test_language, ) # Create genre as a post-step genre_objects_for_book = Genre.objects.all() test_book.genre.set(genre_objects_for_book) # Direct assignment of many-to-many types not allowed. test_book.save() # Create 30 BookInstance objects number_of_book_copies = 30 for book_copy in range(number_of_book_copies): return_date = timezone.localtime() + datetime.timedelta(days=book_copy%5) the_borrower = test_user1 if book_copy % 2 else test_user2 status = 'm' BookInstance.objects.create( book=test_book, imprint='Unlikely Imprint, 2016', due_back=return_date, borrower=the_borrower, status=status, ) def test_redirect_if_not_logged_in(self): response = self.client.get(reverse('my-borrowed')) self.assertRedirects(response, '/accounts/login/?next=/catalog/mybooks/') def test_logged_in_uses_correct_template(self): login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK') response = self.client.get(reverse('my-borrowed')) # Check our user is logged in self.assertEqual(str(response.context['user']), 'testuser1') # Check that we got a response "success" self.assertEqual(response.status_code, 200) # Check we used correct template self.assertTemplateUsed(response, 'catalog/bookinstance_list_borrowed_user.html') </code></pre></div> <p>Um zu überprüfen, dass die View zu einer Login-Seite umleitet, wenn der Benutzer nicht eingeloggt ist, verwenden wir <code>assertRedirects</code>, wie in <code>test_redirect_if_not_logged_in()</code> demonstriert. Um zu überprüfen, dass die Seite für einen eingeloggten Benutzer angezeigt wird, loggen wir zuerst unseren Testbenutzer ein und greifen dann erneut auf die Seite zu, um zu überprüfen, ob wir einen <code>status_code</code> von 200 erhalten (Erfolg).</p> <p>Die restlichen Tests überprüfen, dass unsere View nur Bücher zurückgibt, die bei unserem aktuellen Entleiher "ausgeliehen" sind. Kopieren Sie den untenstehenden Code und fügen Sie ihn an das Ende der oben genannten Testklasse ein.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code> def test_only_borrowed_books_in_list(self): login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK') response = self.client.get(reverse('my-borrowed')) # Check our user is logged in self.assertEqual(str(response.context['user']), 'testuser1') # Check that we got a response "success" self.assertEqual(response.status_code, 200) # Check that initially we don't have any books in list (none on loan) self.assertTrue('bookinstance_list' in response.context) self.assertEqual(len(response.context['bookinstance_list']), 0) # Now change all books to be on loan books = BookInstance.objects.all()[:10] for book in books: book.status = 'o' book.save() # Check that now we have borrowed books in the list response = self.client.get(reverse('my-borrowed')) # Check our user is logged in self.assertEqual(str(response.context['user']), 'testuser1') # Check that we got a response "success" self.assertEqual(response.status_code, 200) self.assertTrue('bookinstance_list' in response.context) # Confirm all books belong to testuser1 and are on loan for book_item in response.context['bookinstance_list']: self.assertEqual(response.context['user'], book_item.borrower) self.assertEqual(book_item.status, 'o') def test_pages_ordered_by_due_date(self): # Change all books to be on loan for book in BookInstance.objects.all(): book.status='o' book.save() login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK') response = self.client.get(reverse('my-borrowed')) # Check our user is logged in self.assertEqual(str(response.context['user']), 'testuser1') # Check that we got a response "success" self.assertEqual(response.status_code, 200) # Confirm that of the items, only 10 are displayed due to pagination. self.assertEqual(len(response.context['bookinstance_list']), 10) last_date = 0 for book in response.context['bookinstance_list']: if last_date == 0: last_date = book.due_back else: self.assertTrue(last_date &lt;= book.due_back) last_date = book.due_back </code></pre></div> <p>Sie könnten auch Paginierungstests hinzufügen, wenn Sie möchten!</p> <h4 id="testen_von_views_mit_formularen">Testen von Views mit Formularen</h4> <p>Das Testen von Views mit Formularen ist etwas komplizierter als in den oben genannten Fällen, da Sie mehr Codepfade testen müssen: Erstanzeige, Anzeige nach fehlgeschlagener Datenvalidierung und Anzeige nach erfolgreicher Validierung. Die gute Nachricht ist, dass wir den Client für das Testen fast genau auf die gleiche Weise wie für Anzeige-Only-Views verwenden.</p> <p>Um dies zu demonstrieren, schreiben wir einige Tests für die View, die verwendet wird, um Bücher zu erneuern (<code>renew_book_librarian()</code>):</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>from catalog.forms import RenewBookForm @permission_required('catalog.can_mark_returned') def renew_book_librarian(request, pk): """View function for renewing a specific BookInstance by librarian.""" book_instance = get_object_or_404(BookInstance, pk=pk) # If this is a POST request then process the Form data if request.method == 'POST': # Create a form instance and populate it with data from the request (binding): book_renewal_form = RenewBookForm(request.POST) # Check if the form is valid: if form.is_valid(): # process the data in form.cleaned_data as required (here we just write it to the model due_back field) book_instance.due_back = form.cleaned_data['renewal_date'] book_instance.save() # redirect to a new URL: return HttpResponseRedirect(reverse('all-borrowed')) # If this is a GET (or any other method) create the default form else: proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3) book_renewal_form = RenewBookForm(initial={'renewal_date': proposed_renewal_date}) context = { 'book_renewal_form': book_renewal_form, 'book_instance': book_instance, } return render(request, 'catalog/book_renew_librarian.html', context) </code></pre></div> <p>Wir müssen testen, dass die View nur für Benutzer verfügbar ist, die die Berechtigung <code>can_mark_returned</code> haben, und dass Benutzer auf eine HTTP 404-Fehlerseite umgeleitet werden, wenn sie versuchen, eine <code>BookInstance</code> zu erneuern, die nicht existiert. Wir sollten überprüfen, dass der Initialwert des Formulars mit einem Datum drei Wochen in der Zukunft gefüllt ist, und dass, wenn die Validierung erfolgreich ist, wir zur "alle entliehenen Bücher"-View umgeleitet werden. Beim Überprüfen der Validierungsfehler-Tests überprüfen wir auch, dass unser Formular die entsprechenden Fehlermeldungen sendet.</p> <p> Fügen Sie den ersten Teil der Testklasse (wie unten gezeigt) zum Ende von <strong>/catalog/tests/test_views.py</strong> hinzu. Diese erstellt zwei Benutzer und zwei Buchinstanzen, aber nur ein Benutzer erhält die Berechtigung, die View zuzugreifen. </p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>import uuid from django.contrib.auth.models import Permission # Required to grant the permission needed to set a book as returned. class RenewBookInstancesViewTest(TestCase): def setUp(self): # Create a user test_user1 = User.objects.create_user(username='testuser1', password='1X&lt;ISRUkw+tuK') test_user2 = User.objects.create_user(username='testuser2', password='2HJ1vRV0Z&amp;3iD') test_user1.save() test_user2.save() # Give test_user2 permission to renew books. permission = Permission.objects.get(name='Set book as returned') test_user2.user_permissions.add(permission) test_user2.save() # Create a book test_author = Author.objects.create(first_name='John', last_name='Smith') test_genre = Genre.objects.create(name='Fantasy') test_language = Language.objects.create(name='English') test_book = Book.objects.create( title='Book Title', summary='My book summary', isbn='ABCDEFG', author=test_author, language=test_language, ) # Create genre as a post-step genre_objects_for_book = Genre.objects.all() test_book.genre.set(genre_objects_for_book) # Direct assignment of many-to-many types not allowed. test_book.save() # Create a BookInstance object for test_user1 return_date = datetime.date.today() + datetime.timedelta(days=5) self.test_bookinstance1 = BookInstance.objects.create( book=test_book, imprint='Unlikely Imprint, 2016', due_back=return_date, borrower=test_user1, status='o', ) # Create a BookInstance object for test_user2 return_date = datetime.date.today() + datetime.timedelta(days=5) self.test_bookinstance2 = BookInstance.objects.create( book=test_book, imprint='Unlikely Imprint, 2016', due_back=return_date, borrower=test_user2, status='o', ) </code></pre></div> <p>Fügen Sie die folgenden Tests am Ende der Testklasse hinzu. Diese überprüfen, dass nur Benutzer mit den richtigen Berechtigungen (<em>testuser2</em>) auf die View zugreifen können. Wir überprüfen alle Fälle: wenn der Benutzer nicht eingeloggt ist, wenn ein Benutzer eingeloggt ist, aber nicht die richtigen Berechtigungen hat, wenn der Benutzer Berechtigungen hat, aber nicht der Entleiher ist (sollte erfolgreich sein), und was passiert, wenn sie auf eine <code>BookInstance</code> zugreifen, die nicht existiert. Wir überprüfen auch, dass die korrekte Vorlage verwendet wird.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code> def test_redirect_if_not_logged_in(self): response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk})) # Manually check redirect (Can't use assertRedirect, because the redirect URL is unpredictable) self.assertEqual(response.status_code, 302) self.assertTrue(response.url.startswith('/accounts/login/')) def test_forbidden_if_logged_in_but_not_correct_permission(self): login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK') response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk})) self.assertEqual(response.status_code, 403) def test_logged_in_with_permission_borrowed_book(self): login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD') response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance2.pk})) # Check that it lets us login - this is our book and we have the right permissions. self.assertEqual(response.status_code, 200) def test_logged_in_with_permission_another_users_borrowed_book(self): login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD') response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk})) # Check that it lets us login. We're a librarian, so we can view any users book self.assertEqual(response.status_code, 200) def test_HTTP404_for_invalid_book_if_logged_in(self): # unlikely UID to match our bookinstance! test_uid = uuid.uuid4() login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD') response = self.client.get(reverse('renew-book-librarian', kwargs={'pk':test_uid})) self.assertEqual(response.status_code, 404) def test_uses_correct_template(self): login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD') response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk})) self.assertEqual(response.status_code, 200) # Check we used correct template self.assertTemplateUsed(response, 'catalog/book_renew_librarian.html') </code></pre></div> <p>Fügen Sie die nächste Testmethode hinzu, wie unten gezeigt. Diese überprüft, ob das Anfangsdatum für das Formular drei Wochen in der Zukunft liegt. Beachten Sie, wie wir in der Lage sind, auf den Initialwert des Formularfeldes (<code>response.context['form'].initial['renewal_date'])</code> zuzugreifen.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code> def test_form_renewal_date_initially_has_date_three_weeks_in_future(self): login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD') response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk})) self.assertEqual(response.status_code, 200) date_3_weeks_in_future = datetime.date.today() + datetime.timedelta(weeks=3) self.assertEqual(response.context['form'].initial['renewal_date'], date_3_weeks_in_future) </code></pre></div> <p>Der nächste Test (fügen Sie diesen auch zur Klasse hinzu) überprüft, dass die View bei erfolgreicher Erneuerung zu einer Liste aller entliehenen Bücher umgeleitet wird. Was hier abweicht, ist, dass wir zum ersten Mal zeigen, wie Sie Daten mit dem Client <code>POST</code>-en können. Die <code>post</code>-Daten sind das zweite Argument der Post-Funktion und werden als Wörterbuch von Schlüssel/Werten angegeben.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code> def test_redirects_to_all_borrowed_book_list_on_success(self): login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD') valid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=2) response = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future}) self.assertRedirects(response, reverse('all-borrowed')) </code></pre></div> <div class="notecard warning"> <p><strong>Warnung:</strong> Der <em>all-borrowed</em>-View wurde als <em>Herausforderung</em> hinzugefügt, und Ihr Code könnte stattdessen zur Startseite '/' umleiten. Wenn ja, ändern Sie die letzten beiden Zeilen des Testcodes so, dass sie wie der unten dargestellte Code sind. Das <code>follow=True</code> in der Anfrage stellt sicher, dass die Anfrage die endgültige Ziel-URL zurückgibt (daher das Prüfen auf <code>/catalog/</code> statt auf <code>/</code>).</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code> response = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future}, follow=True) self.assertRedirects(response, '/catalog/') </code></pre></div> </div> <p>Kopieren Sie die letzten beiden Funktionen in die Klasse, wie unten gezeigt. Diese testen erneut <code>POST</code>-Anfragen, aber in diesem Fall mit ungültigen Erneuerungsdaten. Wir verwenden <code>assertFormError()</code>, um zu überprüfen, dass die Fehlermeldungen wie erwartet sind.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code> def test_form_invalid_renewal_date_past(self): login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD') date_in_past = datetime.date.today() - datetime.timedelta(weeks=1) response = self.client.post(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}), {'renewal_date': date_in_past}) self.assertEqual(response.status_code, 200) self.assertFormError(response.context['form'], 'renewal_date', 'Invalid date - renewal in past') def test_form_invalid_renewal_date_future(self): login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD') invalid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=5) response = self.client.post(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}), {'renewal_date': invalid_date_in_future}) self.assertEqual(response.status_code, 200) self.assertFormError(response.context['form'], 'renewal_date', 'Invalid date - renewal more than 4 weeks ahead') </code></pre></div> <p>Die gleichen Techniken können verwendet werden, um die andere View zu testen.</p></div></section><section aria-labelledby="vorlagen"><h3 id="vorlagen"><a href="#vorlagen">Vorlagen</a></h3><div class="section-content"><p>Django bietet Test-APIs, um zu überprüfen, dass die korrekte Vorlage von Ihren Views aufgerufen wird und um zu überprüfen, dass die korrekten Informationen geschickt werden. Es gibt jedoch keine spezifische API-Unterstützung im Django, um zu testen, dass Ihre HTML-Ausgabe wie erwartet gerendert wird.</p></div></section><section aria-labelledby="weitere_empfohlene_testwerkzeuge"><h2 id="weitere_empfohlene_testwerkzeuge"><a href="#weitere_empfohlene_testwerkzeuge">Weitere empfohlene Testwerkzeuge</a></h2><div class="section-content"><p>Das Testframework von Django kann Ihnen helfen, effektive Unit- und Integrationstests zu schreiben — wir haben nur an der Oberfläche dessen gekratzt, was das zugrunde liegende <strong>unittest</strong>-Framework tun kann, geschweige denn, was Django hinzufügt (zum Beispiel versuchen Sie herauszufinden, wie <a href="https://docs.python.org/3/library/unittest.mock-examples.html" class="external" target="_blank">unittest.mock</a> verwendet wird, um Bibliotheken von Drittanbietern zu patchen, damit Sie Ihren eigenen Code gründlicher testen können).</p> <p>Während es zahlreiche andere Testwerkzeuge gibt, die Sie verwenden können, werden wir nur zwei hervorheben:</p> <ul> <li><a href="https://coverage.readthedocs.io/en/latest/" class="external" target="_blank">Coverage</a>: Dieses Python-Tool berichtet darüber, wie viel Code durch Ihre Tests tatsächlich ausgeführt wird. Es ist besonders nützlich, wenn Sie anfangen und herausfinden möchten, was genau getestet werden soll.</li> <li><a href="/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment">Selenium</a> ist ein Framework zur Automatisierung der Tests in einem echten Browser. Es ermöglicht Ihnen, einen echten Benutzer zu simulieren, der mit der Seite interagiert, und bietet ein großartiges Framework für systematische Tests Ihrer Seite (der nächste Schritt von Integrationstests).</li> </ul></div></section><section aria-labelledby="fordern_sie_sich_selbst_heraus"><h2 id="fordern_sie_sich_selbst_heraus"><a href="#fordern_sie_sich_selbst_heraus">Fordern Sie sich selbst heraus</a></h2><div class="section-content"><p>Es gibt noch viele weitere Modelle und Ansichten, die wir testen können. Als Herausforderung versuchen Sie, einen Testfall für die <code>AuthorCreate</code>-View zu erstellen.</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>class AuthorCreate(PermissionRequiredMixin, CreateView): model = Author fields = ['first_name', 'last_name', 'date_of_birth', 'date_of_death'] initial = {'date_of_death': '11/11/2023'} permission_required = 'catalog.add_author' </code></pre></div> <p> Denken Sie daran, dass Sie alles überprüfen müssen, was Sie spezifizieren oder was Teil des Designs ist. Dies umfasst, wer Zugang hat, das Anfangsdatum, die verwendete Vorlage und wohin die View bei Erfolg umleitet. </p> <p>Sie könnten den folgenden Code verwenden, um Ihren Test einzurichten und Ihrem Benutzer die entsprechende Berechtigung zu erteilen</p> <div class="code-example"><div class="example-header"><span class="language-name">python</span></div><pre class="brush: python notranslate"><code>class AuthorCreateViewTest(TestCase): """Test case for the AuthorCreate view (Created as Challenge).""" def setUp(self): # Create a user test_user = User.objects.create_user( username='test_user', password='some_password') content_typeAuthor = ContentType.objects.get_for_model(Author) permAddAuthor = Permission.objects.get( codename="add_author", content_type=content_typeAuthor, ) test_user.user_permissions.add(permAddAuthor) test_user.save() </code></pre></div></div></section><section aria-labelledby="zusammenfassung"><h2 id="zusammenfassung"><a href="#zusammenfassung">Zusammenfassung</a></h2><div class="section-content"><p>Testcode zu schreiben ist weder lustig noch glamourös und wird daher oft zuletzt (oder gar nicht) beim Erstellen einer Website erledigt. Es ist jedoch ein wesentlicher Teil, um sicherzustellen, dass Ihr Code nach Änderungen sicher veröffentlicht werden kann und wirtschaftlich zu warten ist.</p> <p>In diesem Tutorial haben wir Ihnen gezeigt, wie Sie Tests für Ihre Modelle, Formulare und Views schreiben und ausführen. Am wichtigsten ist, dass wir eine kurze Zusammenfassung dessen gegeben haben, was Sie testen sollten, was oft das Schwierigste zu bestimmen ist, wenn Sie anfangen. Es gibt viel mehr zu wissen, aber selbst mit dem, was Sie bereits gelernt haben, sollten Sie in der Lage sein, effektive Unittests für Ihre Websites zu erstellen.</p> <p>Das nächste und letzte Tutorial zeigt, wie Sie Ihre wunderbare (und vollständig getestete!) Django-Website bereitstellen können.</p></div></section><section aria-labelledby="siehe_auch"><h2 id="siehe_auch"><a href="#siehe_auch">Siehe auch</a></h2><div class="section-content"><ul> <li><a href="https://docs.djangoproject.com/en/5.0/topics/testing/overview/" class="external" target="_blank">Schreiben und Ausführen von Tests</a> (Django-Dokumentation)</li> <li><a href="https://docs.djangoproject.com/en/5.0/intro/tutorial05/" class="external" target="_blank">Schreiben Ihrer ersten Django-App, Teil 5 &gt; Einführung in automatisierte Tests</a> (Django-Dokumentation)</li> <li><a href="https://docs.djangoproject.com/en/5.0/topics/testing/tools/" class="external" target="_blank">Testing-Tools-Referenz</a> (Django-Dokumentation)</li> <li><a href="https://docs.djangoproject.com/en/5.0/topics/testing/advanced/" class="external" target="_blank">Erweiterte Testthemen</a> (Django-Dokumentation)</li> <li><a href="https://toastdriven.com/blog/2011/apr/09/guide-to-testing-in-django/" class="external" target="_blank">Ein Leitfaden zum Testen in Django</a> (Toast Driven Blog, 2011)</li> <li><a href="https://test-driven-django-development.readthedocs.io/en/latest/index.html" class="external" target="_blank">Workshop: Testgetriebene Webentwicklung mit Django</a> (San Diego Python, 2014)</li> <li><a href="https://realpython.com/testing-in-django-part-1-best-practices-and-examples/" class="external" target="_blank">Testen in Django (Teil 1) - Best Practices und Beispiele</a> (RealPython, 2013)</li> </ul><ul class="prev-next"> <li><a class="button secondary" href="/de/docs/Learn/Server-side/Django/Forms"><span class="button-wrap"> Zurück </span></a></li> <li><a class="button secondary" href="/de/docs/Learn/Server-side/Django"><span class="button-wrap"> Übersicht: Django Web Framework (Python)</span></a></li> <li><a class="button secondary" href="/de/docs/Learn/Server-side/Django/Deployment"><span class="button-wrap"> Weiter </span></a></li> </ul></div></section></article><aside class="article-footer"><div class="article-footer-inner"><div class="svg-container"><svg xmlns="http://www.w3.org/2000/svg" width="162" height="162" viewBox="0 0 162 162" fill="none" role="none"><mask id="b" fill="#fff"><path d="M97.203 47.04c8.113-7.886 18.004-13.871 28.906-17.492a78 78 0 0 1 33.969-3.39c11.443 1.39 22.401 5.295 32.024 11.411s17.656 14.28 23.476 23.86c5.819 9.579 9.269 20.318 10.083 31.385a69.85 69.85 0 0 1-5.387 32.44c-4.358 10.272-11.115 19.443-19.747 26.801-8.632 7.359-18.908 12.709-30.034 15.637l-6.17-21.698c7.666-2.017 14.746-5.703 20.694-10.773 5.948-5.071 10.603-11.389 13.606-18.467a48.14 48.14 0 0 0 3.712-22.352c-.561-7.625-2.938-15.025-6.948-21.625s-9.544-12.226-16.175-16.44-14.181-6.904-22.065-7.863a53.75 53.75 0 0 0-23.405 2.336c-7.513 2.495-14.327 6.62-19.918 12.053z"></path></mask><path stroke="url(#a)" stroke-dasharray="6, 6" stroke-width="2" d="M97.203 47.04c8.113-7.886 18.004-13.871 28.906-17.492a78 78 0 0 1 33.969-3.39c11.443 1.39 22.401 5.295 32.024 11.411s17.656 14.28 23.476 23.86c5.819 9.579 9.269 20.318 10.083 31.385a69.85 69.85 0 0 1-5.387 32.44c-4.358 10.272-11.115 19.443-19.747 26.801-8.632 7.359-18.908 12.709-30.034 15.637l-6.17-21.698c7.666-2.017 14.746-5.703 20.694-10.773 5.948-5.071 10.603-11.389 13.606-18.467a48.14 48.14 0 0 0 3.712-22.352c-.561-7.625-2.938-15.025-6.948-21.625s-9.544-12.226-16.175-16.44-14.181-6.904-22.065-7.863a53.75 53.75 0 0 0-23.405 2.336c-7.513 2.495-14.327 6.62-19.918 12.053z" mask="url(#b)" style="stroke:url(#a)" transform="translate(-63.992 -25.587)"></path><ellipse cx="8.066" cy="111.597" fill="var(--background-tertiary)" rx="53.677" ry="53.699" transform="matrix(.71707 -.697 .7243 .6895 0 0)"></ellipse><g clip-path="url(#c)" transform="translate(-63.992 -25.587)"><path fill="#9abff5" d="m144.256 137.379 32.906 12.434a4.41 4.41 0 0 1 2.559 5.667l-9.326 24.679a4.41 4.41 0 0 1-5.667 2.559l-8.226-3.108-2.332 6.17c-.466 1.233-.375 1.883-1.609 1.417l-2.253-.527c-.411-.155-.95-.594-1.206-1.161l-4.734-10.484-12.545-4.741a4.41 4.41 0 0 1-2.559-5.667l9.325-24.679a4.41 4.41 0 0 1 5.667-2.559m9.961 29.617 8.227 3.108 3.264-8.638-.498-6.768-4.113-1.555.548 7.258-4.319-1.632zm-12.339-4.663 8.226 3.108 3.264-8.637-.498-6.769-4.113-1.554.548 7.257-4.319-1.632z"></path></g><g clip-path="url(#d)" transform="translate(-63.992 -25.587)"><path fill="#81b0f3" d="M135.35 60.136 86.67 41.654c-3.346-1.27-7.124.428-8.394 3.775L64.414 81.938c-1.27 3.347.428 7.125 3.774 8.395l12.17 4.62-3.465 9.128c-.693 1.826-1.432 2.457.394 3.15l3.014 1.625c.609.231 1.637.274 2.477-.104l15.53-6.983 18.56 7.047c3.346 1.27 7.124-.428 8.395-3.775l13.862-36.51c1.27-3.346-.428-7.124-3.775-8.395M95.261 83.207l-12.17-4.62 4.852-12.779 7.19-7.017 6.085 2.31-7.725 7.51 6.389 2.426zm18.255 6.93-12.17-4.62 4.852-12.778 7.189-7.017 6.085 2.31-7.725 7.51 6.39 2.426z"></path></g><defs><clipPath id="c"><path fill="#fff" d="m198.638 146.586-65.056-24.583-24.583 65.057 65.056 24.582z"></path></clipPath><clipPath id="d"><path fill="#fff" d="m66.438 14.055 96.242 36.54-36.54 96.243-96.243-36.54z"></path></clipPath><linearGradient id="a" x1="97.203" x2="199.995" y1="47.04" y2="152.793" gradientUnits="userSpaceOnUse"><stop stop-color="#086DFC"></stop><stop offset="0.246" stop-color="#2C81FA"></stop><stop offset="0.516" stop-color="#5497F8"></stop><stop offset="0.821" stop-color="#80B0F6"></stop><stop offset="1" stop-color="#9ABFF5"></stop></linearGradient></defs></svg></div><h2>MDN-Feedback-Box</h2><fieldset class="feedback"><label>War diese Übersetzung hilfreich?</label><div class="button-container"><button type="button" class="button primary has-icon yes"><span class="button-wrap"><span class="icon icon-thumbs-up "></span>Ja</span></button><button type="button" class="button primary has-icon no"><span class="button-wrap"><span class="icon icon-thumbs-down "></span>Nein</span></button></div></fieldset><p class="last-modified-date">Diese Seite wurde automatisch aus dem Englischen übersetzt.</p><div id="on-github" class="on-github"><a href="https://github.com/mdn/translated-content-de/blob/main/files/de/learn/server-side/django/testing/index.md?plain=1" title="Folder: de/learn/server-side/django/testing (Opens in a new tab)" target="_blank" rel="noopener noreferrer">Übersetzung auf GitHub anzeigen</a> <!-- -->•<!-- --> <a href="https://github.com/mdn/translated-content-de/issues/new?template=page-report-de.yml&amp;mdn-url=https%3A%2F%2Fdeveloper.mozilla.org%2Fde%2Fdocs%2FLearn%2FServer-side%2FDjango%2FTesting&amp;metadata=%3C%21--+Do+not+make+changes+below+this+line+--%3E%0A%3Cdetails%3E%0A%3Csummary%3EPage+report+details%3C%2Fsummary%3E%0A%0A*+Folder%3A+%60de%2Flearn%2Fserver-side%2Fdjango%2Ftesting%60%0A*+MDN+URL%3A+https%3A%2F%2Fdeveloper.mozilla.org%2Fde%2Fdocs%2FLearn%2FServer-side%2FDjango%2FTesting%0A*+GitHub+URL%3A+https%3A%2F%2Fgithub.com%2Fmdn%2Ftranslated-content-de%2Fblob%2Fmain%2Ffiles%2Fde%2Flearn%2Fserver-side%2Fdjango%2Ftesting%2Findex.md%0A*+Last+commit%3A+https%3A%2F%2Fgithub.com%2Fmdn%2Ftranslated-content-de%2Fcommit%2Fnull%0A*+Document+last+modified%3A+*date+not+known*%0A%0A%3C%2Fdetails%3E" title="This will take you to GitHub to file a new issue." target="_blank" rel="noopener noreferrer">Fehler mit dieser Übersetzung melden</a></div></div></aside></main></div></div><footer id="nav-footer" class="page-footer"><div class="page-footer-grid"><div class="page-footer-logo-col"><a href="/" class="mdn-footer-logo" aria-label="MDN homepage"><svg width="48" height="17" viewBox="0 0 48 17" fill="none" xmlns="http://www.w3.org/2000/svg"><title id="mdn-footer-logo-svg">MDN logo</title><path d="M20.04 16.512H15.504V10.416C15.504 9.488 15.344 8.824 15.024 8.424C14.72 8.024 14.264 7.824 13.656 7.824C12.92 7.824 12.384 8.064 12.048 8.544C11.728 9.024 11.568 9.64 11.568 10.392V14.184H13.008V16.512H8.472V10.416C8.472 9.488 8.312 8.824 7.992 8.424C7.688 8.024 7.232 7.824 6.624 7.824C5.872 7.824 5.336 8.064 5.016 8.544C4.696 9.024 4.536 9.64 4.536 10.392V14.184H6.6V16.512H0V14.184H1.44V8.04H0.024V5.688H4.536V7.32C5.224 6.088 6.32 5.472 7.824 5.472C8.608 5.472 9.328 5.664 9.984 6.048C10.64 6.432 11.096 7.016 11.352 7.8C11.992 6.248 13.168 5.472 14.88 5.472C15.856 5.472 16.72 5.776 17.472 6.384C18.224 6.992 18.6 7.936 18.6 9.216V14.184H20.04V16.512Z" fill="currentColor"></path><path d="M33.6714 16.512H29.1354V14.496C28.8314 15.12 28.3834 15.656 27.7914 16.104C27.1994 16.536 26.4154 16.752 25.4394 16.752C24.0154 16.752 22.8954 16.264 22.0794 15.288C21.2634 14.312 20.8554 12.984 20.8554 11.304C20.8554 9.688 21.2554 8.312 22.0554 7.176C22.8554 6.04 24.0634 5.472 25.6794 5.472C26.5594 5.472 27.2794 5.648 27.8394 6C28.3994 6.352 28.8314 6.8 29.1354 7.344V2.352H26.9754V0H32.2314V14.184H33.6714V16.512ZM29.1354 11.04V10.776C29.1354 9.88 28.8954 9.184 28.4154 8.688C27.9514 8.176 27.3674 7.92 26.6634 7.92C25.9754 7.92 25.3674 8.176 24.8394 8.688C24.3274 9.2 24.0714 10.008 24.0714 11.112C24.0714 12.152 24.3114 12.944 24.7914 13.488C25.2714 14.032 25.8394 14.304 26.4954 14.304C27.3114 14.304 27.9514 13.96 28.4154 13.272C28.8954 12.584 29.1354 11.84 29.1354 11.04Z" fill="currentColor"></path><path d="M47.9589 16.512H41.9829V14.184H43.4229V10.416C43.4229 9.488 43.2629 8.824 42.9429 8.424C42.6389 8.024 42.1829 7.824 41.5749 7.824C40.8389 7.824 40.2709 8.056 39.8709 8.52C39.4709 8.968 39.2629 9.56 39.2469 10.296V14.184H40.6869V16.512H34.7109V14.184H36.1509V8.04H34.5909V5.688H39.2469V7.344C39.9669 6.096 41.1269 5.472 42.7269 5.472C43.7509 5.472 44.6389 5.776 45.3909 6.384C46.1429 6.992 46.5189 7.936 46.5189 9.216V14.184H47.9589V16.512Z" fill="currentColor"></path></svg></a><p>Your blueprint for a better internet.</p><ul class="social-icons"><li><a href="https://mozilla.social/@mdn" target="_blank" rel="me noopener noreferrer"><span class="icon icon-mastodon"></span><span class="visually-hidden">MDN on Mastodon</span></a></li><li><a href="https://twitter.com/mozdevnet" target="_blank" rel="noopener noreferrer"><span class="icon icon-twitter-x"></span><span class="visually-hidden">MDN on X (formerly Twitter)</span></a></li><li><a href="https://github.com/mdn/" target="_blank" rel="noopener noreferrer"><span class="icon icon-github-mark-small"></span><span class="visually-hidden">MDN on GitHub</span></a></li><li><a href="/en-US/blog/rss.xml" target="_blank"><span class="icon icon-feed"></span><span class="visually-hidden">MDN Blog RSS Feed</span></a></li></ul></div><div class="page-footer-nav-col-1"><h2 class="footer-nav-heading">MDN</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a href="/en-US/about">About</a></li><li class="footer-nav-item"><a href="/en-US/blog/">Blog</a></li><li class="footer-nav-item"><a href="https://www.mozilla.org/en-US/careers/listings/?team=ProdOps" target="_blank" rel="noopener noreferrer">Careers</a></li><li class="footer-nav-item"><a href="/en-US/advertising">Advertise with us</a></li></ul></div><div class="page-footer-nav-col-2"><h2 class="footer-nav-heading">Support</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="https://support.mozilla.org/products/mdn-plus">Product help</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/de/docs/MDN/Community/Issues">Report an issue</a></li></ul></div><div class="page-footer-nav-col-3"><h2 class="footer-nav-heading">Our communities</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="/en-US/community">MDN Community</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="https://discourse.mozilla.org/c/mdn/236" target="_blank" rel="noopener noreferrer">MDN Forum</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/discord" target="_blank" rel="noopener noreferrer">MDN Chat</a></li></ul></div><div class="page-footer-nav-col-4"><h2 class="footer-nav-heading">Developers</h2><ul class="footer-nav-list"><li class="footer-nav-item"><a class="footer-nav-link" href="/de/docs/Web">Web Technologies</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/de/docs/Learn">Learn Web Development</a></li><li class="footer-nav-item"><a class="footer-nav-link" href="/de/plus">MDN Plus</a></li><li class="footer-nav-item"><a href="https://hacks.mozilla.org/" target="_blank" rel="noopener noreferrer">Hacks Blog</a></li></ul></div><div class="page-footer-moz"><a href="https://www.mozilla.org/" class="footer-moz-logo-link" target="_blank" rel="noopener noreferrer"><svg width="112" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><title id="mozilla-footer-logo-svg">Mozilla logo</title><path d="M41.753 14.218c-2.048 0-3.324 1.522-3.324 4.157 0 2.423 1.119 4.286 3.29 4.286 2.082 0 3.447-1.678 3.447-4.347 0-2.826-1.522-4.096-3.413-4.096Zm54.89 7.044c0 .901.437 1.618 1.645 1.618 1.427 0 2.949-1.024 3.044-3.352-.649-.095-1.365-.185-2.02-.185-1.426-.005-2.668.397-2.668 1.92Z" fill="currentColor"></path><path d="M0 0v32h111.908V0H0Zm32.56 25.426h-5.87v-7.884c0-2.423-.806-3.352-2.39-3.352-1.924 0-2.702 1.365-2.702 3.324v4.868h1.864v3.044h-5.864v-7.884c0-2.423-.806-3.352-2.39-3.352-1.924 0-2.702 1.365-2.702 3.324v4.868h2.669v3.044H6.642v-3.044h1.863v-7.918H6.642V11.42h5.864v2.11c.839-1.489 2.3-2.39 4.252-2.39 2.02 0 3.878.963 4.566 3.01.778-1.862 2.361-3.01 4.566-3.01 2.512 0 4.812 1.522 4.812 4.84v6.402h1.863v3.044h-.005Zm9.036.307c-4.314 0-7.296-2.635-7.296-7.106 0-4.096 2.484-7.481 7.514-7.481s7.481 3.38 7.481 7.29c0 4.472-3.228 7.297-7.699 7.297Zm22.578-.307H51.942l-.403-2.11 7.7-8.846h-4.376l-.621 2.17-2.888-.313.498-4.907h12.294l.313 2.11-7.767 8.852h4.533l.654-2.172 3.167.308-.872 4.908Zm7.99 0h-4.191v-5.03h4.19v5.03Zm0-8.976h-4.191v-5.03h4.19v5.03Zm2.618 8.976 6.054-21.358h3.945l-6.054 21.358h-3.945Zm8.136 0 6.048-21.358h3.945l-6.054 21.358h-3.939Zm21.486.307c-1.863 0-2.887-1.085-3.072-2.792-.805 1.427-2.232 2.792-4.498 2.792-2.02 0-4.314-1.085-4.314-4.006 0-3.447 3.323-4.253 6.518-4.253.778 0 1.584.034 2.3.124v-.465c0-1.427-.034-3.133-2.3-3.133-.84 0-1.488.061-2.143.402l-.453 1.578-3.195-.34.549-3.224c2.45-.996 3.692-1.27 5.992-1.27 3.01 0 5.556 1.55 5.556 4.75v6.083c0 .805.314 1.085.963 1.085.184 0 .375-.034.587-.095l.034 2.11a5.432 5.432 0 0 1-2.524.654Z" fill="currentColor"></path></svg></a><ul class="footer-moz-list"><li class="footer-moz-item"><a href="https://www.mozilla.org/privacy/websites/" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Website Privacy Notice</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/privacy/websites/#cookies" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Cookies</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/about/legal/terms/mozilla" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Legal</a></li><li class="footer-moz-item"><a href="https://www.mozilla.org/about/governance/policies/participation/" class="footer-moz-link" target="_blank" rel="noopener noreferrer">Community Participation Guidelines</a></li></ul></div><div class="page-footer-legal"><p id="license" class="page-footer-legal-text">Visit<!-- --> <a href="https://www.mozilla.org" target="_blank" rel="noopener noreferrer">Mozilla Corporation’s</a> <!-- -->not-for-profit parent, the<!-- --> <a target="_blank" rel="noopener noreferrer" href="https://foundation.mozilla.org/">Mozilla Foundation</a>.<br/>Portions of this content are ©1998–<!-- -->2024<!-- --> by individual mozilla.org contributors. Content available under<!-- --> <a href="/de/docs/MDN/Writing_guidelines/Attrib_copyright_license">a Creative Commons license</a>.</p></div></div></footer></div><script type="application/json" id="hydration">{"url":"/de/docs/Learn/Server-side/Django/Testing","doc":{"isMarkdown":true,"isTranslated":true,"isActive":true,"flaws":{},"title":"Django Tutorial Teil 10: Testen einer Django-Webanwendung","mdn_url":"/de/docs/Learn/Server-side/Django/Testing","locale":"de","native":"Deutsch","sidebarHTML":"<ol><li class=\"section\"><a href=\"/de/docs/Learn/Getting_started_with_the_web\">Komplette Anfänger beginnen hier!</a></li><li><details><summary>Erste Schritte mit dem Web</summary><ol><li><a href=\"/de/docs/Learn/Getting_started_with_the_web\">Einführung ins Web</a></li><li><a href=\"/de/docs/Learn/Getting_started_with_the_web/Installing_basic_software\">Installation von grundlegender Software</a></li><li><a href=\"/de/docs/Learn/Getting_started_with_the_web/What_will_your_website_look_like\">Wie wird Ihre Website aussehen?</a></li><li><a href=\"/de/docs/Learn/Getting_started_with_the_web/Dealing_with_files\">Umgang mit Dateien</a></li><li><a href=\"/de/docs/Learn/Getting_started_with_the_web/HTML_basics\">HTML-Grundlagen</a></li><li><a href=\"/de/docs/Learn/Getting_started_with_the_web/CSS_basics\">CSS-Grundlagen</a></li><li><a href=\"/de/docs/Learn/Getting_started_with_the_web/JavaScript_basics\">JavaScript-Grundlagen</a></li><li><a href=\"/de/docs/Learn/Getting_started_with_the_web/Publishing_your_website\">Veröffentlichung Ihrer Website</a></li><li><a href=\"/de/docs/Learn/Getting_started_with_the_web/How_the_Web_works\">Wie das Web funktioniert</a></li></ol></details></li><li class=\"section\"><a href=\"/de/docs/Learn/HTML\">HTML — Strukturierung des Webs</a></li><li><details><summary>Einführung in HTML</summary><ol><li><a href=\"/de/docs/Learn/HTML/Introduction_to_HTML\">Einführung in HTML</a></li><li><a href=\"/de/docs/Learn/HTML/Introduction_to_HTML/Getting_started\">Erste Schritte mit HTML</a></li><li><a href=\"/de/docs/Learn/HTML/Introduction_to_HTML/The_head_metadata_in_HTML\">Was ist im Kopfbereich? Metadaten in HTML</a></li><li><a href=\"/de/docs/Learn/HTML/Introduction_to_HTML/HTML_text_fundamentals\">Grundlagen des HTML-Textes</a></li><li><a href=\"/de/docs/Learn/HTML/Introduction_to_HTML/Creating_hyperlinks\">Erstellen von Hyperlinks</a></li><li><a href=\"/de/docs/Learn/HTML/Introduction_to_HTML/Advanced_text_formatting\">Erweiterte Textformatierung</a></li><li><a href=\"/de/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure\">Struktur eines Dokuments und einer Website</a></li><li><a href=\"/de/docs/Learn/HTML/Introduction_to_HTML/Debugging_HTML\">Debugging HTML</a></li><li><a href=\"/de/docs/Learn/HTML/Introduction_to_HTML/Marking_up_a_letter\">Markierung eines Briefes</a></li><li><a href=\"/de/docs/Learn/HTML/Introduction_to_HTML/Structuring_a_page_of_content\">Die Strukturierung einer Seite mit Inhalt</a></li></ol></details></li><li><details><summary>Multimedia und Einbettung</summary><ol><li><a href=\"/de/docs/Learn/HTML/Multimedia_and_embedding\">Multimedia und Einbettung</a></li><li><a href=\"/de/docs/Learn/HTML/Multimedia_and_embedding/Images_in_HTML\">Bilder in HTML</a></li><li><a href=\"/de/docs/Learn/HTML/Multimedia_and_embedding/Video_and_audio_content\">Video- und Audioinhalte</a></li><li><a href=\"/de/docs/Learn/HTML/Multimedia_and_embedding/Other_embedding_technologies\">Von Objekt zu iframe — andere Einbettungstechnologien</a></li><li><a href=\"/de/docs/Learn/HTML/Multimedia_and_embedding/Adding_vector_graphics_to_the_Web\">Vektorgrafiken zum Web hinzufügen</a></li><li><a href=\"/de/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images\">Responsive Images</a></li><li><a href=\"/de/docs/Learn/HTML/Multimedia_and_embedding/Mozilla_splash_page\">Mozilla Splash-Seite</a></li></ol></details></li><li><details><summary>HTML-Tabellen</summary><ol><li><a href=\"/de/docs/Learn/HTML/Tables\">HTML-Tabellen</a></li><li><a href=\"/de/docs/Learn/HTML/Tables/Basics\">HTML Table Grundlagen</a></li><li><a href=\"/de/docs/Learn/HTML/Tables/Advanced\">Erweiterte Funktionen und Barrierefreiheit von HTML-Tabellen</a></li><li><a href=\"/de/docs/Learn/HTML/Tables/Structuring_planet_data\">Strukturierung von Planeten-Daten</a></li></ol></details></li><li class=\"section\"><a href=\"/de/docs/Learn/CSS\">CSS — Gestaltung des Webs</a></li><li><details><summary>CSS erste Schritte</summary><ol><li><a href=\"/de/docs/Learn/CSS/First_steps\">Erste Schritte mit CSS</a></li><li><a href=\"/de/docs/Learn/CSS/First_steps/What_is_CSS\">Was ist CSS?</a></li><li><a href=\"/de/docs/Learn/CSS/First_steps/Getting_started\">Einstieg in CSS</a></li><li><a href=\"/de/docs/Learn/CSS/First_steps/How_CSS_is_structured\">Wie CSS strukturiert ist</a></li><li><a href=\"/de/docs/Learn/CSS/First_steps/How_CSS_works\">Wie CSS funktioniert</a></li><li><a href=\"/de/docs/Learn/CSS/First_steps/Styling_a_biography_page\">Eine Biografieseite stylen</a></li></ol></details></li><li><details><summary>CSS-Bausteine</summary><ol><li><a href=\"/de/docs/Learn/CSS/Building_blocks\">CSS-Grundbausteine</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Selectors\">CSS-Selektoren</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Selectors/Type_Class_and_ID_Selectors\">Typ-, Klassen- und ID-Selektoren</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Selectors/Attribute_selectors\">Attributselektoren</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Selectors/Pseudo-classes_and_pseudo-elements\">Pseudo-Klassen und Pseudo-Elemente</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Selectors/Combinators\">Kombinatoren</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Cascade_and_inheritance\">Kaskade, Spezifität und Vererbung</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Cascade_layers\">Kaskadenschichten</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/The_box_model\">Das Boxmodell</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Backgrounds_and_borders\">Hintergründe und Rahmen</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Handling_different_text_directions\">Umgang mit unterschiedlichen Textausrichtungen</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Overflowing_content\">Überlaufender Inhalt</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Values_and_units\">CSS-Werte und Einheiten</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Sizing_items_in_CSS\">Größe von Elementen in CSS</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Images_media_form_elements\">Bilder, Medien und Formularelemente</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Styling_tables\">Tabellen stylen</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Advanced_styling_effects\">Erweiterte Styling-Effekte</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Debugging_CSS\">Debugging CSS</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Organizing\">Organisieren Ihres CSS</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Fundamental_CSS_comprehension\">Grundlegendes CSS-Verständnis</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/Creating_fancy_letterheaded_paper\">Erstellen von stilvollem Briefpapier</a></li><li><a href=\"/de/docs/Learn/CSS/Building_blocks/A_cool_looking_box\">Eine cool aussehende Box</a></li></ol></details></li><li><details><summary>Textgestaltung</summary><ol><li><a href=\"/de/docs/Learn/CSS/Styling_text\">CSS Textgestaltung</a></li><li><a href=\"/de/docs/Learn/CSS/Styling_text/Fundamentals\">Grundlegende Text- und Schriftgestaltung</a></li><li><a href=\"/de/docs/Learn/CSS/Styling_text/Styling_lists\">Listen stilisieren</a></li><li><a href=\"/de/docs/Learn/CSS/Styling_text/Styling_links\">Styling von Links</a></li><li><a href=\"/de/docs/Learn/CSS/Styling_text/Web_fonts\">Web-Fonts</a></li><li><a href=\"/de/docs/Learn/CSS/Styling_text/Typesetting_a_homepage\">Setzen einer Community-Schul-Startseite</a></li></ol></details></li><li><details><summary>CSS-Layout</summary><ol><li><a href=\"/de/docs/Learn/CSS/CSS_layout\">CSS-Layout</a></li><li><a href=\"/de/docs/Learn/CSS/CSS_layout/Introduction\">Einführung in CSS Layout</a></li><li><a href=\"/de/docs/Learn/CSS/CSS_layout/Normal_Flow\">Normaler Fluss</a></li><li><a href=\"/de/docs/Learn/CSS/CSS_layout/Flexbox\">Flexbox</a></li><li><a href=\"/de/docs/Learn/CSS/CSS_layout/Grids\">Raster</a></li><li><a href=\"/de/docs/Learn/CSS/CSS_layout/Floats\">Floats</a></li><li><a href=\"/de/docs/Learn/CSS/CSS_layout/Positioning\">Positioning</a></li><li><a href=\"/de/docs/Learn/CSS/CSS_layout/Multiple-column_Layout\">Mehrspaltiges Layout</a></li><li><a href=\"/de/docs/Learn/CSS/CSS_layout/Responsive_Design\">Responsives Design</a></li><li><a href=\"/de/docs/Learn/CSS/CSS_layout/Media_queries\">Einsteigerleitfaden für Media Queries</a></li><li><a href=\"/de/docs/Learn/CSS/CSS_layout/Legacy_Layout_Methods\">Legacy-Layout-Methoden</a></li><li><a href=\"/de/docs/Learn/CSS/CSS_layout/Supporting_Older_Browsers\">Unterstützung älterer Browser</a></li><li><a href=\"/de/docs/Learn/CSS/CSS_layout/Fundamental_Layout_Comprehension\">Grundlegendes Verständnis von Layouts</a></li></ol></details></li><li class=\"section\"><a href=\"/de/docs/Learn/JavaScript\">JavaScript — Dynamisches clientseitiges Skripting</a></li><li><details><summary>JavaScript erste Schritte</summary><ol><li><a href=\"/de/docs/Learn/JavaScript/First_steps\">JavaScript erste Schritte</a></li><li><a href=\"/de/docs/Learn/JavaScript/First_steps/What_is_JavaScript\">Was ist JavaScript?</a></li><li><a href=\"/de/docs/Learn/JavaScript/First_steps/A_first_splash\">Ein erster Sprung in JavaScript</a></li><li><a href=\"/de/docs/Learn/JavaScript/First_steps/What_went_wrong\">Was ist schiefgelaufen? JavaScript-Fehlerbehebung</a></li><li><a href=\"/de/docs/Learn/JavaScript/First_steps/Variables\">Speichern der benötigten Informationen — Variablen</a></li><li><a href=\"/de/docs/Learn/JavaScript/First_steps/Math\">Grundlegende Mathematik in JavaScript – Zahlen und Operatoren</a></li><li><a href=\"/de/docs/Learn/JavaScript/First_steps/Strings\">Umgang mit Text — Strings in JavaScript</a></li><li><a href=\"/de/docs/Learn/JavaScript/First_steps/Useful_string_methods\">Nützliche String-Methoden</a></li><li><a href=\"/de/docs/Learn/JavaScript/First_steps/Arrays\">Arrays</a></li><li><a href=\"/de/docs/Learn/JavaScript/First_steps/Silly_story_generator\">Silly Story Generator</a></li></ol></details></li><li><details><summary>JavaScript-Bausteine</summary><ol><li><a href=\"/de/docs/Learn/JavaScript/Building_blocks\">JavaScript-Bausteine</a></li><li><a href=\"/de/docs/Learn/JavaScript/Building_blocks/conditionals\">Entscheidungen in Ihrem Code treffen — Bedingte Anweisungen</a></li><li><a href=\"/de/docs/Learn/JavaScript/Building_blocks/Looping_code\">Schleifen-Code</a></li><li><a href=\"/de/docs/Learn/JavaScript/Building_blocks/Functions\">Funktionen — wiederverwendbare Codeblöcke</a></li><li><a href=\"/de/docs/Learn/JavaScript/Building_blocks/Build_your_own_function\">Erstellen Sie Ihre eigene Funktion</a></li><li><a href=\"/de/docs/Learn/JavaScript/Building_blocks/Return_values\">Funktionsrückgabewerte</a></li><li><a href=\"/de/docs/Learn/JavaScript/Building_blocks/Events\">Einführung in Ereignisse</a></li><li><a href=\"/de/docs/Learn/JavaScript/Building_blocks/Event_bubbling\">Event bubbling</a></li><li><a href=\"/de/docs/Learn/JavaScript/Building_blocks/Image_gallery\">Bildgalerie</a></li></ol></details></li><li><details><summary>Einführung in JavaScript-Objekte</summary><ol><li><a href=\"/de/docs/Learn/JavaScript/Objects\">Einführung in JavaScript-Objekte</a></li><li><a href=\"/de/docs/Learn/JavaScript/Objects/Basics\">JavaScript Objekt Grundlagen</a></li><li><a href=\"/de/docs/Learn/JavaScript/Objects/Object_prototypes\">Objektprototypen</a></li><li><a href=\"/de/docs/Learn/JavaScript/Objects/Object-oriented_programming\">Objektorientierte Programmierung</a></li><li><a href=\"/de/docs/Learn/JavaScript/Objects/Classes_in_JavaScript\">Klassen in JavaScript</a></li><li><a href=\"/de/docs/Learn/JavaScript/Objects/JSON\">Arbeiten mit JSON</a></li><li><a href=\"/de/docs/Learn/JavaScript/Objects/Object_building_practice\">Objekt-Baupraxis</a></li><li><a href=\"/de/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features\">Hinzufügen von Funktionen zu unserem hüpfenden Kugeln-Demo</a></li></ol></details></li><li><details><summary>Asynchrones JavaScript</summary><ol><li><a href=\"/de/docs/Learn/JavaScript/Asynchronous\">Asynchrones JavaScript</a></li><li><a href=\"/de/docs/Learn/JavaScript/Asynchronous/Introducing\">Einführung in asynchrones JavaScript</a></li><li><a href=\"/de/docs/Learn/JavaScript/Asynchronous/Promises\">Anleitung zur Verwendung von Promises</a></li><li><a href=\"/de/docs/Learn/JavaScript/Asynchronous/Implementing_a_promise-based_API\">Anleitung zur Implementierung einer Promise-basierten API</a></li><li><a href=\"/de/docs/Learn/JavaScript/Asynchronous/Introducing_workers\">Einführung in Workers</a></li><li><a href=\"/de/docs/Learn/JavaScript/Asynchronous/Sequencing_animations\">Sequenzierung von Animationen</a></li></ol></details></li><li><details><summary>Client-seitige Web-APIs</summary><ol><li><a href=\"/de/docs/Learn/JavaScript/Client-side_web_APIs\">Client-Side-Web-APIs</a></li><li><a href=\"/de/docs/Learn/JavaScript/Client-side_web_APIs/Introduction\">Einführung in Web-APIs</a></li><li><a href=\"/de/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents\">Manipulating documents</a></li><li><a href=\"/de/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data\">Abrufen von Daten vom Server</a></li><li><a href=\"/de/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs\">Third-party APIs</a></li><li><a href=\"/de/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics\">Grafiken zeichnen</a></li><li><a href=\"/de/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs\">Video- und Audio-APIs</a></li><li><a href=\"/de/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage\">Client-side storage</a></li></ol></details></li><li class=\"section\"><a href=\"/de/docs/Learn/Forms\">Webformulare — Arbeiten mit Benutzerdaten</a></li><li><details><summary>Grundlagen der Webformulare</summary><ol><li><a href=\"/de/docs/Learn/Forms\">Bausteine für Webformulare</a></li><li><a href=\"/de/docs/Learn/Forms/Your_first_form\">Ihr erstes Formular</a></li><li><a href=\"/de/docs/Learn/Forms/How_to_structure_a_web_form\">Wie Sie ein Webformular strukturieren</a></li><li><a href=\"/de/docs/Learn/Forms/Basic_native_form_controls\">Basis-Native Formularelemente</a></li><li><a href=\"/de/docs/Learn/Forms/HTML5_input_types\">Die HTML5 input Typen</a></li><li><a href=\"/de/docs/Learn/Forms/Other_form_controls\">Andere Formularelemente</a></li><li><a href=\"/de/docs/Learn/Forms/Styling_web_forms\">Styling von Webformularen</a></li><li><a href=\"/de/docs/Learn/Forms/Advanced_form_styling\">Erweiterte Formular-Stilgestaltung</a></li><li><a href=\"/de/docs/Learn/Forms/UI_pseudo-classes\">UI-Pseudo-Klassen</a></li><li><a href=\"/de/docs/Learn/Forms/Form_validation\">Client-seitige Formularvalidierung</a></li><li><a href=\"/de/docs/Learn/Forms/Sending_and_retrieving_form_data\">Senden von Formulardaten</a></li></ol></details></li><li><details><summary>Erweiterte Techniken für Webformulare</summary><ol><li><a href=\"/de/docs/Learn/Forms/How_to_build_custom_form_controls\">Anleitung zur Erstellung benutzerdefinierter Formularelemente</a></li><li><a href=\"/de/docs/Learn/Forms/Sending_forms_through_JavaScript\">Versenden von Formularen über JavaScript</a></li><li><a href=\"/de/docs/Learn/Forms/Property_compatibility_table_for_form_controls\">CSS-Eigenschaftskompatibilitätstabelle für Formularelemente</a></li><li><a href=\"/de/docs/Learn/Forms/HTML_forms_in_legacy_browsers\">HTML-Formulare in älteren Browsern</a></li></ol></details></li><li class=\"section\"><a href=\"/de/docs/Learn/Accessibility\">Barrierefreiheit — Das Web für alle nutzbar machen</a></li><li><details><summary>Barrierefreiheitsleitfäden</summary><ol><li><a href=\"/de/docs/Learn/Accessibility\">Barrierefreiheit</a></li><li><a href=\"/de/docs/Learn/Accessibility/What_is_accessibility\">Was ist Barrierefreiheit?</a></li><li><a href=\"/de/docs/Learn/Accessibility/HTML\">HTML: Eine gute Grundlage für Barrierefreiheit</a></li><li><a href=\"/de/docs/Learn/Accessibility/CSS_and_JavaScript\">CSS und JavaScript: Barrierefreiheits-Best Practices</a></li><li><a href=\"/de/docs/Learn/Accessibility/WAI-ARIA_basics\">Grundlagen von WAI-ARIA</a></li><li><a href=\"/de/docs/Learn/Accessibility/Multimedia\">Barrierefreie Multimedia</a></li><li><a href=\"/de/docs/Learn/Accessibility/Mobile\">Barrierefreiheit auf Mobilgeräten</a></li><li><a href=\"/de/docs/Learn/Accessibility/Accessibility_troubleshooting\">Bewertung: Barrierefreiheits-Troubleshooting</a></li></ol></details></li><li class=\"section\"><a href=\"/de/docs/Learn/Performance\">Leistung — Websites schnell und reaktionsschnell machen</a></li><li><details><summary>Leitfäden zur Leistung</summary><ol><li><a href=\"/de/docs/Learn/Performance\">Webleistung</a></li><li><a href=\"/de/docs/Learn/Performance/why_web_performance\">Der 'Warum' der Web-Performance</a></li><li><a href=\"/de/docs/Learn/Performance/What_is_web_performance\">Was ist Web-Performance?</a></li><li><a href=\"/de/docs/Learn/Performance/Perceived_performance\">Wahrgenommene Leistung</a></li><li><a href=\"/de/docs/Learn/Performance/Measuring_performance\">Messung der Performance</a></li><li><a href=\"/de/docs/Learn/Performance/Multimedia\">Multimedia: Bilder</a></li><li><a href=\"/de/docs/Learn/Performance/video\">Multimedia: Video</a></li><li><a href=\"/de/docs/Learn/Performance/JavaScript\">JavaScript-Leistungsoptimierung</a></li><li><a href=\"/de/docs/Learn/Performance/HTML\">HTML-Leistungsoptimierung</a></li><li><a href=\"/de/docs/Learn/Performance/CSS\">CSS-Leistungsoptimierung</a></li><li><a href=\"/de/docs/Learn/Performance/business_case_for_performance\">Der geschäftliche Nutzen von Web-Performance</a></li></ol></details></li><li class=\"section\"><a href=\"/de/docs/Learn/MathML\">MathML — Schreiben von Mathematik mit MathML</a></li><li><details><summary>MathML erste Schritte</summary><ol><li><a href=\"/de/docs/Learn/MathML/First_steps\">Erste Schritte mit MathML</a></li><li><a href=\"/de/docs/Learn/MathML/First_steps/Getting_started\">Erste Schritte mit MathML</a></li><li><a href=\"/de/docs/Learn/MathML/First_steps/Text_containers\">MathML Text-Container</a></li><li><a href=\"/de/docs/Learn/MathML/First_steps/Fractions_and_roots\">MathML-Brüche und -Wurzeln</a></li><li><a href=\"/de/docs/Learn/MathML/First_steps/Scripts\">MathML gescriptete Elemente</a></li><li><a href=\"/de/docs/Learn/MathML/First_steps/Tables\">MathML-Tabellen</a></li><li><a href=\"/de/docs/Learn/MathML/First_steps/Three_famous_mathematical_formulas\">Drei berühmte mathematische Formeln</a></li></ol></details></li><li class=\"section\"><a href=\"/de/docs/Learn/../Games\">Spiele — Entwicklung von Spielen für das Web</a></li><li><details><summary>Anleitungen und Tutorials</summary><ol><li><a href=\"/de/docs/Games/Introduction\">Einführung in die Spieleentwicklung für das Web</a></li><li><a href=\"/de/docs/Games/Techniques\">Techniken für die Spieleentwicklung</a></li><li><a href=\"/de/docs/Games/Tutorials\">Tutorials</a></li><li><a href=\"/de/docs/Games/Publishing_games\">Publishing Games</a></li></ol></details></li><li class=\"section\"><a href=\"/de/docs/Learn/Tools_and_testing\">Werkzeuge und Tests</a></li><li><details><summary>Client-seitige Webentwicklungstools</summary><ol><li><a href=\"/de/docs/Learn/Tools_and_testing/Understanding_client-side_tools\">Verständnis von Client-seitigen Webentwicklungstools</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Overview\">Übersicht über clientseitige Werkzeuge</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Command_line\">Crashkurs zur Kommandozeile</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Package_management\">Grundlagen des Paketmanagements</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Introducing_complete_toolchain\">Einführung in eine vollständige Toolchain</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Understanding_client-side_tools/Deployment\">Bereitstellung unserer App</a></li></ol></details></li><li><details><summary>Einführung in client-seitige Frameworks</summary><ol><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Introduction\">Einführung in client-seitige Frameworks</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Main_features\">Hauptmerkmale von Frameworks</a></li></ol></details></li><li><details><summary>React</summary><ol><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started\">Erste Schritte mit React</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_todo_list_beginning\">Beginn unserer React-Task-Liste</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_components\">Komponentisieren unserer React-App</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_events_state\">React-Interaktivität: Ereignisse und Status</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_interactivity_filtering_conditional_rendering\">React Interaktivität: Bearbeiten, Filtern, bedingtes Rendering</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_accessibility\">Accessibility in React</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_resources\">React-Ressourcen</a></li></ol></details></li><li><details><summary>Ember</summary><ol><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_getting_started\">Einstieg in Ember</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_structure_componentization\">Ember App-Struktur und Komponentisierung</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_interactivity_events_state\">Ember-Interaktivität: Events, Klassen und Zustand</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_conditional_footer\">Ember Interaktivität: Footer-Funktionalität, bedingtes Rendering</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_routing\">Routing in Ember</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Ember_resources\">Ember-Ressourcen und Fehlerbehebung</a></li></ol></details></li><li><details><summary>Vue</summary><ol><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_getting_started\">Einstieg in Vue</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_first_component\">Erstellen unserer ersten Vue-Komponente</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_rendering_lists\">Rendering einer Liste von Vue-Komponenten</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_methods_events_models\">Hinzufügen eines neuen Todo-Formulars: Vue-Ereignisse, Methoden und Modelle</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_styling\">Styling von Vue-Komponenten mit CSS</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_computed_properties\">Verwendung von Vue computed properties</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_conditional_rendering\">Vue bedingte Darstellung: Bearbeitung bestehender Todos</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_refs_focus_management\">Vue-Refs und Lifecycle-Methoden zur Fokusverwaltung</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Vue_resources\">Vue-Ressourcen</a></li></ol></details></li><li><details><summary>Svelte</summary><ol><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_getting_started\">Erste Schritte mit Svelte</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_Todo_list_beginning\">Starten unserer Svelte-Tasklisten-App</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_variables_props\">Dynamisches Verhalten in Svelte: Arbeiten mit Variablen und Props</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_components\">Komponentisierung unserer Svelte-App</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_reactivity_lifecycle_accessibility\">Fortgeschrittenes Svelte: Reaktivität, Lebenszyklus, Barrierefreiheit</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_stores\">Arbeiten mit Svelte Stores</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_TypeScript\">TypeScript-Unterstützung in Svelte</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_deployment_next\">Bereitstellung und nächste Schritte</a></li></ol></details></li><li><details><summary>Angular</summary><ol><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_getting_started\">Erste Schritte mit Angular</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_todo_list_beginning\">Anfang unserer Angular-To-Do-Liste-App</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_styling\">Styling unserer Angular-Anwendung</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_item_component\">Erstellen einer Item-Komponente</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_filtering\">Filtern unserer To-Do-Elemente</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Angular_building\">Building Angular applications and further resources</a></li></ol></details></li><li><details><summary>Git und GitHub</summary><ol><li><a href=\"/de/docs/Learn/Tools_and_testing/GitHub\">Git und GitHub</a></li></ol></details></li><li><details><summary>Cross-Browser-Tests</summary><ol><li><a href=\"/de/docs/Learn/Tools_and_testing/Cross_browser_testing\">Cross-Browser-Testing</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Introduction\">Einführung in das Cross-Browser-Testing</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Testing_strategies\">Strategien zur Durchführung von Tests</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS\">Umgang mit häufigen HTML- und CSS-Problemen</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript\">Umgang mit häufigen JavaScript-Problemen</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility\">Umgang mit häufigen Problemen der Barrierefreiheit</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Feature_detection\">Implementierung von Feature-Erkennung</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Automated_testing\">Einführung in automatisiertes Testen</a></li><li><a href=\"/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment\">Einrichten Ihrer eigenen Testautomatisierungsumgebung</a></li></ol></details></li><li class=\"section\"><a href=\"/de/docs/Learn/Server-side\">Server-seitige Webprogrammierung</a></li><li><details><summary>Erste Schritte</summary><ol><li><a href=\"/de/docs/Learn/Server-side/First_steps\">Server-seitige Website-Programmierung: Erste Schritte</a></li><li><a href=\"/de/docs/Learn/Server-side/First_steps/Introduction\">Einführung in die serverseitige Programmierung</a></li><li><a href=\"/de/docs/Learn/Server-side/First_steps/Client-Server_overview\">Überblick über Client-Server</a></li><li><a href=\"/de/docs/Learn/Server-side/First_steps/Web_frameworks\">Serverseitige Web-Frameworks</a></li><li><a href=\"/de/docs/Learn/Server-side/First_steps/Website_security\">Website-Sicherheit</a></li></ol></details></li><li><details open=\"\"><summary>Django Web-Framework (Python)</summary><ol><li><a href=\"/de/docs/Learn/Server-side/Django\">Django Web Framework (Python)</a></li><li><a href=\"/de/docs/Learn/Server-side/Django/Introduction\">Einführung in Django</a></li><li><a href=\"/de/docs/Learn/Server-side/Django/development_environment\">Einrichten einer Django-Entwicklungsumgebung</a></li><li><a href=\"/de/docs/Learn/Server-side/Django/Tutorial_local_library_website\">Django-Tutorial: Die Local Library Website</a></li><li><a href=\"/de/docs/Learn/Server-side/Django/skeleton_website\">Django-Tutorial Teil 2: Erstellung einer Skelett-Website</a></li><li><a href=\"/de/docs/Learn/Server-side/Django/Models\">Django-Tutorial Teil 3: Verwenden von Modellen</a></li><li><a href=\"/de/docs/Learn/Server-side/Django/Admin_site\">Django Tutorial Teil 4: Django Admin-Seite</a></li><li><a href=\"/de/docs/Learn/Server-side/Django/Home_page\">Django-Tutorial Teil 5: Erstellen unserer Startseite</a></li><li><a href=\"/de/docs/Learn/Server-side/Django/Generic_views\">Django Tutorial Teil 6: Generische Listen- und Detailansichten</a></li><li><a href=\"/de/docs/Learn/Server-side/Django/Sessions\">Django-Tutorial Teil 7: Sessions-Framework</a></li><li><a href=\"/de/docs/Learn/Server-side/Django/Authentication\">Django-Tutorial Teil 8: Benutzer-Authentifizierung und Berechtigungen</a></li><li><a href=\"/de/docs/Learn/Server-side/Django/Forms\">Django Tutorial Teil 9: Arbeiten mit Formularen</a></li><li><em><a href=\"/de/docs/Learn/Server-side/Django/Testing\" aria-current=\"page\">Django Tutorial Teil 10: Testen einer Django-Webanwendung</a></em></li><li><a href=\"/de/docs/Learn/Server-side/Django/Deployment\">Django-Tutorial Teil 11: Django in Produktion bereitstellen</a></li><li><a href=\"/de/docs/Learn/Server-side/Django/web_application_security\">Django-Webanwendungssicherheit</a></li><li><a href=\"/de/docs/Learn/Server-side/Django/django_assessment_blog\">Bewertung: DIY Django Mini-Blog</a></li></ol></details></li><li><details><summary>Express Web-Framework (Node.js/JavaScript)</summary><ol><li><a href=\"/de/docs/Learn/Server-side/Express_Nodejs\">Express-Webframework (Node.js/JavaScript)</a></li><li><a href=\"/de/docs/Learn/Server-side/Express_Nodejs/Introduction\">Einführung in Express/Node</a></li><li><a href=\"/de/docs/Learn/Server-side/Express_Nodejs/development_environment\">Einrichtung einer Node-Entwicklungsumgebung</a></li><li><a href=\"/de/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website\">Express-Tutorial: Die Website der lokalen Bibliothek</a></li><li><a href=\"/de/docs/Learn/Server-side/Express_Nodejs/skeleton_website\">Express Tutorial Teil 2: Erstellung einer Grundstruktur für eine Website</a></li><li><a href=\"/de/docs/Learn/Server-side/Express_Nodejs/mongoose\">Express Tutorial Teil 3: Verwendung einer Datenbank (mit Mongoose)</a></li><li><a href=\"/de/docs/Learn/Server-side/Express_Nodejs/routes\">Express Tutorial Teil 4: Routen und Controller</a></li><li><a href=\"/de/docs/Learn/Server-side/Express_Nodejs/Displaying_data\">Express Tutorial Teil 5: Bibliotheksdaten anzeigen</a></li><li><a href=\"/de/docs/Learn/Server-side/Express_Nodejs/forms\">Express Tutorial Teil 6: Arbeiten mit Formularen</a></li><li><a href=\"/de/docs/Learn/Server-side/Express_Nodejs/deployment\">Express-Tutorial Teil 7: Bereitstellung für die Produktion</a></li></ol></details></li><li class=\"section\"><a href=\"/de/docs/Learn/Common_questions\">Weitere Ressourcen</a></li><li><details><summary>Häufige Fragen</summary><ol><li><a href=\"/de/docs/Learn/Common_questions\">Häufige Fragen</a></li><li><a href=\"/de/docs/Learn/HTML/Howto\">HTML verwenden, um häufige Probleme zu lösen</a></li><li><a href=\"/de/docs/Learn/CSS/Howto\">CSS verwenden, um häufige Probleme zu lösen</a></li><li><a href=\"/de/docs/Learn/JavaScript/Howto\">Lösen Sie häufige Probleme in Ihrem JavaScript-Code</a></li><li><a href=\"/de/docs/Learn/Common_questions/Web_mechanics\">Web-Mechanik</a></li><li><a href=\"/de/docs/Learn/Common_questions/Tools_and_setup\">Tools und Einrichtung</a></li><li><a href=\"/de/docs/Learn/Common_questions/Design_and_accessibility\">Design und Barrierefreiheit</a></li></ol></details></li></ol>","sidebarMacro":"LearnSidebar","body":[{"type":"prose","value":{"id":null,"title":null,"isH3":false,"content":"<ul class=\"prev-next\">\n <li><a class=\"button secondary\" href=\"/de/docs/Learn/Server-side/Django/Forms\"><span class=\"button-wrap\"> Zurück </span></a></li>\n <li><a class=\"button secondary\" href=\"/de/docs/Learn/Server-side/Django\"><span class=\"button-wrap\"> Übersicht: Django Web Framework (Python)</span></a></li>\n <li><a class=\"button secondary\" href=\"/de/docs/Learn/Server-side/Django/Deployment\"><span class=\"button-wrap\"> Weiter </span></a></li>\n</ul>\n<p>Wenn Websites wachsen, werden sie schwieriger manuell zu testen. Nicht nur gibt es mehr zu testen, sondern auch, da die Interaktionen zwischen den Komponenten komplexer werden, kann eine kleine Änderung in einem Bereich andere Bereiche beeinflussen. Daher sind mehr Änderungen erforderlich, um sicherzustellen, dass alles weiterhin funktioniert und keine Fehler eingeführt werden, während mehr Änderungen vorgenommen werden. Eine Möglichkeit, diese Probleme zu mindern, ist das Schreiben automatisierter Tests, die bei jeder Änderung einfach und zuverlässig ausgeführt werden können. Dieses Tutorial zeigt, wie Sie mit Djangos Testframework das <em>Unittesting</em> Ihrer Website automatisieren können.</p>\n<figure class=\"table-container\"><table>\n <tbody>\n <tr>\n <th scope=\"row\">Voraussetzungen:</th>\n <td>Schließen Sie alle vorherigen Tutorial-Themen ab, einschließlich <a href=\"/de/docs/Learn/Server-side/Django/Forms\">Django Tutorial Teil 9: Arbeiten mit Formularen</a>.</td>\n </tr>\n <tr>\n <th scope=\"row\">Ziel:</th>\n <td>Verstehen, wie man Unittests für auf Django basierende Websites schreibt.</td>\n </tr>\n </tbody>\n</table></figure>"}},{"type":"prose","value":{"id":"überblick","title":"Überblick","isH3":false,"content":"<p>Die <a href=\"/de/docs/Learn/Server-side/Django/Tutorial_local_library_website\">Local Library</a> hat derzeit Seiten, um Listen aller Bücher und Autoren anzuzeigen, Detailansichten für <code>Book</code>- und <code>Author</code>-Objekte, eine Seite, um <code>BookInstance</code>-Objekte zu erneuern, und Seiten zum Erstellen, Aktualisieren und Löschen von <code>Author</code>-Objekten (und auch <code>Book</code>-Datensätzen, wenn Sie die <em>Herausforderung</em> im <a href=\"/de/docs/Learn/Server-side/Django/Forms\">Formular-Tutorial</a> abgeschlossen haben). Selbst bei dieser relativ kleinen Seite kann es mehrere Minuten dauern, manuell zu jeder Seite zu navigieren und <em>oberflächlich</em> zu prüfen, ob alles wie erwartet funktioniert. Wenn wir Änderungen vornehmen und die Seite vergrößern, wird die Zeit, die benötigt wird, um manuell zu überprüfen, dass alles \"ordnungsgemäß\" funktioniert, nur noch zunehmen. Wenn wir so weitermachen würden, würden wir schließlich die meiste Zeit mit Testen verbringen und sehr wenig mit der Verbesserung unseres Codes.</p>\n<p>Automatisierte Tests können wirklich bei diesem Problem helfen! Die offensichtlichen Vorteile sind, dass sie viel schneller als manuelle Tests ausgeführt werden können, auf einem viel detaillierteren Niveau testen können und jedes Mal genau die gleiche Funktionalität testen (menschliche Tester sind bei weitem nicht so zuverlässig!). Da sie schnell sind, können automatisierte Tests regelmäßiger ausgeführt werden, und wenn ein Test fehlschlägt, zeigen sie genau, wo der Code nicht wie erwartet funktioniert.</p>\n<p>Darüber hinaus können automatisierte Tests als der erste reale \"Benutzer\" Ihres Codes fungieren und Sie dazu zwingen, rigoros zu definieren und zu dokumentieren, wie sich Ihre Website verhalten soll. Oft sind sie die Grundlage für Ihre Codebeispiele und Dokumentation. Aus diesen Gründen beginnen einige Softwareentwicklungsprozesse mit der Definition und Implementierung von Tests, nach denen der Code geschrieben wird, um das erforderliche Verhalten zu erfüllen (z. B. <a href=\"https://en.wikipedia.org/wiki/Test-driven_development\" class=\"external\" target=\"_blank\">Testgetriebene</a> und <a href=\"https://en.wikipedia.org/wiki/Behavior-driven_development\" class=\"external\" target=\"_blank\">verhaltensgetriebene</a> Entwicklung).</p>\n<p>Dieses Tutorial zeigt, wie man automatisierte Tests für Django schreibt, indem eine Reihe von Tests auf der <em>LocalLibrary</em>-Website hinzugefügt wird.</p>"}},{"type":"prose","value":{"id":"arten_des_testens","title":"Arten des Testens","isH3":true,"content":"<p>Es gibt zahlreiche Typen, Ebenen und Klassifikationen von Tests und Testansätzen. Die wichtigsten automatisierten Tests sind:</p>\n<dl>\n <dt id=\"unit-tests\"><a href=\"#unit-tests\">Unit-Tests</a></dt>\n <dd>\n <p>Überprüfen das funktionale Verhalten einzelner Komponenten, oft auf Klassen- und Funktionsebene.</p>\n </dd>\n <dt id=\"regressionstests\"><a href=\"#regressionstests\">Regressionstests</a></dt>\n <dd>\n <p>Tests, die historische Fehler reproduzieren. Jeder Test wird zunächst ausgeführt, um zu überprüfen, ob der Fehler behoben wurde, und dann erneut ausgeführt, um sicherzustellen, dass er nach späteren Codeänderungen nicht erneut eingeführt wurde.</p>\n </dd>\n <dt id=\"integrationstests\"><a href=\"#integrationstests\">Integrationstests</a></dt>\n <dd>\n <p>Überprüfen, wie Gruppierungen von Komponenten funktionieren, wenn sie zusammen verwendet werden. Integrationstests berücksichtigen die erforderlichen Interaktionen zwischen den Komponenten, aber nicht unbedingt die internen Vorgänge jeder Komponente. Sie können einfache Gruppierungen von Komponenten bis hin zur gesamten Website abdecken.</p>\n </dd>\n</dl>\n<div class=\"notecard note\">\n <p><strong>Hinweis:</strong> Andere gängige Arten von Tests sind Black-Box-, White-Box-, manuelle, automatisierte, Canary-, Smoke-, Konformitäts-, Akzeptanz-, Funktions-, System-, Leistungs-, Last- und Belastungstests. Schauen Sie sie nach, um weitere Informationen zu erhalten.</p>\n</div>"}},{"type":"prose","value":{"id":"was_bietet_django_zum_testen","title":"Was bietet Django zum Testen?","isH3":true,"content":"<p>Das Testen einer Website ist eine komplexe Aufgabe, da sie aus mehreren Logikebenen besteht – von der HTTP-Ebene der Anforderungsverarbeitung über Modellabfragen bis zur Formularvalidierung und -verarbeitung und der Rendererstellung von Vorlagen.</p>\n<p>Django bietet ein Testframework mit einer kleinen Hierarchie von Klassen, die auf der Python-Standardbibliothek <a href=\"https://docs.python.org/3/library/unittest.html#module-unittest\" class=\"external\" target=\"_blank\"><code>unittest</code></a> aufbauen. Trotz des Namens ist dieses Testframework sowohl für Unit- als auch für Integrationstests geeignet. Das Django-Framework fügt API-Methoden und Tools hinzu, die das Testen von Web- und Django-spezifischem Verhalten unterstützen. Diese ermöglichen Ihnen das Simulieren von Anforderungen, das Einfügen von Testdaten und das Überprüfen der Ausgabe Ihrer Anwendung. Django bietet auch eine API (<a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/tools/#liveservertestcase\" class=\"external\" target=\"_blank\">LiveServerTestCase</a>) und Werkzeuge für <a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/advanced/#other-testing-frameworks\" class=\"external\" target=\"_blank\">die Verwendung verschiedener Testframeworks</a>, beispielsweise können Sie das beliebte <a href=\"/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment\">Selenium</a> Framework integrieren, um einem Benutzer zu simulieren, der mit einem Live-Browser interagiert.</p>\n<p>Um einen Test zu schreiben, erben Sie von einer der Django- (oder <em>unittest</em>-) Test-Basisklassen (<a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/tools/#simpletestcase\" class=\"external\" target=\"_blank\">SimpleTestCase</a>, <a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/tools/#transactiontestcase\" class=\"external\" target=\"_blank\">TransactionTestCase</a>, <a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/tools/#testcase\" class=\"external\" target=\"_blank\">TestCase</a>, <a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/tools/#liveservertestcase\" class=\"external\" target=\"_blank\">LiveServerTestCase</a>) und schreiben Sie dann separate Methoden, um zu überprüfen, dass die spezifische Funktionalität wie erwartet funktioniert (Tests verwenden \"assert\"-Methoden, um zu testen, dass Ausdrücke in <code>True</code>- oder <code>False</code>-Werten resultieren, oder dass zwei Werte gleich sind usw.). Wenn Sie einen Testrun starten, führt das Framework die ausgewählten Testmethoden in Ihren abgeleiteten Klassen aus. Die Testmethoden werden unabhängig ausgeführt, mit gemeinsamem Setup und/oder Abbauverhalten, wie unten gezeigt.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>class YourTestClass(TestCase):\n def setUp(self):\n # Setup run before every test method.\n pass\n\n def tearDown(self):\n # Clean up run after every test method.\n pass\n\n def test_something_that_will_pass(self):\n self.assertFalse(False)\n\n def test_something_that_will_fail(self):\n self.assertTrue(False)\n</code></pre></div>\n<p>Die beste Basisklasse für die meisten Tests ist <a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/tools/#testcase\" class=\"external\" target=\"_blank\">django.test.TestCase</a>. Diese Testklasse erstellt eine saubere Datenbank, bevor ihre Tests ausgeführt werden, und führt jede Testfunktion in ihrer eigenen Transaktion aus. Die Klasse besitzt auch einen Test-<a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/tools/#django.test.Client\" class=\"external\" target=\"_blank\">Client</a>, den Sie verwenden können, um die Interaktion eines Benutzers mit dem Code auf der View-Ebene zu simulieren. In den folgenden Abschnitten konzentrieren wir uns auf Unit-Tests, die mit dieser <a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/tools/#testcase\" class=\"external\" target=\"_blank\">TestCase</a>-Basisklasse erstellt werden.</p>\n<div class=\"notecard note\">\n <p><strong>Hinweis:</strong> Die <a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/tools/#testcase\" class=\"external\" target=\"_blank\">django.test.TestCase</a>-Klasse ist sehr praktisch, kann jedoch dazu führen, dass einige Tests langsamer sind als nötig (nicht jeder Test muss seine eigene Datenbank einrichten oder die View-Interaktion simulieren). Sobald Sie vertraut sind mit dem, was Sie mit dieser Klasse tun können, möchten Sie möglicherweise einige Ihrer Tests durch die verfügbaren einfacheren Testklassen ersetzen.</p>\n</div>"}},{"type":"prose","value":{"id":"was_sollten_sie_testen","title":"Was sollten Sie testen?","isH3":true,"content":"<p>Sie sollten alle Aspekte Ihres eigenen Codes testen, jedoch keine Bibliotheken oder Funktionalitäten, die von Python oder Django bereitgestellt werden.</p>\n<p>Betrachten Sie zum Beispiel das unten definierte <code>Author</code>-Modell. Sie müssen nicht explizit testen, ob <code>first_name</code> und <code>last_name</code> ordnungsgemäß als <code>CharField</code> in der Datenbank gespeichert wurden, da dies etwas ist, das von Django definiert ist (obwohl Sie dies in der Praxis natürlich während der Entwicklung unvermeidlich testen werden). Ebenso müssen Sie nicht testen, ob <code>date_of_birth</code> als Datumsfeld validiert wurde, da auch dies in Django implementiert ist.</p>\n<p>Sie sollten jedoch den Text überprüfen, der für die Etiketten verwendet wird (<em>First name, Last name, Date of birth, Died</em>), und die Größe des für den Text zugewiesenen Feldes (<em>100 Zeichen</em>), da diese Teil Ihres Designs sind und in Zukunft gebrochen/geändert werden könnten.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>class Author(models.Model):\n first_name = models.CharField(max_length=100)\n last_name = models.CharField(max_length=100)\n date_of_birth = models.DateField(null=True, blank=True)\n date_of_death = models.DateField('Died', null=True, blank=True)\n\n def get_absolute_url(self):\n return reverse('author-detail', args=[str(self.id)])\n\n def __str__(self):\n return '%s, %s' % (self.last_name, self.first_name)\n</code></pre></div>\n<p>Ebenso sollten Sie überprüfen, ob die benutzerdefinierten Methoden <code>get_absolute_url()</code> und <code>__str__()</code> wie erforderlich funktionieren, da sie Ihr eigener Code bzw. Ihre Geschäftslogik sind. Im Fall von <code>get_absolute_url()</code> können Sie darauf vertrauen, dass die Django-Methode <code>reverse()</code> ordnungsgemäß implementiert wurde. Was Sie testen, ist also, dass die zugehörige View tatsächlich definiert wurde.</p>\n<div class=\"notecard note\">\n <p>\n <strong>Hinweis:</strong> Aufmerksame Leser werden möglicherweise feststellen, dass wir auch das Geburts- und Sterbedatum auf sinnvolle Werte beschränken und sicherstellen möchten, dass der Tod nach der Geburt erfolgt.\n In Django würde diese Einschränkung in Ihren Formularklassen hinzugefügt (obwohl Sie Validatoren für Modelfelder und Modellvalidatoren definieren können, werden diese nur auf der Formulardaten-Ebene verwendet, wenn sie durch die <code>clean()</code>-Methode des Modells aufgerufen werden. Dies erfordert ein <code>ModelForm</code>, oder die <code>clean()</code>-Methode des Modells muss explizit aufgerufen werden.)\n </p>\n</div>\n<p>Mit diesem Gedanken im Hinterkopf beginnen wir also damit, wie man Tests definiert und ausführt.</p>"}},{"type":"prose","value":{"id":"überblick_über_die_teststruktur","title":"Überblick über die Teststruktur","isH3":false,"content":"<p>Bevor wir näher darauf eingehen, \"was zu testen ist\", werfen wir zunächst einen kurzen Blick darauf, <em>wo</em> und <em>wie</em> Tests definiert werden.</p>\n<p>Django verwendet die <a href=\"https://docs.python.org/3/library/unittest.html#unittest-test-discovery\" class=\"external\" target=\"_blank\">Test Discovery</a> der <code>unittest</code>-Moduls, die Tests im aktuellen Arbeitsverzeichnis in jeder Datei findet, die mit dem Muster <strong>test*.py</strong> benannt ist. Vorausgesetzt, Sie benennen die Dateien entsprechend, können Sie jede beliebige Struktur verwenden. Wir empfehlen, ein Modul für Ihren Testcode zu erstellen und separate Dateien für Modelle, Ansichten, Formulare und alle anderen Arten von Code zu verwenden, die Sie testen müssen. Zum Beispiel:</p>\n<pre class=\"brush: plain notranslate\">catalog/\n /tests/\n __init__.py\n test_models.py\n test_forms.py\n test_views.py\n</pre>\n<p>Erstellen Sie eine Dateistruktur wie oben gezeigt in Ihrem <em>LocalLibrary</em>-Projekt. Die <strong>__init__.py</strong> sollte eine leere Datei sein (dies sagt Python, dass das Verzeichnis ein Paket ist). Sie können die drei Testdateien erstellen, indem Sie die Skelett-Testdatei <strong>/catalog/tests.py</strong> kopieren und umbenennen.</p>\n<div class=\"notecard note\">\n <p><strong>Hinweis:</strong> Die Skelett-Testdatei <strong>/catalog/tests.py</strong> wurde automatisch erstellt, als wir <a href=\"/de/docs/Learn/Server-side/Django/skeleton_website\">die Django-Skelett-Website erstellten</a>. Es ist vollkommen \"legal\", alle Ihre Tests darin zu platzieren, aber wenn Sie richtig testen, werden Sie schnell eine sehr große und unübersichtliche Testdatei haben.</p>\n <p>Löschen Sie die Skelettdatei, da wir sie nicht benötigen.</p>\n</div>\n<p>Öffnen Sie <strong>/catalog/tests/test_models.py</strong>. Die Datei sollte <code>django.test.TestCase</code> importieren, wie gezeigt:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>from django.test import TestCase\n\n# Create your tests here.\n</code></pre></div>\n<p>Oft werden Sie eine Testklasse für jedes Modell/View/Formular hinzufügen, das Sie testen möchten, mit individuellen Methoden, die bestimmte Funktionalitäten testen. In anderen Fällen möchten Sie möglicherweise eine separate Klasse haben, um einen spezifischen Anwendungsfall zu testen, mit individuellen Testfunktionen, die Aspekte dieses Anwendungsfalls testen (zum Beispiel eine Klasse, um zu testen, ob ein Modelfeld ordnungsgemäß validiert wurde, mit Funktionen, um jeden möglichen Fehlschlag zu testen). Auch hier liegt die Struktur sehr bei Ihnen, aber es ist am besten, wenn Sie konsistent sind.</p>\n<p>Fügen Sie die untenstehende Testklasse am Ende der Datei hinzu. Die Klasse zeigt, wie Sie eine Testfallklasse durch Ableiten von <code>TestCase</code> konstruieren.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>class YourTestClass(TestCase):\n @classmethod\n def setUpTestData(cls):\n print(\"setUpTestData: Run once to set up non-modified data for all class methods.\")\n pass\n\n def setUp(self):\n print(\"setUp: Run once for every test method to set up clean data.\")\n pass\n\n def test_false_is_false(self):\n print(\"Method: test_false_is_false.\")\n self.assertFalse(False)\n\n def test_false_is_true(self):\n print(\"Method: test_false_is_true.\")\n self.assertTrue(False)\n\n def test_one_plus_one_equals_two(self):\n print(\"Method: test_one_plus_one_equals_two.\")\n self.assertEqual(1 + 1, 2)\n</code></pre></div>\n<p>Die neue Klasse definiert zwei Methoden, die Sie für die Konfiguration vor dem Test verwenden können (z. B. um Modelle oder andere Objekte zu erstellen, die Sie für den Test benötigen):</p>\n<ul>\n <li><code>setUpTestData()</code> wird einmal zu Beginn des Testruns für das Klassen-Setup aufgerufen. Sie verwenden dies, um Objekte zu erstellen, die in keiner der Testmethoden geändert oder verändert werden.</li>\n <li><code>setUp()</code> wird vor jeder Testfunktion aufgerufen, um Objekte einzurichten, die möglicherweise durch den Test geändert werden (jede Testfunktion erhält eine \"frische\" Version dieser Objekte).</li>\n</ul>\n<div class=\"notecard note\">\n <p><strong>Hinweis:</strong> Die Testklassen haben auch eine <code>tearDown()</code>-Methode, die wir nicht verwendet haben. Diese Methode ist für Datenbanktests nicht besonders nützlich, da die <code>TestCase</code>-Basisklasse den Abbau der Datenbank für Sie übernimmt.</p>\n</div>\n<p>Unter diesen haben wir eine Reihe von Testmethoden, die <code>Assert</code>-Funktionen verwenden, um zu testen, ob Bedingungen wahr, falsch oder gleich sind (<code>AssertTrue</code>, <code>AssertFalse</code>, <code>AssertEqual</code>). Wenn die Bedingung nicht wie erwartet bewertet wird, wird der Test fehlschlagen und den Fehler an Ihrer Konsole melden.</p>\n<p>Die <code>AssertTrue</code>, <code>AssertFalse</code>, <code>AssertEqual</code> sind Standardbehauptungen von <strong>unittest</strong>. Es gibt andere Standardbehauptungen im Framework sowie <a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/tools/#assertions\" class=\"external\" target=\"_blank\">Django-spezifische Behauptungen</a>, um zu testen, ob eine View umleitet (<code>assertRedirects</code>), um zu testen, ob eine bestimmte Vorlage verwendet wurde (<code>assertTemplateUsed</code>) usw.</p>\n<div class=\"notecard note\">\n <p><strong>Hinweis:</strong> Normalerweise sollten Sie <strong>keine</strong> <strong>print()</strong>-Funktionen in Ihre Tests einfügen, wie oben gezeigt. Wir tun dies hier nur, damit Sie die Reihenfolge sehen können, in der die Setup-Funktionen in der Konsole aufgerufen werden (im folgenden Abschnitt).</p>\n</div>"}},{"type":"prose","value":{"id":"wie_führt_man_die_tests_aus","title":"Wie führt man die Tests aus","isH3":false,"content":"<p>Der einfachste Weg, alle Tests auszuführen, ist die Verwendung des Befehls:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">bash</span></div><pre class=\"brush: bash notranslate\"><code>python3 manage.py test\n</code></pre></div>\n<p>Dieser wird alle Dateien mit dem Muster <strong>test*.py</strong> im aktuellen Verzeichnis entdecken und alle Tests ausführen, die unter Verwendung geeigneter Basisklassen definiert wurden (hier haben wir eine Reihe von Testdateien, aber nur <strong>/catalog/tests/test_models.py</strong> enthält derzeit Tests). Standardmäßig berichten die Tests nur einzeln über Testfehler und geben dann eine Zusammenfassung der Tests aus.</p>\n<div class=\"notecard note\">\n <p><strong>Hinweis:</strong> Wenn Sie Fehler wie: <code>ValueError: Missing staticfiles manifest entry...</code> erhalten, kann dies daran liegen, dass beim Testen <em>collectstatic</em> standardmäßig nicht ausgeführt wird, und Ihre App eine Speicherklasse verwendet, die dies erfordert (siehe <a href=\"https://docs.djangoproject.com/en/5.0/ref/contrib/staticfiles/#django.contrib.staticfiles.storage.ManifestStaticFilesStorage.manifest_strict\" class=\"external\" target=\"_blank\">manifest_strict</a> für weitere Informationen). Es gibt mehrere Möglichkeiten, dieses Problem zu lösen - die einfachste ist, <em>collectstatic</em> vor dem Ausführen der Tests auszuführen:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">bash</span></div><pre class=\"brush: bash notranslate\"><code>python3 manage.py collectstatic\n</code></pre></div>\n</div>\n<p>Führen Sie die Tests im Stammverzeichnis der <em>LocalLibrary</em> aus. Sie sollten eine Ausgabe ähnlich der unten stehenden sehen.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">bash</span></div><pre class=\"brush: bash notranslate\"><code>&gt; python3 manage.py test\n\nCreating test database for alias 'default'...\nsetUpTestData: Run once to set up non-modified data for all class methods.\nsetUp: Run once for every test method to set up clean data.\nMethod: test_false_is_false.\nsetUp: Run once for every test method to set up clean data.\nMethod: test_false_is_true.\nsetUp: Run once for every test method to set up clean data.\nMethod: test_one_plus_one_equals_two.\n.\n======================================================================\nFAIL: test_false_is_true (catalog.tests.tests_models.YourTestClass)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"D:\\GitHub\\django_tmp\\library_w_t_2\\locallibrary\\catalog\\tests\\tests_models.py\", line 22, in test_false_is_true\n self.assertTrue(False)\nAssertionError: False is not true\n\n----------------------------------------------------------------------\nRan 3 tests in 0.075s\n\nFAILED (failures=1)\nDestroying test database for alias 'default'...\n</code></pre></div>\n<p>Hier sehen wir, dass wir einen Testfehler hatten, und wir können genau sehen, welche Funktion fehlgeschlagen ist und warum (dieser Fehler wird erwartet, weil <code>False</code> nicht <code>True</code> ist!).</p>\n<div class=\"notecard note\">\n <p><strong>Hinweis:</strong> Das Wichtigste, was Sie aus der oben genannten Testausgabe lernen sollten, ist, dass es viel wertvoller ist, wenn Sie beschreibende/informative Namen für Ihre Objekte und Methoden verwenden.</p>\n</div>\n<p>Die Ausgabe der <code>print()</code>-Funktionen zeigt, wie die <code>setUpTestData()</code>-Methode einmal für die Klasse aufgerufen wird und <code>setUp()</code> vor jeder Methode aufgerufen wird. Denken Sie daran, dass Sie normalerweise diese Art von <code>print()</code> nicht zu Ihren Tests hinzufügen würden.</p>\n<p>In den nächsten Abschnitten wird gezeigt, wie Sie spezifische Tests ausführen können und wie Sie steuern können, wie viele Informationen die Tests anzeigen.</p>"}},{"type":"prose","value":{"id":"mehr_testinformationen_anzeigen","title":"Mehr Testinformationen anzeigen","isH3":true,"content":"<p>Wenn Sie mehr Informationen über den Testrun erhalten möchten, können Sie die <em>Ausgabeintensität</em> ändern. Zum Beispiel, um die Testerfolge sowie Fehler aufzulisten (und eine ganze Menge Informationen, wie die Testdatenbank eingerichtet wird), können Sie die Ausgabeintensität auf \"2\" setzen, wie gezeigt:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">bash</span></div><pre class=\"brush: bash notranslate\"><code>python3 manage.py test --verbosity 2\n</code></pre></div>\n<p>Die erlaubten Intensitätsstufen sind 0, 1, 2 und 3, wobei der Standard \"1\" ist.</p>"}},{"type":"prose","value":{"id":"dinge_beschleunigen","title":"Dinge beschleunigen","isH3":true,"content":"<p>Wenn Ihre Tests unabhängig sind, können Sie diese auf einem Mehrprozessormaschine erheblich beschleunigen, indem Sie sie parallel ausführen. Die Verwendung von <code>--parallel auto</code> im Folgenden führt einen Testprozess pro verfügbarem Kern aus. Das <code>auto</code> ist optional, und Sie können auch eine bestimmte Anzahl von Kernen angeben, die verwendet werden sollen.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">bash</span></div><pre class=\"brush: bash notranslate\"><code>python3 manage.py test --parallel auto\n</code></pre></div>\n<p>Weitere Informationen, einschließlich was zu tun ist, wenn Ihre Tests nicht unabhängig sind, finden Sie unter <a href=\"https://docs.djangoproject.com/en/5.0/ref/django-admin/#envvar-DJANGO_TEST_PROCESSES\" class=\"external\" target=\"_blank\">DJANGO_TEST_PROCESSES</a>.</p>"}},{"type":"prose","value":{"id":"bestimmte_tests_ausführen","title":"Bestimmte Tests ausführen","isH3":true,"content":"<p>Wenn Sie eine Teilmenge Ihrer Tests ausführen möchten, können Sie dies tun, indem Sie den vollständigen Punktpfad zum Paket bzw. Modul, zur <code>TestCase</code>-Unterklasse oder zur Methode angeben:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">bash</span></div><pre class=\"brush: bash notranslate\"><code># Run the specified module\npython3 manage.py test catalog.tests\n\n# Run the specified module\npython3 manage.py test catalog.tests.test_models\n\n# Run the specified class\npython3 manage.py test catalog.tests.test_models.YourTestClass\n\n# Run the specified method\npython3 manage.py test catalog.tests.test_models.YourTestClass.test_one_plus_one_equals_two\n</code></pre></div>"}},{"type":"prose","value":{"id":"andere_test_runner-optionen","title":"Andere Test Runner-Optionen","isH3":true,"content":"<p>Der Test Runner bietet viele andere Optionen, einschließlich der Möglichkeit, Tests zu mischen (<code>--shuffle</code>), sie im Debug-Modus auszuführen (<code>--debug-mode</code>) und den Python-Logger zu verwenden, um die Ergebnisse aufzuzeichnen. Weitere Informationen finden Sie in der Django-<a href=\"https://docs.djangoproject.com/en/5.0/ref/django-admin/#test\" class=\"external\" target=\"_blank\">Test Runner</a>-Dokumentation.</p>"}},{"type":"prose","value":{"id":"locallibrary_tests","title":"LocalLibrary Tests","isH3":false,"content":"<p>Jetzt wissen wir, wie man unsere Tests ausführt und was für Dinge wir testen müssen, schauen wir uns einige praktische Beispiele an.</p>\n<div class=\"notecard note\">\n <p><strong>Hinweis:</strong> Wir werden nicht jeden möglichen Test schreiben, aber dies sollte Ihnen eine Vorstellung davon geben, wie Tests funktionieren und was mehr Sie tun können.</p>\n</div>"}},{"type":"prose","value":{"id":"modelle","title":"Modelle","isH3":true,"content":"<p>Wie oben diskutiert, sollten wir alles testen, was Teil unseres Designs ist oder durch Code definiert ist, den wir geschrieben haben, aber nicht die Funktionalität des zugrunde liegenden Frameworks und anderer Drittanbieterbibliotheken.</p>\n<p>Betrachten Sie zum Beispiel das <code>Author</code>-Modell unten. Hier sollten wir die Etiketten für alle Felder testen, da selbst wenn wir die meisten von ihnen nicht explizit spezifiziert haben, wir ein Design haben, das besagt, welche Werte diese sein sollten. Wenn wir die Werte nicht testen, dann wissen wir nicht, dass die Etiketten der Felder die beabsichtigten Werte haben. Ebenso, obwohl wir darauf vertrauen, dass Django ein Feld mit der angegebenen Länge erstellt, lohnt es sich, einen Test für diese Länge festzulegen, um sicherzustellen, dass sie wie geplant implementiert wurde.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>class Author(models.Model):\n first_name = models.CharField(max_length=100)\n last_name = models.CharField(max_length=100)\n date_of_birth = models.DateField(null=True, blank=True)\n date_of_death = models.DateField('Died', null=True, blank=True)\n\n def get_absolute_url(self):\n return reverse('author-detail', args=[str(self.id)])\n\n def __str__(self):\n return f'{self.last_name}, {self.first_name}'\n</code></pre></div>\n<p>Öffnen Sie unsere <strong>/catalog/tests/test_models.py</strong>, und ersetzen Sie eventuellen vorhandenen Code durch den folgenden Testcode für das <code>Author</code>-Modell.</p>\n<p>Hier sehen Sie, dass wir zuerst <code>TestCase</code> importieren und unsere Testklasse (<code>AuthorModelTest</code>) davon ableiten, mit einem beschreibenden Namen, damit wir fehlerhafte Tests in der Ausgabe leicht identifizieren können. Danach rufen wir <code>setUpTestData()</code> auf, um ein Autorenobjekt zu erstellen, das wir verwenden, aber in keiner der Tests verändern werden.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>from django.test import TestCase\n\nfrom catalog.models import Author\n\nclass AuthorModelTest(TestCase):\n @classmethod\n def setUpTestData(cls):\n # Set up non-modified objects used by all test methods\n Author.objects.create(first_name='Big', last_name='Bob')\n\n def test_first_name_label(self):\n author = Author.objects.get(id=1)\n field_label = author._meta.get_field('first_name').verbose_name\n self.assertEqual(field_label, 'first name')\n\n def test_date_of_death_label(self):\n author = Author.objects.get(id=1)\n field_label = author._meta.get_field('date_of_death').verbose_name\n self.assertEqual(field_label, 'died')\n\n def test_first_name_max_length(self):\n author = Author.objects.get(id=1)\n max_length = author._meta.get_field('first_name').max_length\n self.assertEqual(max_length, 100)\n\n def test_object_name_is_last_name_comma_first_name(self):\n author = Author.objects.get(id=1)\n expected_object_name = f'{author.last_name}, {author.first_name}'\n self.assertEqual(str(author), expected_object_name)\n\n def test_get_absolute_url(self):\n author = Author.objects.get(id=1)\n # This will also fail if the urlconf is not defined.\n self.assertEqual(author.get_absolute_url(), '/catalog/author/1')\n</code></pre></div>\n<p>Die Feldtests überprüfen, ob die Werte der Etiketten der Felder (<code>verbose_name</code>) und dass die Größe der Zeichenfelder wie erwartet ist. Diese Methoden haben alle beschreibende Namen und folgen dem gleichen Muster:</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code># Get an author object to test\nauthor = Author.objects.get(id=1)\n\n# Get the metadata for the required field and use it to query the required field data\nfield_label = author._meta.get_field('first_name').verbose_name\n\n# Compare the value to the expected result\nself.assertEqual(field_label, 'first name')\n</code></pre></div>\n<p>Interessante Punkte sind:</p>\n<ul>\n <li>Wir können das <code>verbose_name</code> nicht direkt mit <code>author.first_name.verbose_name</code> abrufen, weil <code>author.first_name</code> ein <em>String</em> ist (kein Zugriff auf das <code>first_name</code>-Objekt, mit dem wir auf seine Eigenschaften zugreifen können). Stattdessen müssen wir das Attribut <code>_meta</code> des Autors verwenden, um eine Instanz des Feldes zu erhalten und diese zu verwenden, um nach den zusätzlichen Informationen zu suchen.</li>\n <li>Wir haben uns entschieden <code>assertEqual(field_label,'first name')</code> anstelle von <code>assertTrue(field_label == 'first name')</code> zu verwenden. Der Grund hierfür ist, dass, wenn der Test fehlschlägt, die Ausgabe bei ersterem Ihnen sagt, was das Label eigentlich war, was die Fehlersuche ein wenig erleichtert.</li>\n</ul>\n<div class=\"notecard note\">\n <p><strong>Hinweis:</strong> Tests für die <code>last_name</code>- und <code>date_of_birth</code>-Etiketten sowie der Test für die Länge des <code>last_name</code>-Feldes wurden ausgelassen. Fügen Sie jetzt Ihre eigenen Versionen hinzu, indem Sie den oben gezeigten Namenskonventionen und Ansätzen folgen.</p>\n</div>\n<p>Wir müssen auch unsere benutzerdefinierten Methoden testen. Diese überprüfen im Wesentlichen nur, ob der Objektname wie erwartet im \"Nachname, Vorname\"-Format konstruiert wurde und ob die URL, die wir für ein <code>Author</code>-Objekt erhalten, wie erwartet ist.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>def test_object_name_is_last_name_comma_first_name(self):\n author = Author.objects.get(id=1)\n expected_object_name = f'{author.last_name}, {author.first_name}'\n self.assertEqual(str(author), expected_object_name)\n\ndef test_get_absolute_url(self):\n author = Author.objects.get(id=1)\n # This will also fail if the urlconf is not defined.\n self.assertEqual(author.get_absolute_url(), '/catalog/author/1')\n</code></pre></div>\n<p>Führen Sie die Tests jetzt aus. Wenn Sie das Author-Modell so erstellt haben, wie wir es im Modelle-Tutorial beschrieben haben, ist es sehr wahrscheinlich, dass Sie einen Fehler für das <code>date_of_death</code>-Label erhalten, wie unten gezeigt. Der Test schlägt fehl, weil er erwartet, dass die Etikettendefinition dem Django-Konvention folgt, den ersten Buchstaben des Etiketts nicht zu kapitalisieren (Django macht dies für Sie).</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">bash</span></div><pre class=\"brush: bash notranslate\"><code>======================================================================\nFAIL: test_date_of_death_label (catalog.tests.test_models.AuthorModelTest)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"D:\\...\\locallibrary\\catalog\\tests\\test_models.py\", line 32, in test_date_of_death_label\n self.assertEqual(field_label,'died')\nAssertionError: 'Died' != 'died'\n- Died\n? ^\n+ died\n? ^\n</code></pre></div>\n<p>Dies ist ein sehr kleiner Fehler, aber er zeigt auf, wie das Schreiben von Tests gründlicher mögliche Annahmen überprüfen kann.</p>\n<div class=\"notecard note\">\n <p><strong>Hinweis:</strong> Ändern Sie das Label für das <code>date_of_death</code>-Feld (<strong>/catalog/models.py</strong>) in \"died\" und führen Sie die Tests erneut aus.</p>\n</div>\n<p>Die Muster für die Prüfung der anderen Modelle sind ähnlich, daher werden wir diese nicht weiter diskutieren. Fühlen Sie sich frei, Ihre eigenen Tests für unsere anderen Modelle zu erstellen.</p>"}},{"type":"prose","value":{"id":"formulare","title":"Formulare","isH3":true,"content":"<p>Die Philosophie für die Prüfung Ihrer Formulare ist die gleiche wie für die Prüfung Ihrer Modelle; Sie müssen alles testen, was Sie selbst kodiert haben oder was Ihr Design spezifiziert, aber nicht das Verhalten des zugrunde liegenden Frameworks und anderer Drittanbieterbibliotheken.</p>\n<p>Generell bedeutet dies, dass Sie überprüfen sollten, dass die Formulare die Felder haben, die Sie möchten, und dass diese mit den entsprechenden Etiketten und Hilfetexten angezeigt werden. Sie müssen nicht überprüfen, dass Django den Feldtyp korrekt validiert (es sei denn, Sie haben Ihr eigenes benutzerdefiniertes Feld und die Validierung erstellt) – das heißt, Sie müssen nicht testen, ob ein E-Mail-Feld nur E-Mails akzeptiert. Allerdings müssten Sie jede zusätzliche Validierung testen, die Sie erwarten, die auf den Feldern durchgeführt wird, und alle Nachrichten, die Ihr Code für Fehler generieren wird.</p>\n<p>Betrachten Sie unser Formular zum Erneuern von Büchern. Dieses hat nur ein Feld für das Erneuerungsdatum, das ein Etikett und einen Hilfetext haben wird, die wir überprüfen müssen.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>class RenewBookForm(forms.Form):\n \"\"\"Form for a librarian to renew books.\"\"\"\n renewal_date = forms.DateField(help_text=\"Enter a date between now and 4 weeks (default 3).\")\n\n def clean_renewal_date(self):\n data = self.cleaned_data['renewal_date']\n\n # Check if a date is not in the past.\n if data &lt; datetime.date.today():\n raise ValidationError(_('Invalid date - renewal in past'))\n\n # Check if date is in the allowed range (+4 weeks from today).\n if data &gt; datetime.date.today() + datetime.timedelta(weeks=4):\n raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))\n\n # Remember to always return the cleaned data.\n return data\n</code></pre></div>\n<p>Öffnen Sie unsere <strong>/catalog/tests/test_forms.py</strong>-Datei und ersetzen Sie etwaigen vorhandenen Code mit dem folgenden Testcode für das <code>RenewBookForm</code>-Formular. Wir beginnen damit, unser Formular und einige Python- und Django-Bibliotheken zu importieren, die das Testen zeitbezogener Funktionalität unterstützen. Anschließend deklarieren wir unsere Formulartestklasse auf die gleiche Weise wie für Modelle, mit einem beschreibenden Namen für unsere von <code>TestCase</code> abgeleitete Testklasse.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>import datetime\n\nfrom django.test import TestCase\nfrom django.utils import timezone\n\nfrom catalog.forms import RenewBookForm\n\nclass RenewBookFormTest(TestCase):\n def test_renew_form_date_field_label(self):\n form = RenewBookForm()\n self.assertTrue(form.fields['renewal_date'].label is None or form.fields['renewal_date'].label == 'renewal date')\n\n def test_renew_form_date_field_help_text(self):\n form = RenewBookForm()\n self.assertEqual(form.fields['renewal_date'].help_text, 'Enter a date between now and 4 weeks (default 3).')\n\n def test_renew_form_date_in_past(self):\n date = datetime.date.today() - datetime.timedelta(days=1)\n form = RenewBookForm(data={'renewal_date': date})\n self.assertFalse(form.is_valid())\n\n def test_renew_form_date_too_far_in_future(self):\n date = datetime.date.today() + datetime.timedelta(weeks=4) + datetime.timedelta(days=1)\n form = RenewBookForm(data={'renewal_date': date})\n self.assertFalse(form.is_valid())\n\n def test_renew_form_date_today(self):\n date = datetime.date.today()\n form = RenewBookForm(data={'renewal_date': date})\n self.assertTrue(form.is_valid())\n\n def test_renew_form_date_max(self):\n date = timezone.localtime() + datetime.timedelta(weeks=4)\n form = RenewBookForm(data={'renewal_date': date})\n self.assertTrue(form.is_valid())\n</code></pre></div>\n<p>Die ersten beiden Funktionen testen, ob das <code>label</code> und der <code>help_text</code> des Feldes wie erwartet sind. Wir müssen auf das Feld über das Felderverzeichnis zugreifen (z. B. <code>form.fields['renewal_date']</code>). Beachten Sie hier, dass wir auch testen müssen, ob der Labelwert <code>None</code> ist, da Django, auch wenn es das korrekte Etikett rendert, <code>None</code> zurückgibt, wenn der Wert nicht <em>explizit</em> gesetzt ist.</p>\n<p>Die restlichen Funktionen testen, ob das Formular für Erneuerungsdaten innerhalb des akzeptablen Bereichs gültig ist und für Werte außerhalb des Bereichs ungültig ist. Beachten Sie, wie wir Testdatumswerte um unser aktuelles Datum (<code>datetime.date.today()</code>) mit <code>datetime.timedelta()</code> konstruieren (in diesem Fall gibt eine Anzahl von Tagen oder Wochen an). Wir erstellen dann einfach das Formular, übergeben unsere Daten und testen, ob es gültig ist.</p>\n<div class=\"notecard note\">\n <p><strong>Hinweis:</strong> Hier verwenden wir tatsächlich nicht die Datenbank oder den Test-Client. Erwägen Sie, diese Tests zu ändern, um <a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/tools/#django.test.SimpleTestCase\" class=\"external\" target=\"_blank\">SimpleTestCase</a> zu verwenden.</p>\n <p>Wir müssen auch validieren, dass die richtigen Fehler ausgelöst werden, wenn das Formular ungültig ist, aber dies wird normalerweise als Teil der View-Verarbeitung durchgeführt, daher werden wir uns darum im nächsten Abschnitt kümmern.</p>\n</div>\n<div class=\"notecard warning\">\n <p><strong>Warnung:</strong> Wenn Sie die Klasse <a href=\"/de/docs/Learn/Server-side/Django/Forms#modelforms\">ModelForm</a> <code>RenewBookModelForm(forms.ModelForm)</code> anstelle der Klasse <code>RenewBookForm(forms.Form)</code> verwenden, dann wäre der Formularfeldname <strong>'due_back'</strong> anstelle von <strong>'renewal_date'</strong>.</p>\n</div>\n<p>Das ist alles für Formulare; wir haben einige andere, aber sie werden automatisch durch unsere generischen Class-Based-Editing-Views erstellt und sollten dort getestet werden! Führen Sie die Tests aus und bestätigen Sie, dass unser Code immer noch funktioniert!</p>"}},{"type":"prose","value":{"id":"views","title":"Views","isH3":true,"content":"<p>Um unser View-Verhalten zu validieren, verwenden wir den Django-Test-<a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/tools/#django.test.Client\" class=\"external\" target=\"_blank\">Client</a>. Diese Klasse verhält sich wie ein Dummie-Webbrowser, den wir nutzen können, um <code>GET</code>- und <code>POST</code>-Anfragen zu simulieren und die Antwort zu beobachten. Wir können fast alles über die Antwort sehen, von der niedrigen HTTP-Ebene (Ergebnisheader und Statuscodes) über die Vorlage, die wir verwenden, um das HTML zu rendern, bis zu den Kontextdaten, die wir an diese weitergeben. Wir können auch die Kette der Umleitungen (falls vorhanden) sehen und die URL und den Statuscode in jedem Schritt überprüfen. Dies ermöglicht es uns zu überprüfen, dass jede View das tut, was erwartet wird.</p>\n<p>Beginnen wir mit einer unserer einfachsten Views, die eine Liste aller Autoren bereitstellt. Diese wird unter der URL <strong>/catalog/authors/</strong> angezeigt (eine URL mit dem Namen 'authors' in der URL-Konfiguration).</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>class AuthorListView(generic.ListView):\n model = Author\n paginate_by = 10\n</code></pre></div>\n<p>Da dies eine generische Listenansicht ist, wird fast alles von Django für uns erledigt. Wenn Sie Django vertrauen, müssen Sie im Grunde nur testen, dass die View unter der richtigen URL zugänglich ist und mit ihrem Namen aufgerufen werden kann. Wenn Sie jedoch einen testgetriebenen Entwicklungsprozess verwenden, beginnen Sie mit dem Schreiben von Tests, die bestätigen, dass die View alle Autoren anzeigt und sie in Listen von jeweils 10 Seiten paginiert.</p>\n<p>Öffnen Sie die Datei <strong>/catalog/tests/test_views.py</strong> und ersetzen Sie eventuellen vorhandenen Text mit dem folgenden Testcode für <code>AuthorListView</code>. Wie zuvor importieren wir unser Modell und einige nützliche Klassen. In der <code>setUpTestData()</code>-Methode richten wir eine Reihe von <code>Author</code>-Objekten ein, damit wir unsere Paginierung testen können.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>from django.test import TestCase\nfrom django.urls import reverse\n\nfrom catalog.models import Author\n\nclass AuthorListViewTest(TestCase):\n @classmethod\n def setUpTestData(cls):\n # Create 13 authors for pagination tests\n number_of_authors = 13\n\n for author_id in range(number_of_authors):\n Author.objects.create(\n first_name=f'Dominique {author_id}',\n last_name=f'Surname {author_id}',\n )\n\n def test_view_url_exists_at_desired_location(self):\n response = self.client.get('/catalog/authors/')\n self.assertEqual(response.status_code, 200)\n\n def test_view_url_accessible_by_name(self):\n response = self.client.get(reverse('authors'))\n self.assertEqual(response.status_code, 200)\n\n def test_view_uses_correct_template(self):\n response = self.client.get(reverse('authors'))\n self.assertEqual(response.status_code, 200)\n self.assertTemplateUsed(response, 'catalog/author_list.html')\n\n def test_pagination_is_ten(self):\n response = self.client.get(reverse('authors'))\n self.assertEqual(response.status_code, 200)\n self.assertTrue('is_paginated' in response.context)\n self.assertTrue(response.context['is_paginated'] == True)\n self.assertEqual(len(response.context['author_list']), 10)\n\n def test_lists_all_authors(self):\n # Get second page and confirm it has (exactly) remaining 3 items\n response = self.client.get(reverse('authors')+'?page=2')\n self.assertEqual(response.status_code, 200)\n self.assertTrue('is_paginated' in response.context)\n self.assertTrue(response.context['is_paginated'] == True)\n self.assertEqual(len(response.context['author_list']), 3)\n</code></pre></div>\n<p>Alle Tests nutzen den Client (der zur <code>TestCase</code>-abgeleiteten Klasse gehört), um eine <code>GET</code>-Anfrage zu simulieren und eine Antwort zu erhalten. Die erste Version überprüft eine spezifische URL (beachten Sie, nur den spezifischen Pfad ohne die Domain), während die zweite die URL aus ihrem Namen in der URL-Konfiguration generiert.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>response = self.client.get('/catalog/authors/')\nresponse = self.client.get(reverse('authors'))\n</code></pre></div>\n<p>Sobald wir die Antwort haben, fragen wir sie nach ihrem Statuscode, der verwendeten Vorlage, ob die Antwort paginiert ist, der Anzahl der zurückgegebenen Elemente und der Gesamtanzahl der Elemente ab.</p>\n<div class=\"notecard note\">\n <p><strong>Hinweis:</strong> Wenn Sie die Variable <code>paginate_by</code> in Ihrer Datei <strong>/catalog/views.py</strong> auf eine andere Zahl als 10 gesetzt haben, stellen Sie sicher, dass Sie die Zeilen, die testen, ob die korrekte Anzahl von Elementen in paginierten Vorlagen angezeigt wird, im oben genannten und in den folgenden Abschnitten aktualisieren. Zum Beispiel, wenn Sie die Variable für die Autorenlisten-Seite auf 5 gesetzt haben, aktualisieren Sie die Zeile oben auf:</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>self.assertTrue(len(response.context['author_list']) == 5)\n</code></pre></div>\n</div>\n<p>\n Die interessanteste Variable, die wir oben demonstrieren, ist <code>response.context</code>, welche die Kontextvariable ist, die von der View an die Vorlage übergeben wird.\n Dies ist unglaublich nützlich beim Testen, da es uns ermöglicht zu bestätigen, dass unsere Vorlage alle Daten erhält, die sie benötigt. Mit anderen Worten, wir können überprüfen, dass wir die beabsichtigte Vorlage verwenden und welche Daten die Vorlage erhält, was viel dazu beiträgt, zu bestätigen, dass alle Renderfehler ausschließlich der Vorlage zuzuschreiben sind.\n</p>\n<h4 id=\"views_die_auf_eingeloggte_benutzer_beschränkt_sind\">Views, die auf eingeloggte Benutzer beschränkt sind</h4>\n<p>In einigen Fällen möchten Sie eine View testen, die nur für eingeloggte Benutzer beschränkt ist. Zum Beispiel ist unsere <code>LoanedBooksByUserListView</code> sehr ähnlich unserer vorherigen Ansicht, aber nur für eingeloggte Benutzer verfügbar und zeigt nur <code>BookInstance</code>-Einträge an, die vom aktuellen Benutzer entliehen wurden, den Status \"on loan\" haben und \"älteste zuerst\" geordnet sind.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>from django.contrib.auth.mixins import LoginRequiredMixin\n\nclass LoanedBooksByUserListView(LoginRequiredMixin, generic.ListView):\n \"\"\"Generic class-based view listing books on loan to current user.\"\"\"\n model = BookInstance\n template_name ='catalog/bookinstance_list_borrowed_user.html'\n paginate_by = 10\n\n def get_queryset(self):\n return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back')\n</code></pre></div>\n<p>Fügen Sie den folgenden Testcode zu <strong>/catalog/tests/test_views.py</strong> hinzu. Hier verwenden wir zuerst <code>SetUp()</code>, um einige Benutzerkonten und <code>BookInstance</code>-Objekte (zusammen mit ihren zugehörigen Büchern und anderen Einträgen) zu erstellen, die wir später in den Tests verwenden werden. Die Hälfte der Bücher wird von jedem Testbenutzer ausgeliehen, aber wir haben den Status aller Bücher zunächst auf \"maintenance\" gesetzt. Wir haben <code>SetUp()</code> anstelle von <code>setUpTestData()</code> verwendet, da wir einige dieser Objekte später ändern werden.</p>\n<div class=\"notecard note\">\n <p><strong>Hinweis:</strong> Der <code>setUp()</code>-Code unten erstellt ein Buch mit einer bestimmten <code>Language</code>, aber <em>Ihr</em> Code enthält möglicherweise nicht das <code>Language</code>-Modell, da dies als <em>Herausforderung</em> erstellt wurde. Wenn dies der Fall ist, kommentieren Sie die Teile des Codes aus, die Language-Objekte erzeugen oder importieren. Dies sollten Sie auch im Abschnitt <code>RenewBookInstancesViewTest</code> tun, der folgt.</p>\n</div>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>import datetime\n\nfrom django.utils import timezone\n\n# Get user model from settings\nfrom django.contrib.auth import get_user_model\nUser = get_user_model()\n\nfrom catalog.models import BookInstance, Book, Genre, Language\n\nclass LoanedBookInstancesByUserListViewTest(TestCase):\n def setUp(self):\n # Create two users\n test_user1 = User.objects.create_user(username='testuser1', password='1X&lt;ISRUkw+tuK')\n test_user2 = User.objects.create_user(username='testuser2', password='2HJ1vRV0Z&amp;3iD')\n\n test_user1.save()\n test_user2.save()\n\n # Create a book\n test_author = Author.objects.create(first_name='John', last_name='Smith')\n test_genre = Genre.objects.create(name='Fantasy')\n test_language = Language.objects.create(name='English')\n test_book = Book.objects.create(\n title='Book Title',\n summary='My book summary',\n isbn='ABCDEFG',\n author=test_author,\n language=test_language,\n )\n\n # Create genre as a post-step\n genre_objects_for_book = Genre.objects.all()\n test_book.genre.set(genre_objects_for_book) # Direct assignment of many-to-many types not allowed.\n test_book.save()\n\n # Create 30 BookInstance objects\n number_of_book_copies = 30\n for book_copy in range(number_of_book_copies):\n return_date = timezone.localtime() + datetime.timedelta(days=book_copy%5)\n the_borrower = test_user1 if book_copy % 2 else test_user2\n status = 'm'\n BookInstance.objects.create(\n book=test_book,\n imprint='Unlikely Imprint, 2016',\n due_back=return_date,\n borrower=the_borrower,\n status=status,\n )\n\n def test_redirect_if_not_logged_in(self):\n response = self.client.get(reverse('my-borrowed'))\n self.assertRedirects(response, '/accounts/login/?next=/catalog/mybooks/')\n\n def test_logged_in_uses_correct_template(self):\n login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK')\n response = self.client.get(reverse('my-borrowed'))\n\n # Check our user is logged in\n self.assertEqual(str(response.context['user']), 'testuser1')\n # Check that we got a response \"success\"\n self.assertEqual(response.status_code, 200)\n\n # Check we used correct template\n self.assertTemplateUsed(response, 'catalog/bookinstance_list_borrowed_user.html')\n</code></pre></div>\n<p>Um zu überprüfen, dass die View zu einer Login-Seite umleitet, wenn der Benutzer nicht eingeloggt ist, verwenden wir <code>assertRedirects</code>, wie in <code>test_redirect_if_not_logged_in()</code> demonstriert. Um zu überprüfen, dass die Seite für einen eingeloggten Benutzer angezeigt wird, loggen wir zuerst unseren Testbenutzer ein und greifen dann erneut auf die Seite zu, um zu überprüfen, ob wir einen <code>status_code</code> von 200 erhalten (Erfolg).</p>\n<p>Die restlichen Tests überprüfen, dass unsere View nur Bücher zurückgibt, die bei unserem aktuellen Entleiher \"ausgeliehen\" sind. Kopieren Sie den untenstehenden Code und fügen Sie ihn an das Ende der oben genannten Testklasse ein.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code> def test_only_borrowed_books_in_list(self):\n login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK')\n response = self.client.get(reverse('my-borrowed'))\n\n # Check our user is logged in\n self.assertEqual(str(response.context['user']), 'testuser1')\n # Check that we got a response \"success\"\n self.assertEqual(response.status_code, 200)\n\n # Check that initially we don't have any books in list (none on loan)\n self.assertTrue('bookinstance_list' in response.context)\n self.assertEqual(len(response.context['bookinstance_list']), 0)\n\n # Now change all books to be on loan\n books = BookInstance.objects.all()[:10]\n\n for book in books:\n book.status = 'o'\n book.save()\n\n # Check that now we have borrowed books in the list\n response = self.client.get(reverse('my-borrowed'))\n # Check our user is logged in\n self.assertEqual(str(response.context['user']), 'testuser1')\n # Check that we got a response \"success\"\n self.assertEqual(response.status_code, 200)\n\n self.assertTrue('bookinstance_list' in response.context)\n\n # Confirm all books belong to testuser1 and are on loan\n for book_item in response.context['bookinstance_list']:\n self.assertEqual(response.context['user'], book_item.borrower)\n self.assertEqual(book_item.status, 'o')\n\n def test_pages_ordered_by_due_date(self):\n # Change all books to be on loan\n for book in BookInstance.objects.all():\n book.status='o'\n book.save()\n\n login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK')\n response = self.client.get(reverse('my-borrowed'))\n\n # Check our user is logged in\n self.assertEqual(str(response.context['user']), 'testuser1')\n # Check that we got a response \"success\"\n self.assertEqual(response.status_code, 200)\n\n # Confirm that of the items, only 10 are displayed due to pagination.\n self.assertEqual(len(response.context['bookinstance_list']), 10)\n\n last_date = 0\n for book in response.context['bookinstance_list']:\n if last_date == 0:\n last_date = book.due_back\n else:\n self.assertTrue(last_date &lt;= book.due_back)\n last_date = book.due_back\n</code></pre></div>\n<p>Sie könnten auch Paginierungstests hinzufügen, wenn Sie möchten!</p>\n<h4 id=\"testen_von_views_mit_formularen\">Testen von Views mit Formularen</h4>\n<p>Das Testen von Views mit Formularen ist etwas komplizierter als in den oben genannten Fällen, da Sie mehr Codepfade testen müssen: Erstanzeige, Anzeige nach fehlgeschlagener Datenvalidierung und Anzeige nach erfolgreicher Validierung. Die gute Nachricht ist, dass wir den Client für das Testen fast genau auf die gleiche Weise wie für Anzeige-Only-Views verwenden.</p>\n<p>Um dies zu demonstrieren, schreiben wir einige Tests für die View, die verwendet wird, um Bücher zu erneuern (<code>renew_book_librarian()</code>):</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>from catalog.forms import RenewBookForm\n\n@permission_required('catalog.can_mark_returned')\ndef renew_book_librarian(request, pk):\n \"\"\"View function for renewing a specific BookInstance by librarian.\"\"\"\n book_instance = get_object_or_404(BookInstance, pk=pk)\n\n # If this is a POST request then process the Form data\n if request.method == 'POST':\n\n # Create a form instance and populate it with data from the request (binding):\n book_renewal_form = RenewBookForm(request.POST)\n\n # Check if the form is valid:\n if form.is_valid():\n # process the data in form.cleaned_data as required (here we just write it to the model due_back field)\n book_instance.due_back = form.cleaned_data['renewal_date']\n book_instance.save()\n\n # redirect to a new URL:\n return HttpResponseRedirect(reverse('all-borrowed'))\n\n # If this is a GET (or any other method) create the default form\n else:\n proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)\n book_renewal_form = RenewBookForm(initial={'renewal_date': proposed_renewal_date})\n\n context = {\n 'book_renewal_form': book_renewal_form,\n 'book_instance': book_instance,\n }\n\n return render(request, 'catalog/book_renew_librarian.html', context)\n</code></pre></div>\n<p>Wir müssen testen, dass die View nur für Benutzer verfügbar ist, die die Berechtigung <code>can_mark_returned</code> haben, und dass Benutzer auf eine HTTP 404-Fehlerseite umgeleitet werden, wenn sie versuchen, eine <code>BookInstance</code> zu erneuern, die nicht existiert. Wir sollten überprüfen, dass der Initialwert des Formulars mit einem Datum drei Wochen in der Zukunft gefüllt ist, und dass, wenn die Validierung erfolgreich ist, wir zur \"alle entliehenen Bücher\"-View umgeleitet werden. Beim Überprüfen der Validierungsfehler-Tests überprüfen wir auch, dass unser Formular die entsprechenden Fehlermeldungen sendet.</p>\n<p>\n Fügen Sie den ersten Teil der Testklasse (wie unten gezeigt) zum Ende von <strong>/catalog/tests/test_views.py</strong> hinzu.\n Diese erstellt zwei Benutzer und zwei Buchinstanzen, aber nur ein Benutzer erhält die Berechtigung, die View zuzugreifen.\n</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>import uuid\n\nfrom django.contrib.auth.models import Permission # Required to grant the permission needed to set a book as returned.\n\nclass RenewBookInstancesViewTest(TestCase):\n def setUp(self):\n # Create a user\n test_user1 = User.objects.create_user(username='testuser1', password='1X&lt;ISRUkw+tuK')\n test_user2 = User.objects.create_user(username='testuser2', password='2HJ1vRV0Z&amp;3iD')\n\n test_user1.save()\n test_user2.save()\n\n # Give test_user2 permission to renew books.\n permission = Permission.objects.get(name='Set book as returned')\n test_user2.user_permissions.add(permission)\n test_user2.save()\n\n # Create a book\n test_author = Author.objects.create(first_name='John', last_name='Smith')\n test_genre = Genre.objects.create(name='Fantasy')\n test_language = Language.objects.create(name='English')\n test_book = Book.objects.create(\n title='Book Title',\n summary='My book summary',\n isbn='ABCDEFG',\n author=test_author,\n language=test_language,\n )\n\n # Create genre as a post-step\n genre_objects_for_book = Genre.objects.all()\n test_book.genre.set(genre_objects_for_book) # Direct assignment of many-to-many types not allowed.\n test_book.save()\n\n # Create a BookInstance object for test_user1\n return_date = datetime.date.today() + datetime.timedelta(days=5)\n self.test_bookinstance1 = BookInstance.objects.create(\n book=test_book,\n imprint='Unlikely Imprint, 2016',\n due_back=return_date,\n borrower=test_user1,\n status='o',\n )\n\n # Create a BookInstance object for test_user2\n return_date = datetime.date.today() + datetime.timedelta(days=5)\n self.test_bookinstance2 = BookInstance.objects.create(\n book=test_book,\n imprint='Unlikely Imprint, 2016',\n due_back=return_date,\n borrower=test_user2,\n status='o',\n )\n</code></pre></div>\n<p>Fügen Sie die folgenden Tests am Ende der Testklasse hinzu. Diese überprüfen, dass nur Benutzer mit den richtigen Berechtigungen (<em>testuser2</em>) auf die View zugreifen können. Wir überprüfen alle Fälle: wenn der Benutzer nicht eingeloggt ist, wenn ein Benutzer eingeloggt ist, aber nicht die richtigen Berechtigungen hat, wenn der Benutzer Berechtigungen hat, aber nicht der Entleiher ist (sollte erfolgreich sein), und was passiert, wenn sie auf eine <code>BookInstance</code> zugreifen, die nicht existiert. Wir überprüfen auch, dass die korrekte Vorlage verwendet wird.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code> def test_redirect_if_not_logged_in(self):\n response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))\n # Manually check redirect (Can't use assertRedirect, because the redirect URL is unpredictable)\n self.assertEqual(response.status_code, 302)\n self.assertTrue(response.url.startswith('/accounts/login/'))\n\n def test_forbidden_if_logged_in_but_not_correct_permission(self):\n login = self.client.login(username='testuser1', password='1X&lt;ISRUkw+tuK')\n response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))\n self.assertEqual(response.status_code, 403)\n\n def test_logged_in_with_permission_borrowed_book(self):\n login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')\n response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance2.pk}))\n\n # Check that it lets us login - this is our book and we have the right permissions.\n self.assertEqual(response.status_code, 200)\n\n def test_logged_in_with_permission_another_users_borrowed_book(self):\n login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')\n response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))\n\n # Check that it lets us login. We're a librarian, so we can view any users book\n self.assertEqual(response.status_code, 200)\n\n def test_HTTP404_for_invalid_book_if_logged_in(self):\n # unlikely UID to match our bookinstance!\n test_uid = uuid.uuid4()\n login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')\n response = self.client.get(reverse('renew-book-librarian', kwargs={'pk':test_uid}))\n self.assertEqual(response.status_code, 404)\n\n def test_uses_correct_template(self):\n login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')\n response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))\n self.assertEqual(response.status_code, 200)\n\n # Check we used correct template\n self.assertTemplateUsed(response, 'catalog/book_renew_librarian.html')\n</code></pre></div>\n<p>Fügen Sie die nächste Testmethode hinzu, wie unten gezeigt. Diese überprüft, ob das Anfangsdatum für das Formular drei Wochen in der Zukunft liegt. Beachten Sie, wie wir in der Lage sind, auf den Initialwert des Formularfeldes (<code>response.context['form'].initial['renewal_date'])</code> zuzugreifen.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code> def test_form_renewal_date_initially_has_date_three_weeks_in_future(self):\n login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')\n response = self.client.get(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}))\n self.assertEqual(response.status_code, 200)\n\n date_3_weeks_in_future = datetime.date.today() + datetime.timedelta(weeks=3)\n self.assertEqual(response.context['form'].initial['renewal_date'], date_3_weeks_in_future)\n</code></pre></div>\n<p>Der nächste Test (fügen Sie diesen auch zur Klasse hinzu) überprüft, dass die View bei erfolgreicher Erneuerung zu einer Liste aller entliehenen Bücher umgeleitet wird. Was hier abweicht, ist, dass wir zum ersten Mal zeigen, wie Sie Daten mit dem Client <code>POST</code>-en können. Die <code>post</code>-Daten sind das zweite Argument der Post-Funktion und werden als Wörterbuch von Schlüssel/Werten angegeben.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code> def test_redirects_to_all_borrowed_book_list_on_success(self):\n login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')\n valid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=2)\n response = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future})\n self.assertRedirects(response, reverse('all-borrowed'))\n</code></pre></div>\n<div class=\"notecard warning\">\n <p><strong>Warnung:</strong> Der <em>all-borrowed</em>-View wurde als <em>Herausforderung</em> hinzugefügt, und Ihr Code könnte stattdessen zur Startseite '/' umleiten. Wenn ja, ändern Sie die letzten beiden Zeilen des Testcodes so, dass sie wie der unten dargestellte Code sind. Das <code>follow=True</code> in der Anfrage stellt sicher, dass die Anfrage die endgültige Ziel-URL zurückgibt (daher das Prüfen auf <code>/catalog/</code> statt auf <code>/</code>).</p>\n <div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code> response = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future}, follow=True)\n self.assertRedirects(response, '/catalog/')\n</code></pre></div>\n</div>\n<p>Kopieren Sie die letzten beiden Funktionen in die Klasse, wie unten gezeigt. Diese testen erneut <code>POST</code>-Anfragen, aber in diesem Fall mit ungültigen Erneuerungsdaten. Wir verwenden <code>assertFormError()</code>, um zu überprüfen, dass die Fehlermeldungen wie erwartet sind.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code> def test_form_invalid_renewal_date_past(self):\n login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')\n date_in_past = datetime.date.today() - datetime.timedelta(weeks=1)\n response = self.client.post(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}), {'renewal_date': date_in_past})\n self.assertEqual(response.status_code, 200)\n self.assertFormError(response.context['form'], 'renewal_date', 'Invalid date - renewal in past')\n\n def test_form_invalid_renewal_date_future(self):\n login = self.client.login(username='testuser2', password='2HJ1vRV0Z&amp;3iD')\n invalid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=5)\n response = self.client.post(reverse('renew-book-librarian', kwargs={'pk': self.test_bookinstance1.pk}), {'renewal_date': invalid_date_in_future})\n self.assertEqual(response.status_code, 200)\n self.assertFormError(response.context['form'], 'renewal_date', 'Invalid date - renewal more than 4 weeks ahead')\n</code></pre></div>\n<p>Die gleichen Techniken können verwendet werden, um die andere View zu testen.</p>"}},{"type":"prose","value":{"id":"vorlagen","title":"Vorlagen","isH3":true,"content":"<p>Django bietet Test-APIs, um zu überprüfen, dass die korrekte Vorlage von Ihren Views aufgerufen wird und um zu überprüfen, dass die korrekten Informationen geschickt werden. Es gibt jedoch keine spezifische API-Unterstützung im Django, um zu testen, dass Ihre HTML-Ausgabe wie erwartet gerendert wird.</p>"}},{"type":"prose","value":{"id":"weitere_empfohlene_testwerkzeuge","title":"Weitere empfohlene Testwerkzeuge","isH3":false,"content":"<p>Das Testframework von Django kann Ihnen helfen, effektive Unit- und Integrationstests zu schreiben — wir haben nur an der Oberfläche dessen gekratzt, was das zugrunde liegende <strong>unittest</strong>-Framework tun kann, geschweige denn, was Django hinzufügt (zum Beispiel versuchen Sie herauszufinden, wie <a href=\"https://docs.python.org/3/library/unittest.mock-examples.html\" class=\"external\" target=\"_blank\">unittest.mock</a> verwendet wird, um Bibliotheken von Drittanbietern zu patchen, damit Sie Ihren eigenen Code gründlicher testen können).</p>\n<p>Während es zahlreiche andere Testwerkzeuge gibt, die Sie verwenden können, werden wir nur zwei hervorheben:</p>\n<ul>\n <li><a href=\"https://coverage.readthedocs.io/en/latest/\" class=\"external\" target=\"_blank\">Coverage</a>: Dieses Python-Tool berichtet darüber, wie viel Code durch Ihre Tests tatsächlich ausgeführt wird. Es ist besonders nützlich, wenn Sie anfangen und herausfinden möchten, was genau getestet werden soll.</li>\n <li><a href=\"/de/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment\">Selenium</a> ist ein Framework zur Automatisierung der Tests in einem echten Browser. Es ermöglicht Ihnen, einen echten Benutzer zu simulieren, der mit der Seite interagiert, und bietet ein großartiges Framework für systematische Tests Ihrer Seite (der nächste Schritt von Integrationstests).</li>\n</ul>"}},{"type":"prose","value":{"id":"fordern_sie_sich_selbst_heraus","title":"Fordern Sie sich selbst heraus","isH3":false,"content":"<p>Es gibt noch viele weitere Modelle und Ansichten, die wir testen können. Als Herausforderung versuchen Sie, einen Testfall für die <code>AuthorCreate</code>-View zu erstellen.</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>class AuthorCreate(PermissionRequiredMixin, CreateView):\n model = Author\n fields = ['first_name', 'last_name', 'date_of_birth', 'date_of_death']\n initial = {'date_of_death': '11/11/2023'}\n permission_required = 'catalog.add_author'\n</code></pre></div>\n<p>\n Denken Sie daran, dass Sie alles überprüfen müssen, was Sie spezifizieren oder was Teil des Designs ist.\n Dies umfasst, wer Zugang hat, das Anfangsdatum, die verwendete Vorlage und wohin die View bei Erfolg umleitet.\n</p>\n<p>Sie könnten den folgenden Code verwenden, um Ihren Test einzurichten und Ihrem Benutzer die entsprechende Berechtigung zu erteilen</p>\n<div class=\"code-example\"><div class=\"example-header\"><span class=\"language-name\">python</span></div><pre class=\"brush: python notranslate\"><code>class AuthorCreateViewTest(TestCase):\n \"\"\"Test case for the AuthorCreate view (Created as Challenge).\"\"\"\n\n def setUp(self):\n # Create a user\n test_user = User.objects.create_user(\n username='test_user', password='some_password')\n\n content_typeAuthor = ContentType.objects.get_for_model(Author)\n permAddAuthor = Permission.objects.get(\n codename=\"add_author\",\n content_type=content_typeAuthor,\n )\n\n test_user.user_permissions.add(permAddAuthor)\n test_user.save()\n</code></pre></div>"}},{"type":"prose","value":{"id":"zusammenfassung","title":"Zusammenfassung","isH3":false,"content":"<p>Testcode zu schreiben ist weder lustig noch glamourös und wird daher oft zuletzt (oder gar nicht) beim Erstellen einer Website erledigt. Es ist jedoch ein wesentlicher Teil, um sicherzustellen, dass Ihr Code nach Änderungen sicher veröffentlicht werden kann und wirtschaftlich zu warten ist.</p>\n<p>In diesem Tutorial haben wir Ihnen gezeigt, wie Sie Tests für Ihre Modelle, Formulare und Views schreiben und ausführen. Am wichtigsten ist, dass wir eine kurze Zusammenfassung dessen gegeben haben, was Sie testen sollten, was oft das Schwierigste zu bestimmen ist, wenn Sie anfangen. Es gibt viel mehr zu wissen, aber selbst mit dem, was Sie bereits gelernt haben, sollten Sie in der Lage sein, effektive Unittests für Ihre Websites zu erstellen.</p>\n<p>Das nächste und letzte Tutorial zeigt, wie Sie Ihre wunderbare (und vollständig getestete!) Django-Website bereitstellen können.</p>"}},{"type":"prose","value":{"id":"siehe_auch","title":"Siehe auch","isH3":false,"content":"<ul>\n <li><a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/overview/\" class=\"external\" target=\"_blank\">Schreiben und Ausführen von Tests</a> (Django-Dokumentation)</li>\n <li><a href=\"https://docs.djangoproject.com/en/5.0/intro/tutorial05/\" class=\"external\" target=\"_blank\">Schreiben Ihrer ersten Django-App, Teil 5 &gt; Einführung in automatisierte Tests</a> (Django-Dokumentation)</li>\n <li><a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/tools/\" class=\"external\" target=\"_blank\">Testing-Tools-Referenz</a> (Django-Dokumentation)</li>\n <li><a href=\"https://docs.djangoproject.com/en/5.0/topics/testing/advanced/\" class=\"external\" target=\"_blank\">Erweiterte Testthemen</a> (Django-Dokumentation)</li>\n <li><a href=\"https://toastdriven.com/blog/2011/apr/09/guide-to-testing-in-django/\" class=\"external\" target=\"_blank\">Ein Leitfaden zum Testen in Django</a> (Toast Driven Blog, 2011)</li>\n <li><a href=\"https://test-driven-django-development.readthedocs.io/en/latest/index.html\" class=\"external\" target=\"_blank\">Workshop: Testgetriebene Webentwicklung mit Django</a> (San Diego Python, 2014)</li>\n <li><a href=\"https://realpython.com/testing-in-django-part-1-best-practices-and-examples/\" class=\"external\" target=\"_blank\">Testen in Django (Teil 1) - Best Practices und Beispiele</a> (RealPython, 2013)</li>\n</ul><ul class=\"prev-next\">\n <li><a class=\"button secondary\" href=\"/de/docs/Learn/Server-side/Django/Forms\"><span class=\"button-wrap\"> Zurück </span></a></li>\n <li><a class=\"button secondary\" href=\"/de/docs/Learn/Server-side/Django\"><span class=\"button-wrap\"> Übersicht: Django Web Framework (Python)</span></a></li>\n <li><a class=\"button secondary\" href=\"/de/docs/Learn/Server-side/Django/Deployment\"><span class=\"button-wrap\"> Weiter </span></a></li>\n</ul>"}}],"toc":[{"text":"Überblick","id":"überblick"},{"text":"Überblick über die Teststruktur","id":"überblick_über_die_teststruktur"},{"text":"Wie führt man die Tests aus","id":"wie_führt_man_die_tests_aus"},{"text":"LocalLibrary Tests","id":"locallibrary_tests"},{"text":"Weitere empfohlene Testwerkzeuge","id":"weitere_empfohlene_testwerkzeuge"},{"text":"Fordern Sie sich selbst heraus","id":"fordern_sie_sich_selbst_heraus"},{"text":"Zusammenfassung","id":"zusammenfassung"},{"text":"Siehe auch","id":"siehe_auch"}],"summary":"Wenn Websites wachsen, werden sie schwieriger manuell zu testen. Nicht nur gibt es mehr zu testen, sondern auch, da die Interaktionen zwischen den Komponenten komplexer werden, kann eine kleine Änderung in einem Bereich andere Bereiche beeinflussen. Daher sind mehr Änderungen erforderlich, um sicherzustellen, dass alles weiterhin funktioniert und keine Fehler eingeführt werden, während mehr Änderungen vorgenommen werden. Eine Möglichkeit, diese Probleme zu mindern, ist das Schreiben automatisierter Tests, die bei jeder Änderung einfach und zuverlässig ausgeführt werden können. Dieses Tutorial zeigt, wie Sie mit Djangos Testframework das Unittesting Ihrer Website automatisieren können.","popularity":0,"modified":null,"other_translations":[{"locale":"en-US","title":"Django Tutorial Part 10: Testing a Django web application","native":"English (US)"},{"locale":"es","title":"Tutorial de Django Parte 10: Probando una aplicación web Django","native":"Español"},{"locale":"fr","title":"Django Tutorial Part 10: Testing a Django web application","native":"Français"},{"locale":"ko","title":"Django 튜토리얼 파트 10: Django 웹 어플리케이션 테스트하기","native":"한국어"},{"locale":"pt-BR","title":"Tutorial Django Parte 10: Testando uma aplicação web Django","native":"Português (do Brasil)"},{"locale":"ru","title":"Руководство часть 10: Тестирование приложений Django","native":"Русский"},{"locale":"zh-CN","title":"Django 教程 10: 测试 Django 网页应用","native":"中文 (简体)"}],"pageType":"unknown","source":{"folder":"de/learn/server-side/django/testing","github_url":"https://github.com/mdn/translated-content/blob/main/files/de/learn/server-side/django/testing/index.md","last_commit_url":"https://github.com/mdn/translated-content/commit/null","filename":"index.md"},"short_title":"Django Tutorial Teil 10: Testen einer Django-Webanwendung","parents":[{"uri":"/de/docs/Learn","title":"Webentwicklung lernen"},{"uri":"/de/docs/Learn/Server-side","title":"Server-side-Website-Programmierung"},{"uri":"/de/docs/Learn/Server-side/Django","title":"Django Web Framework (Python)"},{"uri":"/de/docs/Learn/Server-side/Django/Testing","title":"Django Tutorial Teil 10: Testen einer Django-Webanwendung"}],"pageTitle":"Django Tutorial Teil 10: Testen einer Django-Webanwendung - Webentwicklung lernen | MDN","noIndexing":false}}</script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10