├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── LICENSE ├── _config.yml ├── app-example └── client │ └── src │ └── App.css ├── app_example ├── backend │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── brekelov │ │ │ │ └── example │ │ │ │ └── backend │ │ │ │ ├── BackendApplication.java │ │ │ │ ├── controller │ │ │ │ └── GroupController.java │ │ │ │ ├── model │ │ │ │ ├── Event.java │ │ │ │ ├── Group.java │ │ │ │ └── User.java │ │ │ │ └── repository │ │ │ │ └── GroupRepository.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── brekelov │ │ └── example │ │ └── backend │ │ └── BackendApplicationTests.java └── frontend │ ├── .editorconfig │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── docker-compose.yml │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt │ ├── src │ ├── App.css │ ├── App.js │ ├── AppContext.js │ ├── TodoItem.js │ ├── TodoItem.test.js │ ├── TodoList.js │ ├── TodoList.test.js │ ├── __mocks__ │ │ ├── axios.js │ │ └── react-router-dom.js │ ├── custom-render.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── makeTodos.js │ ├── serviceWorker.js │ └── setupTests.js │ └── yarn.lock ├── frontend └── src │ └── App.css ├── lec_5_example ├── HELP.md ├── back.iml ├── mvnw ├── mvnw.cmd ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── lec5 │ │ │ │ └── back │ │ │ │ ├── BackApplication.java │ │ │ │ ├── EmptyNameException.java │ │ │ │ ├── GreetingController.java │ │ │ │ ├── GreetingService.java │ │ │ │ ├── HealthCheckController.java │ │ │ │ ├── Repository.java │ │ │ │ ├── User.java │ │ │ │ ├── User2Builder.java │ │ │ │ ├── UserBuilder.java │ │ │ │ ├── UserNotFoundException.java │ │ │ │ └── UsersController.java │ │ └── resources │ │ │ └── application.properties │ └── test │ │ └── java │ │ └── com │ │ └── lec5 │ │ └── back │ │ ├── BackApplicationTests.java │ │ ├── GreetingControllerComponentTests.java │ │ ├── GreetingControllerTests.java │ │ ├── GreetingServiceTests.java │ │ └── UserControllerTests.java └── target │ ├── classes │ ├── application.properties │ └── com │ │ └── lec5 │ │ └── back │ │ ├── BackApplication.class │ │ ├── EmptyNameException.class │ │ ├── GreetingController.class │ │ ├── GreetingService.class │ │ ├── HealthCheckController.class │ │ ├── Repository.class │ │ ├── User.class │ │ ├── User2Builder.class │ │ ├── UserBuilder.class │ │ ├── UserNotFoundException.class │ │ └── UsersController.class │ └── test-classes │ └── com │ └── lec5 │ └── back │ ├── BackApplicationTests.class │ ├── GreetingControllerComponentTests.class │ ├── GreetingControllerTests.class │ ├── GreetingServiceTests.class │ └── UserControllerTests.class ├── lectures_v_2020 ├── README.md ├── ST.Lecture1.pdf ├── ST.Lecture10.pdf ├── ST.Lecture11.pdf ├── ST.Lecture12.pdf ├── ST.Lecture4.pdf ├── ST.Lecture7.pdf └── ST.Lecture9.pdf └── lectures_v_2021 ├── README.MD ├── ST.Lecture1.pdf ├── ST.Lecture10.pdf ├── ST.Lecture12.pdf ├── ST.Lecture3.pdf ├── ST.Lecture6.pdf ├── ST.Lecture8.pdf └── ST.Lecture9.pdf /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | defaults: 15 | run: 16 | working-directory: ./app_example/frontend 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | matrix: 21 | node-version: [12.x, 14.x, 16.x] 22 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 23 | 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Use Node.js ${{ matrix.node-version }} 27 | uses: actions/setup-node@v2 28 | with: 29 | node-version: ${{ matrix.node-version }} 30 | cache: 'npm' 31 | cache-dependency-path: ./app_example/frontend/package-lock.json 32 | - run: npm ci 33 | - run: npm run build --if-present 34 | - run: npm test 35 | 36 | - name: deploy to gh pages 37 | uses: peaceiris/actions-gh-pages@v3 38 | with: 39 | github_token: ${{ secrets.GITHUB_TOKEN }} 40 | publish_dir: ./app_example/frontend/build 41 | 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /lec_5_example/target 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Vsevolod 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /app-example/client/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app_example/backend/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | 4 | -------------------------------------------------------------------------------- /app_example/backend/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | # Provide a "standardized" way to retrieve the CLI args that will 300 | # work with both Windows and non-Windows executions. 301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 302 | export MAVEN_CMD_LINE_ARGS 303 | 304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 305 | 306 | exec "$JAVACMD" \ 307 | $MAVEN_OPTS \ 308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 311 | -------------------------------------------------------------------------------- /app_example/backend/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /app_example/backend/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.5.6 9 | 10 | 11 | com.brekelov.example 12 | backend 13 | 0.0.1-SNAPSHOT 14 | backend 15 | Demo project 16 | 17 | 11 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-jpa 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | 30 | com.h2database 31 | h2 32 | runtime 33 | 34 | 35 | org.projectlombok 36 | lombok 37 | true 38 | 39 | 40 | javax.validation 41 | validation-api 42 | 2.0.1.Final 43 | 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-starter-test 49 | test 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-maven-plugin 58 | 59 | 60 | 61 | org.projectlombok 62 | lombok 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /app_example/backend/src/main/java/com/brekelov/example/backend/BackendApplication.java: -------------------------------------------------------------------------------- 1 | package com.brekelov.example.backend; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class BackendApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(BackendApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app_example/backend/src/main/java/com/brekelov/example/backend/controller/GroupController.java: -------------------------------------------------------------------------------- 1 | package com.brekelov.example.backend.controller; 2 | 3 | import com.brekelov.example.backend.model.Group; 4 | import com.brekelov.example.backend.repository.GroupRepository; 5 | import java.util.Collection; 6 | import java.util.Optional; 7 | import javax.validation.Valid; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.ResponseEntity; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.net.URI; 15 | import java.net.URISyntaxException; 16 | 17 | @RestController 18 | @RequestMapping("/api") 19 | class GroupController { 20 | 21 | private final Logger log = LoggerFactory.getLogger(GroupController.class); 22 | private GroupRepository groupRepository; 23 | 24 | public GroupController(GroupRepository groupRepository) { 25 | this.groupRepository = groupRepository; 26 | } 27 | 28 | @GetMapping("/groups") 29 | Collection groups() { 30 | return groupRepository.findAll(); 31 | } 32 | 33 | @GetMapping("/group/{id}") 34 | ResponseEntity getGroup(@PathVariable Long id) { 35 | Optional group = groupRepository.findById(id); 36 | return group.map(response -> ResponseEntity.ok().body(response)) 37 | .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND)); 38 | } 39 | 40 | @PostMapping("/group") 41 | ResponseEntity createGroup(@Valid @RequestBody Group group) throws URISyntaxException { 42 | log.info("Request to create group: {}", group); 43 | Group result = groupRepository.save(group); 44 | return ResponseEntity.created(new URI("/api/group/" + result.getId())) 45 | .body(result); 46 | } 47 | 48 | @PutMapping("/group/{id}") 49 | ResponseEntity updateGroup(@Valid @RequestBody Group group) { 50 | log.info("Request to update group: {}", group); 51 | Group result = groupRepository.save(group); 52 | return ResponseEntity.ok().body(result); 53 | } 54 | 55 | @DeleteMapping("/group/{id}") 56 | public ResponseEntity deleteGroup(@PathVariable Long id) { 57 | log.info("Request to delete group: {}", id); 58 | groupRepository.deleteById(id); 59 | return ResponseEntity.ok().build(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app_example/backend/src/main/java/com/brekelov/example/backend/model/Event.java: -------------------------------------------------------------------------------- 1 | package com.brekelov.example.backend.model; 2 | 3 | import java.time.Instant; 4 | import java.util.Set; 5 | import javax.persistence.Entity; 6 | import javax.persistence.GeneratedValue; 7 | import javax.persistence.Id; 8 | import javax.persistence.ManyToMany; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Builder; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | @Data 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @Builder 18 | @Entity 19 | public class Event { 20 | 21 | @Id 22 | @GeneratedValue 23 | private Long id; 24 | private Instant date; 25 | private String title; 26 | private String description; 27 | @ManyToMany 28 | private Set attendees; 29 | } 30 | -------------------------------------------------------------------------------- /app_example/backend/src/main/java/com/brekelov/example/backend/model/Group.java: -------------------------------------------------------------------------------- 1 | package com.brekelov.example.backend.model; 2 | 3 | import java.util.Set; 4 | import javax.persistence.CascadeType; 5 | import javax.persistence.Entity; 6 | import javax.persistence.FetchType; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.Id; 9 | import javax.persistence.ManyToOne; 10 | import javax.persistence.OneToMany; 11 | import javax.persistence.Table; 12 | import lombok.Data; 13 | import lombok.NoArgsConstructor; 14 | import lombok.NonNull; 15 | import lombok.RequiredArgsConstructor; 16 | 17 | @Data 18 | @NoArgsConstructor 19 | @RequiredArgsConstructor 20 | @Entity 21 | @Table(name = "user_group") 22 | public class Group { 23 | 24 | @Id 25 | @GeneratedValue 26 | private Long id; 27 | @NonNull 28 | private String name; 29 | private String address; 30 | private String city; 31 | private String stateOrProvince; 32 | private String country; 33 | private String postalCode; 34 | @ManyToOne(cascade= CascadeType.PERSIST) 35 | private User user; 36 | 37 | @OneToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL) 38 | private Set events; 39 | } -------------------------------------------------------------------------------- /app_example/backend/src/main/java/com/brekelov/example/backend/model/User.java: -------------------------------------------------------------------------------- 1 | package com.brekelov.example.backend.model; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.Id; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | @Entity 13 | public class User { 14 | 15 | @Id 16 | private String id; 17 | private String name; 18 | private String email; 19 | } 20 | -------------------------------------------------------------------------------- /app_example/backend/src/main/java/com/brekelov/example/backend/repository/GroupRepository.java: -------------------------------------------------------------------------------- 1 | package com.brekelov.example.backend.repository; 2 | 3 | import com.brekelov.example.backend.model.Group; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface GroupRepository extends JpaRepository { 7 | Group findByName(String name); 8 | } -------------------------------------------------------------------------------- /app_example/backend/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app_example/backend/src/test/java/com/brekelov/example/backend/BackendApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.brekelov.example.backend; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class BackendApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app_example/frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true -------------------------------------------------------------------------------- /app_example/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # binaries 2 | bin/ 3 | dist/ 4 | lib/ 5 | 6 | # editors 7 | *.swp 8 | .idea/ 9 | .vs/ 10 | .vscode/ 11 | .DS_Store -------------------------------------------------------------------------------- /app_example/frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12-alpine as build 2 | WORKDIR /app 3 | COPY package.json /app/package.json 4 | RUN npm install 5 | COPY . /app 6 | RUN npm run build 7 | 8 | FROM nginx:1.20.1-alpine 9 | COPY --from=build /app/build /usr/share/nginx/html 10 | EXPOSE 80 11 | CMD ["nginx", "-g", "daemon off;"] 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app_example/frontend/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `yarn start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `yarn test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `yarn build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `yarn eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `yarn build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /app_example/frontend/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | frontend: 4 | image: frontend:1.0 5 | ports: 6 | - "80:80" 7 | networks: 8 | - olo 9 | 10 | networks: 11 | olo: 12 | driver: bridge 13 | -------------------------------------------------------------------------------- /app_example/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "start-rtl", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.3.2", 8 | "@testing-library/user-event": "^7.1.2", 9 | "axios": "^0.19.2", 10 | "react": "^16.13.1", 11 | "react-dom": "^16.13.1", 12 | "react-router-dom": "^5.2.0", 13 | "react-scripts": "3.4.1" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": "react-app" 23 | }, 24 | "browserslist": { 25 | "production": [ 26 | ">0.2%", 27 | "not dead", 28 | "not op_mini all" 29 | ], 30 | "development": [ 31 | "last 1 chrome version", 32 | "last 1 firefox version", 33 | "last 1 safari version" 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app_example/frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/app_example/frontend/public/favicon.ico -------------------------------------------------------------------------------- /app_example/frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /app_example/frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/app_example/frontend/public/logo192.png -------------------------------------------------------------------------------- /app_example/frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/app_example/frontend/public/logo512.png -------------------------------------------------------------------------------- /app_example/frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /app_example/frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /app_example/frontend/src/App.css: -------------------------------------------------------------------------------- 1 | 2 | .App { 3 | min-height: 100vh; 4 | text-align: center; 5 | } 6 | .App-header { 7 | height: 10vh; 8 | display: flex; 9 | background-color: #282c34; 10 | flex-direction: column; 11 | align-items: center; 12 | justify-content: center; 13 | font-size: calc(10px + 2vmin); 14 | color: white; 15 | } 16 | .App-body { 17 | width: 60%; 18 | margin: 20px auto; 19 | } 20 | ul { 21 | padding: 0; 22 | display: flex; 23 | list-style-type: decimal; 24 | flex-direction: column; 25 | } 26 | li { 27 | font-size: large; 28 | text-align: left; 29 | padding: 0.5rem 0; 30 | } 31 | li a { 32 | text-transform: capitalize; 33 | text-decoration: none; 34 | } 35 | .todo-title { 36 | text-transform: capitalize; 37 | } 38 | .completed { 39 | color: green; 40 | } 41 | .not-completed { 42 | color: red; 43 | } 44 | -------------------------------------------------------------------------------- /app_example/frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { BrowserRouter, Route } from "react-router-dom"; 3 | 4 | import "./App.css"; 5 | import { TodoList } from "./TodoList"; 6 | import { TodoItem } from "./TodoItem"; 7 | 8 | function App() { 9 | return ( 10 |
11 |
12 |

Getting started with React testing library

13 |
14 | 15 |
16 | 17 | 18 | 19 | 20 |
21 |
22 | ); 23 | } 24 | 25 | export default App; 26 | -------------------------------------------------------------------------------- /app_example/frontend/src/AppContext.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const AppContext = React.createContext({}); 4 | 5 | export const AppProvider = ({ children }) => { 6 | const reducer = (state, action) => { 7 | switch (action.type) { 8 | case "LOAD_TODOLIST": 9 | return { ...state, todoList: action.todoList }; 10 | case "LOAD_SINGLE_TODO": 11 | return { ...state, activeToDoItem: action.todo }; 12 | default: 13 | return state; 14 | } 15 | }; 16 | 17 | const [appData, appDispatch] = React.useReducer(reducer, { 18 | todoList: [], 19 | activeToDoItem: { id: 0 }, 20 | }); 21 | 22 | return ( 23 | 24 | {children} 25 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /app_example/frontend/src/TodoItem.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import axios from "axios"; 3 | import { useParams } from "react-router-dom"; 4 | 5 | import "./App.css"; 6 | 7 | import { AppContext } from "./AppContext"; 8 | 9 | export const TodoItem = () => { 10 | const { id } = useParams(); 11 | 12 | const [loading, setLoading] = React.useState(true); 13 | 14 | const { 15 | appData: { activeToDoItem }, 16 | appDispatch, 17 | } = React.useContext(AppContext); 18 | 19 | const { title, completed, userId } = activeToDoItem; 20 | 21 | React.useEffect(() => { 22 | axios 23 | .get(`https://jsonplaceholder.typicode.com/todos/${id}`) 24 | .then((resp) => { 25 | const { data } = resp; 26 | appDispatch({ type: "LOAD_SINGLE_TODO", todo: data }); 27 | setLoading(false); 28 | }); 29 | }, [id, appDispatch]); 30 | 31 | return ( 32 |
33 | {loading ? ( 34 |

Fetching todo item {id}

35 | ) : ( 36 |
37 |

{title}

38 |

Added by: {userId}

39 | {completed ? ( 40 |

This item has been completed

41 | ) : ( 42 |

This item is yet to be completed

43 | )} 44 | 45 |
46 | )} 47 |
48 | ); 49 | }; 50 | -------------------------------------------------------------------------------- /app_example/frontend/src/TodoItem.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import axios from "axios"; 3 | 4 | import { render, screen, waitForElementToBeRemoved } from "./custom-render"; 5 | import { useParams, MemoryRouter } from "react-router-dom"; 6 | 7 | import { TodoItem } from "./TodoItem"; 8 | 9 | describe("", () => { 10 | it("can tell mocked from unmocked functions", () => { 11 | expect(jest.isMockFunction(useParams)).toBe(true); 12 | expect(jest.isMockFunction(MemoryRouter)).toBe(false); 13 | }); 14 | 15 | it("Renders correctly for a completed item", async () => { 16 | useParams.mockReturnValue({ id: 1 }); 17 | 18 | render(); 19 | 20 | await waitForElementToBeRemoved(() => 21 | screen.getByText(/Fetching todo item 1/i) 22 | ); 23 | expect(axios.get).toHaveBeenCalledTimes(1); 24 | expect(screen.getByText(/todo item 1/)).toBeInTheDocument(); 25 | expect(screen.getByText(/Added by: 1/)).toBeInTheDocument(); 26 | expect( 27 | screen.getByText(/This item has been completed/) 28 | ).toBeInTheDocument(); 29 | }); 30 | 31 | it("Renders correctly for an uncompleted item", async () => { 32 | useParams.mockReturnValue({ id: 2 }); 33 | 34 | render(); 35 | await waitForElementToBeRemoved(() => 36 | screen.getByText(/Fetching todo item 2/i) 37 | ); 38 | expect(axios.get).toHaveBeenCalledTimes(2); 39 | expect(screen.getByText(/todo item 2/)).toBeInTheDocument(); 40 | expect(screen.getByText(/Added by: 2/)).toBeInTheDocument(); 41 | expect( 42 | screen.getByText(/This item is yet to be completed/) 43 | ).toBeInTheDocument(); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /app_example/frontend/src/TodoList.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import axios from "axios"; 3 | import { Link } from "react-router-dom"; 4 | 5 | import "./App.css"; 6 | 7 | import { AppContext } from "./AppContext"; 8 | 9 | export const TodoList = () => { 10 | const [loading, setLoading] = React.useState(true); 11 | const { appData, appDispatch } = React.useContext(AppContext); 12 | 13 | React.useEffect(() => { 14 | axios.get("https://jsonplaceholder.typicode.com/todos").then((resp) => { 15 | const { data } = resp; 16 | appDispatch({ type: "LOAD_TODOLIST", todoList: data }); 17 | setLoading(false); 18 | }); 19 | }, [appDispatch, setLoading]); 20 | 21 | return ( 22 |
23 | {loading ? ( 24 |

Fetching todos

25 | ) : ( 26 |
    27 | {appData.todoList.slice(0, 15).map((item) => { 28 | const { id, title } = item; 29 | return ( 30 |
  • 31 | 32 | {title} 33 | 34 |
  • 35 | ); 36 | })} 37 |
38 | )} 39 |
40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /app_example/frontend/src/TodoList.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import axios from "axios"; 3 | 4 | import { render, screen, waitForElementToBeRemoved } from "./custom-render"; 5 | 6 | import { TodoList } from "./TodoList"; 7 | 8 | import { todos } from "./makeTodos"; 9 | 10 | describe("", () => { 11 | it("Renders component", async () => { 12 | render(); 13 | await waitForElementToBeRemoved(() => screen.getByText(/Fetching todos/i)); 14 | 15 | expect(axios.get).toHaveBeenCalledTimes(1); 16 | todos.slice(0, 15).forEach((td) => { 17 | expect(screen.getByText(td.title)).toBeInTheDocument(); 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /app_example/frontend/src/__mocks__/axios.js: -------------------------------------------------------------------------------- 1 | import { todos } from "../makeTodos"; 2 | 3 | export default { 4 | get: jest.fn().mockImplementation((url) => { 5 | switch (url) { 6 | case "https://jsonplaceholder.typicode.com/todos": 7 | return Promise.resolve({ data: todos }); 8 | case "https://jsonplaceholder.typicode.com/todos/1": 9 | return Promise.resolve({ 10 | data: { id: 1, title: "todo item 1", userId: 1, completed: true }, 11 | }); 12 | case "https://jsonplaceholder.typicode.com/todos/2": 13 | return Promise.resolve({ 14 | data: { id: 2, title: "todo item 2", userId: 2, completed: false }, 15 | }); 16 | 17 | default: 18 | throw new Error(`UNMATCHED URL: ${url}`); 19 | } 20 | }), 21 | }; 22 | -------------------------------------------------------------------------------- /app_example/frontend/src/__mocks__/react-router-dom.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ...jest.requireActual("react-router-dom"), 3 | useParams: jest.fn(), 4 | }; 5 | -------------------------------------------------------------------------------- /app_example/frontend/src/custom-render.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import { MemoryRouter } from "react-router-dom"; 4 | 5 | import { AppProvider } from "./AppContext"; 6 | 7 | const Wrapper = ({ children }) => { 8 | return ( 9 | 10 | {children} 11 | 12 | ); 13 | }; 14 | 15 | const customRender = (ui, options) => 16 | render(ui, { wrapper: Wrapper, ...options }); 17 | 18 | // re-export everything 19 | export * from "@testing-library/react"; 20 | 21 | // override render method 22 | export { customRender as render }; 23 | -------------------------------------------------------------------------------- /app_example/frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /app_example/frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | import { AppProvider } from "./AppContext"; 8 | 9 | ReactDOM.render( 10 | 11 | 12 | 13 | 14 | , 15 | document.getElementById("root") 16 | ); 17 | 18 | // If you want your app to work offline and load faster, you can change 19 | // unregister() to register() below. Note this comes with some pitfalls. 20 | // Learn more about service workers: https://bit.ly/CRA-PWA 21 | serviceWorker.unregister(); 22 | -------------------------------------------------------------------------------- /app_example/frontend/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app_example/frontend/src/makeTodos.js: -------------------------------------------------------------------------------- 1 | const makeTodos = (n) => { 2 | // returns n number of to-do items 3 | // default is 15 4 | 5 | const num = n || 15; 6 | const todos = []; 7 | for (let i = 0; i < num; i++) { 8 | todos.push({ 9 | id: i, 10 | userId: i, 11 | title: `Todo item ${i}`, 12 | completed: [true, false][Math.floor(Math.random() * 2)], 13 | }); 14 | } 15 | return todos; 16 | }; 17 | 18 | export const todos = makeTodos(200); 19 | -------------------------------------------------------------------------------- /app_example/frontend/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' }, 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready 134 | .then(registration => { 135 | registration.unregister(); 136 | }) 137 | .catch(error => { 138 | console.error(error.message); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /app_example/frontend/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lec_5_example/HELP.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ### Reference Documentation 4 | For further reference, please consider the following sections: 5 | 6 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 7 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/maven-plugin/reference/html/) 8 | * [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/maven-plugin/reference/html/#build-image) 9 | * [Testcontainers MongoDB Module Reference Guide](https://www.testcontainers.org/modules/databases/mongodb/) 10 | * [Rest Repositories](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/htmlsingle/#howto-use-exposing-spring-data-repositories-rest-endpoint) 11 | * [Testcontainers](https://www.testcontainers.org/) 12 | * [Spring Web](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/htmlsingle/#boot-features-developing-web-applications) 13 | * [Spring Data MongoDB](https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/htmlsingle/#boot-features-mongodb) 14 | 15 | ### Guides 16 | The following guides illustrate how to use some features concretely: 17 | 18 | * [Accessing JPA Data with REST](https://spring.io/guides/gs/accessing-data-rest/) 19 | * [Accessing Neo4j Data with REST](https://spring.io/guides/gs/accessing-neo4j-data-rest/) 20 | * [Accessing MongoDB Data with REST](https://spring.io/guides/gs/accessing-mongodb-data-rest/) 21 | * [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) 22 | * [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) 23 | * [Building REST services with Spring](https://spring.io/guides/tutorials/bookmarks/) 24 | * [Accessing Data with MongoDB](https://spring.io/guides/gs/accessing-data-mongodb/) 25 | 26 | -------------------------------------------------------------------------------- /lec_5_example/back.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /lec_5_example/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | # Provide a "standardized" way to retrieve the CLI args that will 300 | # work with both Windows and non-Windows executions. 301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 302 | export MAVEN_CMD_LINE_ARGS 303 | 304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 305 | 306 | exec "$JAVACMD" \ 307 | $MAVEN_OPTS \ 308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 311 | -------------------------------------------------------------------------------- /lec_5_example/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /lec_5_example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.3.4.RELEASE 9 | 10 | 11 | com.lec5 12 | back 13 | 0.0.1-SNAPSHOT 14 | back 15 | Lecture 5 16 | 17 | 18 | 1.8 19 | 1.14.3 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-data-mongodb 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-data-rest 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-web 34 | 35 | 36 | 37 | org.projectlombok 38 | lombok 39 | true 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-test 44 | test 45 | 46 | 47 | org.junit.vintage 48 | junit-vintage-engine 49 | 50 | 51 | 52 | 53 | org.springframework.restdocs 54 | spring-restdocs-mockmvc 55 | test 56 | 57 | 58 | org.testcontainers 59 | junit-jupiter 60 | test 61 | 62 | 63 | org.testcontainers 64 | mongodb 65 | test 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.testcontainers 73 | testcontainers-bom 74 | ${testcontainers.version} 75 | pom 76 | import 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | org.asciidoctor 85 | asciidoctor-maven-plugin 86 | 1.5.8 87 | 88 | 89 | generate-docs 90 | prepare-package 91 | 92 | process-asciidoc 93 | 94 | 95 | html 96 | book 97 | 98 | 99 | 100 | 101 | 102 | org.springframework.restdocs 103 | spring-restdocs-asciidoctor 104 | ${spring-restdocs.version} 105 | 106 | 107 | 108 | 109 | org.springframework.boot 110 | spring-boot-maven-plugin 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /lec_5_example/src/main/java/com/lec5/back/BackApplication.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class BackApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(BackApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /lec_5_example/src/main/java/com/lec5/back/EmptyNameException.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | public class EmptyNameException extends RuntimeException { 4 | public EmptyNameException() { 5 | super("Name is empty"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lec_5_example/src/main/java/com/lec5/back/GreetingController.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | import javax.websocket.server.PathParam; 8 | 9 | @RestController 10 | public class GreetingController { 11 | 12 | @Autowired 13 | GreetingService service; 14 | 15 | @GetMapping("/greeting") 16 | public String greeting(@PathParam("name") String name) { 17 | return service.getGreeting(name); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lec_5_example/src/main/java/com/lec5/back/GreetingService.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | @Service 6 | public class GreetingService { 7 | 8 | public String getGreeting(String name) { 9 | return "Hello, " + name; 10 | } 11 | 12 | public String checkForEmpty(String name) { 13 | if (name.equals("")) throw new EmptyNameException(); 14 | return name; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lec_5_example/src/main/java/com/lec5/back/HealthCheckController.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | public class HealthCheckController { 8 | 9 | @GetMapping("/healthcheck") 10 | public String healthcheck() { 11 | return "OK"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lec_5_example/src/main/java/com/lec5/back/Repository.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | import org.springframework.data.mongodb.repository.MongoRepository; 4 | 5 | public interface Repository extends MongoRepository { 6 | } 7 | -------------------------------------------------------------------------------- /lec_5_example/src/main/java/com/lec5/back/User.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | import lombok.Data; 4 | 5 | //@Getter 6 | //@Setter 7 | @Data 8 | public class User { 9 | String id; 10 | String name; 11 | 12 | public User(String id, String name) { 13 | this.id = id; 14 | this.name = name; 15 | } 16 | 17 | // public String getId() { 18 | // return id; 19 | // } 20 | // 21 | // public void setId(String id) { 22 | // this.id = id; 23 | // } 24 | // 25 | // public String getName() { 26 | // return name; 27 | // } 28 | // 29 | // public void setName(String name) { 30 | // this.name = name; 31 | // } 32 | } 33 | -------------------------------------------------------------------------------- /lec_5_example/src/main/java/com/lec5/back/User2Builder.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | public class User2Builder extends UserBuilder { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lec_5_example/src/main/java/com/lec5/back/UserBuilder.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | import lombok.Builder; 4 | 5 | @Builder 6 | public class UserBuilder { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /lec_5_example/src/main/java/com/lec5/back/UserNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | public class UserNotFoundException extends RuntimeException { 4 | public UserNotFoundException(String id) { 5 | super("Couldn't find user with id " + id); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lec_5_example/src/main/java/com/lec5/back/UsersController.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | import org.springframework.web.bind.annotation.*; 4 | 5 | import java.util.List; 6 | 7 | @RestController 8 | public class UsersController { 9 | 10 | private final Repository repository; 11 | 12 | public UsersController(Repository repository) { 13 | this.repository = repository; 14 | } 15 | 16 | @GetMapping("/users") 17 | List all() { 18 | return repository.findAll(); 19 | } 20 | 21 | @PostMapping("/users") 22 | User newEmployee(@RequestBody User newUser) { 23 | return repository.save(newUser); 24 | } 25 | 26 | @GetMapping("/users/{id}") 27 | User one(@PathVariable String id) { 28 | return repository.findById(id).orElseThrow(() -> new UserNotFoundException(id)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lec_5_example/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | spring.data.mongodb.host= -------------------------------------------------------------------------------- /lec_5_example/src/test/java/com/lec5/back/BackApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class BackApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /lec_5_example/src/test/java/com/lec5/back/GreetingControllerComponentTests.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 6 | import org.springframework.boot.test.mock.mockito.MockBean; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | import static org.mockito.BDDMockito.given; 10 | 11 | @WebMvcTest 12 | public class GreetingControllerComponentTests { 13 | 14 | @MockBean 15 | GreetingService service; 16 | 17 | @Autowired 18 | GreetingController controller; 19 | 20 | @Test 21 | public void testGreeting() { 22 | given(this.service.getGreeting("User")).willReturn("Hello, User"); 23 | 24 | String greeting = controller.greeting("User"); 25 | 26 | assertEquals("Hello, User", greeting); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lec_5_example/src/test/java/com/lec5/back/GreetingControllerTests.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.boot.web.client.RestTemplateBuilder; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.web.client.RestTemplate; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | 12 | @SpringBootTest 13 | public class GreetingControllerTests { 14 | 15 | @Autowired 16 | RestTemplateBuilder restTemplateBuilder; 17 | 18 | @Test 19 | public void testGreetingController(){ 20 | RestTemplate restTemplate = restTemplateBuilder.build(); 21 | ResponseEntity response = restTemplate.getForEntity("http://localhost:8080/greeting?name=Test", String.class); 22 | 23 | assertEquals("Hello, Test", response.getBody()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lec_5_example/src/test/java/com/lec5/back/GreetingServiceTests.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.junit.jupiter.params.ParameterizedTest; 5 | import org.junit.jupiter.params.provider.MethodSource; 6 | import org.junit.jupiter.params.provider.ValueSource; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | import static org.junit.jupiter.api.Assertions.assertThrows; 13 | 14 | public class GreetingServiceTests { 15 | 16 | @Test 17 | public void testGetGreeting() { 18 | //given 19 | GreetingService greetingService = new GreetingService(); 20 | 21 | //when 22 | String greeting = greetingService.getGreeting("Test"); 23 | 24 | //test 25 | assertEquals("Hello, Test", greeting); 26 | } 27 | 28 | @ParameterizedTest 29 | @ValueSource(strings = { "User", "", "?#$%?#$%?@#$?" }) 30 | public void testGreeting(String user) { 31 | //given 32 | GreetingService greetingService = new GreetingService(); 33 | 34 | //when 35 | String greeting = greetingService.getGreeting(user); 36 | 37 | //test 38 | assertEquals("Hello, " + user, greeting); 39 | } 40 | 41 | 42 | private static List getStreamWithData() { 43 | return Arrays.asList("Test"); 44 | } 45 | 46 | @ParameterizedTest 47 | @MethodSource("getStreamWithData") 48 | public void testGreeting1(String input) { 49 | GreetingService service = new GreetingService(); 50 | assertEquals(service.getGreeting(input), "Hello, " + input); 51 | } 52 | 53 | @Test 54 | void checkForEmpty() { 55 | //given 56 | GreetingService greetingService = new GreetingService(); 57 | 58 | //test 59 | assertThrows(EmptyNameException.class, () -> greetingService.checkForEmpty("")); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lec_5_example/src/test/java/com/lec5/back/UserControllerTests.java: -------------------------------------------------------------------------------- 1 | package com.lec5.back; 2 | 3 | import org.junit.jupiter.api.BeforeAll; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.boot.test.util.TestPropertyValues; 8 | import org.springframework.boot.web.client.RestTemplateBuilder; 9 | import org.springframework.boot.web.server.LocalServerPort; 10 | import org.springframework.context.ApplicationContextInitializer; 11 | import org.springframework.context.ConfigurableApplicationContext; 12 | import org.springframework.core.ParameterizedTypeReference; 13 | import org.springframework.core.env.MapPropertySource; 14 | import org.springframework.http.HttpMethod; 15 | import org.springframework.test.context.ContextConfiguration; 16 | import org.springframework.web.client.RestTemplate; 17 | import org.testcontainers.containers.MongoDBContainer; 18 | import org.testcontainers.junit.jupiter.Testcontainers; 19 | 20 | import java.util.Collections; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | import static org.junit.jupiter.api.Assertions.assertEquals; 26 | 27 | @Testcontainers 28 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 29 | @ContextConfiguration(initializers = UserControllerTests.Initializer.class) 30 | public class UserControllerTests { 31 | 32 | public static class Initializer implements ApplicationContextInitializer { 33 | 34 | @Override 35 | public void initialize(ConfigurableApplicationContext configurableApplicationContext) { 36 | MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:4.0.10").withExposedPorts(27017); 37 | 38 | mongoDBContainer.start(); 39 | 40 | Map map = new HashMap<>(); 41 | map.put("spring.data.mongodb.host", mongoDBContainer.getContainerIpAddress()); 42 | map.put("spring.data.mongodb.port", mongoDBContainer.getMappedPort(27017)); 43 | configurableApplicationContext.getEnvironment().getPropertySources().addLast( 44 | new MapPropertySource("TestConfigProperties", map) 45 | ); 46 | } 47 | } 48 | 49 | @Autowired 50 | RestTemplateBuilder restTemplateBuilder; 51 | 52 | @LocalServerPort 53 | int port; 54 | 55 | @Test 56 | public void testSaveUser() { 57 | RestTemplate restTemplate = restTemplateBuilder.build(); 58 | 59 | ParameterizedTypeReference> typeRef = new ParameterizedTypeReference>() { 60 | }; 61 | 62 | List users = restTemplate.exchange("http://localhost:"+port+"/users", HttpMethod.GET, null, typeRef).getBody(); 63 | assertEquals(Collections.emptyList(), users); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lec_5_example/target/classes/application.properties: -------------------------------------------------------------------------------- 1 | 2 | spring.data.mongodb.host= -------------------------------------------------------------------------------- /lec_5_example/target/classes/com/lec5/back/BackApplication.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/classes/com/lec5/back/BackApplication.class -------------------------------------------------------------------------------- /lec_5_example/target/classes/com/lec5/back/EmptyNameException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/classes/com/lec5/back/EmptyNameException.class -------------------------------------------------------------------------------- /lec_5_example/target/classes/com/lec5/back/GreetingController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/classes/com/lec5/back/GreetingController.class -------------------------------------------------------------------------------- /lec_5_example/target/classes/com/lec5/back/GreetingService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/classes/com/lec5/back/GreetingService.class -------------------------------------------------------------------------------- /lec_5_example/target/classes/com/lec5/back/HealthCheckController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/classes/com/lec5/back/HealthCheckController.class -------------------------------------------------------------------------------- /lec_5_example/target/classes/com/lec5/back/Repository.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/classes/com/lec5/back/Repository.class -------------------------------------------------------------------------------- /lec_5_example/target/classes/com/lec5/back/User.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/classes/com/lec5/back/User.class -------------------------------------------------------------------------------- /lec_5_example/target/classes/com/lec5/back/User2Builder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/classes/com/lec5/back/User2Builder.class -------------------------------------------------------------------------------- /lec_5_example/target/classes/com/lec5/back/UserBuilder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/classes/com/lec5/back/UserBuilder.class -------------------------------------------------------------------------------- /lec_5_example/target/classes/com/lec5/back/UserNotFoundException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/classes/com/lec5/back/UserNotFoundException.class -------------------------------------------------------------------------------- /lec_5_example/target/classes/com/lec5/back/UsersController.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/classes/com/lec5/back/UsersController.class -------------------------------------------------------------------------------- /lec_5_example/target/test-classes/com/lec5/back/BackApplicationTests.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/test-classes/com/lec5/back/BackApplicationTests.class -------------------------------------------------------------------------------- /lec_5_example/target/test-classes/com/lec5/back/GreetingControllerComponentTests.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/test-classes/com/lec5/back/GreetingControllerComponentTests.class -------------------------------------------------------------------------------- /lec_5_example/target/test-classes/com/lec5/back/GreetingControllerTests.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/test-classes/com/lec5/back/GreetingControllerTests.class -------------------------------------------------------------------------------- /lec_5_example/target/test-classes/com/lec5/back/GreetingServiceTests.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/test-classes/com/lec5/back/GreetingServiceTests.class -------------------------------------------------------------------------------- /lec_5_example/target/test-classes/com/lec5/back/UserControllerTests.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lec_5_example/target/test-classes/com/lec5/back/UserControllerTests.class -------------------------------------------------------------------------------- /lectures_v_2020/README.md: -------------------------------------------------------------------------------- 1 | ## Software testing lectures 2 | 3 | Меня зовут Всеволод Брекелов. 4 | 5 | Twitter: https://twitter.com/brekelov 6 | 7 | Я проводил лекции в ИТМО по прикладному тестированию. 8 | 9 | Все видео записи, домашние задания, дополнительные материалы можно найти здесь. 10 | 11 | ### Занятие 1. Основы теории. 12 | 13 | - [Методологии разработки. Статья. Habrahabr](https://habr.com/ru/company/edison/blog/269789/) 14 | - [Виды, типы тестирования, техники тест-дизайна. Статья.](https://dou.ua/forums/topic/13389/) 15 | - [Лекция 1](ST.Lecture1.pdf) 16 | - [Видео](https://youtu.be/nU9ByrAT2i0) 17 | 18 | --- 19 | Домашнее чтиво: 20 | - [https://martinfowler.com/articles/practical-test-pyramid.html](https://martinfowler.com/articles/practical-test-pyramid.html) 21 | - [https://martinfowler.com/bliki/IntegrationTest.html](https://martinfowler.com/bliki/IntegrationTest.html) 22 | - [https://martinfowler.com/bliki/TestDouble.html](https://martinfowler.com/bliki/TestDouble.html) 23 | 24 | Домашнее видео (не то, о котором вы подумали): 25 | - [https://www.youtube.com/watch?v=L2c8awaHIAg](https://www.youtube.com/watch?v=L2c8awaHIAg) (ссылка на его примеры https://github.com/qala-io/test-pyramid) 26 | - [https://www.youtube.com/watch?v=-q_9oPRAxFo](https://www.youtube.com/watch?v=-q_9oPRAxFo) (инструменты для тестировщика) 27 | 28 | --- 29 | Полезные ссылки: 30 | - [Блог Martin Fawler](https://martinfowler.com/testing/) 31 | - [Блог Максима Шульги с отличными рекомендациями по книгам и статьям](https://www.maxshulga.ru/p/useful-books-review.html) 32 | - [Блог от Google про тестирование](https://testing.googleblog.com/) 33 | 34 | Telegram-каналы: 35 | - [@js_for_testing](https://t.me/js_for_testing) 36 | - [@selenide_ru](https://t.me/selenide_ru) (Selenide библиотека) 37 | - [@qa_load](https://t.me/qa_load) (Чат перфоманс тестировщиков) 38 | - [@qa_ru](https://t.me/qa_ru) 39 | - [@qa_automation](https://t.me/qa_automation) 40 | - [@qa_fin](https://t.me/qa_fin) 41 | - [@heisenbugconf](https://t.me/heisenbugconf) (Чат конференции по тестированию Heisenbug) 42 | - [@sqaunderhood](https://t.me/sqaunderhood) 43 | - [@allure_ru](https://t.me/allure_ru) (Чат Allure репортинг фреймворка) 44 | - [@qajuniors](https://t.me/qajuniors) 45 | - [@serious_tester](https://t.me/serious_tester) 46 | - [@aerokube](https://t.me/aerokube) (Чат ребят из команды Aerokube, которые делают Selenoid и Moon) 47 | 48 | Youtube: 49 | - [https://www.youtube.com/c/Heisenbugconf/videos](https://www.youtube.com/c/Heisenbugconf/videos) (Отличные видео с конференции Heisenbug) 50 | 51 | ### Занятие 2. Тестирование Frontend. 52 | 53 | - [Лекция 2] TBD 54 | - [Пример] TBD 55 | - [Видео](https://youtu.be/AaPvh2-edaU) 56 | 57 | - [Как стартовать приложение на React и разобраться быстро с TypeScript](https://github.com/typescript-cheatsheets/react) 58 | - [Пошаговая инструкция для тестирования компонент на React c Jest и Enzyme](https://medium.com/devschacht/what-and-how-to-test-with-jest-and-enzyme-full-instruction-on-react-components-testing-d3504f3fbc54) 59 | - [React Jest workshop](https://github.com/kentcdodds/react-jest-workshop) 60 | - [Modern React testing](https://dev.to/sapegin/modern-react-testing-part-1-best-practices-1o93) 61 | - [Testing library for React docs](https://testing-library.com/) 62 | - [Jest test runner framework](https://jestjs.io/) 63 | - [Playwright node.js library for cross-browser testing](https://github.com/microsoft/playwright) 64 | - [Тестирование Angular приложений](https://medium.com/fafnur/%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D0%BE%D0%B2-%D0%B2-angular-%D1%81-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E-jest-%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D1%80%D0%B5%D0%B0%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9-%D0%B0%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%BD%D0%BE%D0%B9-%D0%BB%D0%BE%D0%B3%D0%B8%D0%BA%D0%B8-396ba5eca147) 65 | - [Unit тестирование Angular 5](https://habr.com/ru/post/349380/) 66 | - [Модульное тестирование Vue.js](https://ru.vuejs.org/v2/cookbook/unit-testing-vue-components.html) 67 | 68 | Youtube: 69 | - [Good video about frontend testing with Jasmine and Karma with samples](https://www.youtube.com/watch?v=zF1hlOxucHE) 70 | 71 | Notes: Looks like Angular is dying and that's why Protractor probably won't be used much in a long term. 72 | So use this video for understanding main principles and how you can handle jasmine, karma and mocking. 73 | 74 | --- 75 | Домашнее задание 76 | 77 | Main часть: 78 | - Создать репозиторий на GitHub, в котором создать папку `client` 79 | - В папке `client` создать приложение на React / Angular / Vue фреймворке на ваш выбор. У кого будет выбран самый редкий фреймворк -- получат дополнительные баллы. 80 | - Требования к приложению: должно содержать несколько страниц с роутингом, обязательно содержать сервис, который общается с node.js бекендом. 81 | - Присутствуют unit, component и e2e тесты в минимальном количестве 1шт каждый. 82 | 83 | Advanced часть: 84 | - Приложение содержит авторизацию и не собрано из генераторов вида JHipster. 85 | - Написаны тесты для проверки авторизации 86 | - Существует нескольно наборов тестов (несколько suites) 87 | 88 | Bonus часть: 89 | - Вы рассматриваете два любых фреймворка на выбор. Например: Mocha и Jest или Playwright и Cypress. 90 | - Пишете примеры с каждым из фреймовокров для своего приложения. 91 | - Короткая статья или заметки в чем именно для вас кажутся основные отличия. 92 | - Делаете в своем репозитории на GitHub активным GitHub pages и размещаете эту статью там. 93 | 94 | Вопросы для проверки знаний: 95 | - Зачем e2e тесты, если можно писать компонентные тесты? Чем отличается e2e тест от компонентных? 96 | - Терминология `describe` `it` `xit` мне понятна и я могу объяснить что это 97 | - Что такое test runner? 98 | - Тест зависит от запуска другого теста(ждет определенного состояния системы). Что может пойти не так? 99 | - `beforeTest()`,`afterTest()` зачем это? 100 | - Какие бывают `assertion frameworks`? 101 | - Почему рекомендуется писать немного e2e тестов? 102 | - Сколько проверок должно быть в одном юнит тесте? 103 | - Как называются тесты, которые то проходят, то не проходят? Почему так бывает и что с ними делать? 104 | 105 | 106 | ### Занятие 3. Тестирование Frontend: e2e тестирование. 107 | 108 | - [Лекция 3](https://miro.com/app/board/o9J_kkBAhy8=/) 109 | - [Пример] TBD 110 | - [Видео](https://youtu.be/jtu5DCmr3TA) 111 | 112 | - [Playwright](https://github.com/microsoft/playwright) 113 | - [Cypress](https://www.cypress.io/) 114 | - [Webdriver.io](https://webdriver.io/) 115 | - [e2e vs unit testing by Kent C. Dodds](https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests) 116 | 117 | --- 118 | Домашнее задание 119 | 120 | Main часть: 121 | - В первой части домашнего задания у вас уже есть приложение с юнит и компонентными тестами. Самое время дополнить его e2e тестами. 122 | Необходимо написать не менее 5 тестов на каждом из фреймворков: Cypress и Playwright. 123 | 124 | Advanced часть: 125 | - Разобраться как с помощью Cypress и Playwright делать авторизацию в вашем приложении, чтобы каждый тест не проходил форму логина(авторизации). 126 | 127 | Bonus часть: 128 | - Написать e2e тесты, которые делают скриншот тестирование и запись видео. Найти дефекты, которые будут обнаружены с помощью видео или скриншот теста, но при этом найти их с помощью юнит или компонентных тестов не тривиально. 129 | - По результатам написать статью в ваш блог(блог на github pages, который вы делали в 1 дз) 130 | 131 | ### Занятие 4. Тестирование Backend. Введение. 132 | 133 | - [Лекция 4](ST.Lecture4.pdf) 134 | - [Пример](https://spring.io/guides/tutorials/react-and-spring-data-rest/) 135 | - [Видео](https://youtu.be/z-Yh0QlJGP4) 136 | 137 | Ссылки HowToDo(гайды) 138 | 139 | - [React + Redux + Spring](https://habr.com/ru/company/alfa/blog/340776/) 140 | - [Пошагово UI на ReactJS + Backend на Java w/Sptring](https://spring.io/guides/tutorials/react-and-spring-data-rest/) 141 | - [Chrome DevTools panel](https://developers.google.com/web/tools/chrome-devtools/network) 142 | - [SoapUI test](https://www.soapui.org/getting-started/soap-test/) 143 | 144 | Ссылки для ознакомления 145 | 146 | - [Http протокол](https://developer.mozilla.org/ru/docs/Web/HTTP/Overview) 147 | - [Серия постов про REST API простыми словами](https://habr.com/ru/post/483202/) 148 | - [Postman and cUrl](https://www.taniarascia.com/making-api-requests-postman-curl/#:~:text=Postman%20is%20an%20API%20testing,to%20do%20the%20same%20tasks.) 149 | - [Chrome DevTools panel](https://developers.google.com/web/tools/chrome-devtools/network) 150 | - [Uber:Prototool for testing proto and not only for that](https://github.com/uber/prototool) 151 | 152 | ### Занятие 5. Тестирование Backend. Unit testing. Component testing. 153 | 154 | - [Лекция 5] 155 | - [Пример](lec_5_example) 156 | - [Видео](https://youtu.be/nSwc_jeeVNw) 157 | 158 | Ссылки 159 | - [Видео Кирилла Толкачева про тестирование с Spring Boot](https://www.youtube.com/watch?v=uc-cfX-5wQA) 160 | - [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide/) 161 | - [TestContainers](https://www.testcontainers.org/) 162 | - [Доклад Сергея Егорова про TestContainers](https://www.youtube.com/watch?v=GazYkGBamnE) 163 | - [Доклад Анатолия Коровина про тестирование микросервис и очередей с TestContainers](https://www.youtube.com/watch?v=UeQfaulJJDo&t=4s) 164 | - [Spring Testing Guide](https://spring.io/guides/gs/testing-web/) 165 | - [Доклад от Marc Philipp про JUnit 5](https://www.youtube.com/watch?v=751gMXH-lEE) 166 | 167 | --- 168 | Домашнее задание 169 | Main часть: 170 | Сделать сервис на Java+Spring+любая DB, который имеет как мин 1 Controller. 171 | Написать Unit и Component тесты для этого сервиса. Использовать TestContainers для одного теста с DB. Использовать Mockito для мокирования тестов с внешним сервисом. 172 | Написать документацию(README) какие тесты еще необходимо написать, но вы не успели. 173 | 174 | Advanced часть: 175 | Сделать взаимодействие сервиса и вашего Frontend приложения. 176 | Сделать тесты на авторизацию. 177 | Создать отдельные Spring Test Configruation, которые можно переключать с помощью флага при запуске тестов. 178 | Сделать генерацию тестовой документации через Asci Doctor(Spring Rest Docs). 179 | 180 | Bonus часть: 181 | 182 | Придумать функциональность, с которой можно использовать очереди/стримы вида RabbitMQ/Kafka streams. Написать компонентные тесты на эту функциональность(используя TestContainers). 183 | 184 | --- 185 | {#java #spring #springboot #springboottest #testcontainers #testng #junit5} 186 | 187 | Вопросы для проверки знаний: 188 | - Зачем нужен отдельный Config для тестов? 189 | - Как можно создать отдельный Suite для запуска разных тестов? 190 | - Можно ли в тесте создавать образ базы данных и работать с ней(не подымая саму базу данных для теста отдельно в окружении)? 191 | - Как сделать параметризованный тест с JUnit? 192 | - Есть ли hamcrest в JUnit 5? 193 | - Почему JUnit 5 лучше TestNG? (или нет? =) 194 | 195 | ### Занятие 6. CI/CD. GitHub actions. Azure. 196 | 197 | - [Лекция 6] 198 | - [Пример] 199 | - [Видео](https://youtu.be/nfVba1sglLs) 200 | 201 | Ссылки 202 | - [Видео Илья Климов про CI/CD](https://www.youtube.com/watch?v=CwU-OiS_PEQ) 203 | - [Что такое CI/CD](https://selectel.ru/blog/what-is-ci-cd/) 204 | - [GitHub Actions](https://github.com/features/actions) 205 | - [Azure GitHub action](https://github.com/Azure/actions) 206 | - [Spring Boot app to Azure](https://spring.io/guides/gs/spring-boot-for-azure/) 207 | - [CI/CD с GitHub actions статья](https://habr.com/ru/post/476368/) 208 | - [Spring example with CD to Azure](https://github.com/Azure-Samples/java-spring-petclinic) 209 | - [Repo with GitHub actions step by step](https://github.com/qagid/github-actions) 210 | - [Jenkins + Docker set up pipeline](https://www.youtube.com/watch?v=J0d-CbJKAwU) 211 | - [React app GitHub actions example](https://dev.to/dyarleniber/setting-up-a-ci-cd-workflow-on-github-actions-for-a-react-app-with-github-pages-and-codecov-4hnp) 212 | - [React app GitHub actions to AWS example](https://github.com/aws-samples/aws-starter-react-for-github-actions) 213 | - [LazyDocker to manage docker images and containers](https://github.com/jesseduffield/lazydocker) 214 | 215 | --- 216 | Домашнее задание 217 | Main часть: 218 | 219 | Добавить GitHub action для запуска тестов на UI и Backend по пушу из в master ветку. 220 | 221 | Advanced часть: 222 | 223 | Добавить GitHub action для деплоя приложения UI+BE на Azure/Vercel/Яндекс Облако. 224 | 225 | Bonus часть: 226 | 227 | Использовать Kubernetes в Azure/Яндекс Облаке для разворачивания среды. 228 | 229 | --- 230 | {#githubActions #cicd #azure #cloud} 231 | 232 | Вопросы для проверки знаний: 233 | - Что такое CI/CD? 234 | - Можно ли без Docker задеплоить приложение? 235 | - Какие основные шаги прописываются в CI пайплайне для UI/BE? 236 | - Можно ли настроить деплоймент на одно и тоже окружение из разных веток? 237 | - Зачем нужно деплоить по комиту/пушу в мастер? 238 | 239 | ### Занятие 7. Reporting. BDD. 240 | 241 | - [Лекция 7](ST.Lecture7.pdf) 242 | - [Пример] 243 | - [Видео](https://youtu.be/agEwIr05TRk) 244 | 245 | Ссылки 246 | - [Allure reporting](http://allure.qatools.ru/) 247 | - [Allure doc](https://docs.qameta.io/allure/) 248 | - [Allure examples](https://github.com/allure-examples) 249 | - [BDD.Gherkin syntax](https://cucumber.io/docs/gherkin/) 250 | - [Akita BDD framework used in Alpha-lab](https://github.com/alfa-laboratory/akita) 251 | 252 | --- 253 | Домашнее задание 254 | 255 | Bonus часть: 256 | 257 | Использовать Allure reporting для написанных тестов. 258 | 259 | --- 260 | {#reporting #bdd #allure} 261 | 262 | ### Занятие 8. Contract tests. Pact. Spring cloud contract. 263 | 264 | - [Лекция 8] 265 | - [Пример] 266 | - [Видео](https://youtu.be/QAmlyBdXjeY) 267 | 268 | Ссылки 269 | - [Pact workshop step by step](https://github.com/pact-foundation/pact-workshop-js) 270 | - [Spring cloud contract examples](https://github.com/spring-cloud-samples/spring-cloud-contract-samples) 271 | - [Spring cloud doc](https://cloud.spring.io/spring-cloud-contract/reference/html/using.html) 272 | - [Pact broker](https://docs.pact.io/getting_started/sharing_pacts/) 273 | - [Protocol buffers](https://developers.google.com/protocol-buffers) 274 | 275 | --- 276 | Домашнее задание 277 | 278 | Bonus часть: 279 | 280 | Написать Pact-тесты к своему сервису. 281 | 282 | --- 283 | {#contract-tests #pact #swagger} 284 | 285 | ### Занятие 9. A11Y. Instruments. 286 | 287 | - [Лекция 9](ST.Lecture9.pdf) 288 | - [Пример] 289 | - [Видео](https://youtu.be/lzeWAL6rE_o) 290 | 291 | Ссылки 292 | 293 | - [Rus | Пошаговое руководство для тестировщиков по проверке доступности сайта](https://adequatica.medium.com/accessibility-testing-d10e9f613c4d) 294 | - [WCAG doc](https://www.w3.org/WAI/WCAG21/Understanding/) 295 | - [Rus | Веблайнд. Рекомендации](https://weblind.ru/inner.html#image-content) 296 | - [Examples of accessibility patterns](https://www.accessibility-developer-guide.com/examples/) 297 | - [Chrome Dev-tools how-to](https://www.smashingmagazine.com/2020/08/accessibility-chrome-devtools/) 298 | - [Accessibility dev guide](https://www.accessibility-developer-guide.com/) 299 | - [Udacity course about accessibility](https://www.udacity.com/course/web-accessibility--ud891) 300 | 301 | --- 302 | Домашнее задание 303 | 304 | Bonus часть: 305 | 306 | Протестировать свой веб-сайт на а11y с помощью инструментов от Mozilla и Lighthouse. Пофиксить проблемы. Сделать автоматический тест с axe. 307 | 308 | --- 309 | {#a11y} 310 | 311 | 312 | ### Занятие 10. Selenium. Selenide. Selenoid. 313 | 314 | - [Лекция 10](ST.Lecture10.pdf) 315 | - [Пример](https://github.com/selenide-examples) 316 | - [Видео](https://youtu.be/G3HHvP_bUas) 317 | 318 | Ссылки 319 | - [Selenide + Selenoid configuration](https://habr.com/ru/post/473454/) 320 | - [Selenium WebDriver step-by-step. PageObject](https://habr.com/ru/post/502292/) 321 | - [Selenium Grid story](https://habr.com/ru/company/avito/blog/352208/) 322 | - [Selenoid how-to install](https://medium.com/@eugenetruuts/how-to-install-selenoid-on-macos-751ee2955c70) 323 | 324 | --- 325 | Домашнее задание 326 | 327 | Main часть: 328 | - Написать e2e тесты, используя Selenide. 329 | 330 | Advanced часть: 331 | - Настроить запуск тестов с Selenoid. 332 | 333 | Bonus часть: 334 | - Настроить генерацию отчетов с Allure report, сделав полный сетап в GitHub: e2e тесты с selenide запускаются с 335 | использованием Selenoid на разных окружениях(браузерах) параллельно и собирают отчет с помощью Allure Report. 336 | - Написать статью как собрать такой сетап. 337 | 338 | --- 339 | {#selenium #selenide #selenoid #e2e-tests #parallel-run} 340 | 341 | 342 | ### Занятие 11. Performance testing. 343 | 344 | - [Лекция 11](ST.Lecture11.pdf) 345 | - [Пример](https://gitlab.com/tinkoffperfworkshop) 346 | - [Видео](https://youtu.be/SEFqaGvcK-0) 347 | 348 | Ссылки 349 | - [Анализ ключевых показателей производительности](https://habr.com/ru/company/microsoft/blog/271547/) 350 | - [ISTQB термины](https://www.rstqb.org/ru/istqb-downloads.html?file=files/content/rstqb/downloads/ISTQB%20Downloads/ISTQB_CTFL_PT_Syllabus_2018_Russian.pdf) 351 | - [9 этапов тестирования](https://www.a1qa.ru/blog/9-etapov-testirovaniya-proizvoditelnosti/) 352 | - [Доклад Андрея Акиньшина](https://www.youtube.com/watch?v=jZ0quqA1Fn8) 353 | - [Доклад Владимира Ситникова про подвоные камни в нагрузочном тестировании](https://www.youtube.com/watch?v=3PWBBc7rZxw) 354 | - [Доклад Алексея Лавренюка про Pandora](https://www.youtube.com/watch?v=lkusMkIniq0) 355 | 356 | --- 357 | Домашнее задание 358 | 359 | Main часть: 360 | - Пройти воркшоп и выложить результат в гитхаб репозиторий. 361 | 362 | Advanced часть: 363 | - Настроить CI с Github actions. 364 | 365 | Bonus часть: 366 | - Настроить генерацию отчетов с Allure report. 367 | 368 | Notes: отдельное спасибо Сергею Чепкасову и Максиму Рогожникову за отличный воркшоп. 369 | 370 | --- 371 | {#performance #load #gatling #jmeter} 372 | 373 | 374 | ### Занятие 12. Best practices. Итоговая лекция. 375 | 376 | - [Лекция 11](ST.Lecture12.pdf) 377 | - [Видео](https://youtu.be/HcOyZTC77lc) 378 | -------------------------------------------------------------------------------- /lectures_v_2020/ST.Lecture1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2020/ST.Lecture1.pdf -------------------------------------------------------------------------------- /lectures_v_2020/ST.Lecture10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2020/ST.Lecture10.pdf -------------------------------------------------------------------------------- /lectures_v_2020/ST.Lecture11.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2020/ST.Lecture11.pdf -------------------------------------------------------------------------------- /lectures_v_2020/ST.Lecture12.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2020/ST.Lecture12.pdf -------------------------------------------------------------------------------- /lectures_v_2020/ST.Lecture4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2020/ST.Lecture4.pdf -------------------------------------------------------------------------------- /lectures_v_2020/ST.Lecture7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2020/ST.Lecture7.pdf -------------------------------------------------------------------------------- /lectures_v_2020/ST.Lecture9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2020/ST.Lecture9.pdf -------------------------------------------------------------------------------- /lectures_v_2021/README.MD: -------------------------------------------------------------------------------- 1 | # Software testing lectures 2 | 3 | Меня зовут Всеволод Брекелов. 4 | Twitter: https://twitter.com/brekelov 5 | 6 | ### Занятие 1. Основы теории. 7 | 8 | - [Методологии разработки. Статья. Habrahabr](https://habr.com/ru/company/edison/blog/269789/) 9 | - [Виды, типы тестирования, техники тест-дизайна. Статья.](https://dou.ua/forums/topic/13389/) 10 | - [Лекция 1](ST.Lecture1.pdf) 11 | - [Видео](https://youtu.be/elDiFlxI234) 12 | 13 | --- 14 | Домашнее чтиво: 15 | - [https://martinfowler.com/articles/practical-test-pyramid.html](https://martinfowler.com/articles/practical-test-pyramid.html) 16 | - [https://martinfowler.com/bliki/IntegrationTest.html](https://martinfowler.com/bliki/IntegrationTest.html) 17 | - [https://martinfowler.com/bliki/TestDouble.html](https://martinfowler.com/bliki/TestDouble.html) 18 | 19 | Домашнее видео (не то, о котором вы подумали): 20 | - [https://www.youtube.com/watch?v=L2c8awaHIAg](https://www.youtube.com/watch?v=L2c8awaHIAg) (ссылка на его примеры https://github.com/qala-io/test-pyramid) 21 | - [https://www.youtube.com/watch?v=-q_9oPRAxFo](https://www.youtube.com/watch?v=-q_9oPRAxFo) (инструменты для тестировщика) 22 | 23 | --- 24 | Полезные ссылки: 25 | - [Блог Martin Fawler](https://martinfowler.com/testing/) 26 | - [Блог Максима Шульги с отличными рекомендациями по книгам и статьям](https://www.maxshulga.ru/p/useful-books-review.html) 27 | - [Блог от Google про тестирование](https://testing.googleblog.com/) 28 | 29 | Telegram-каналы: 30 | - [@js_for_testing](https://t.me/js_for_testing) 31 | - [@selenide_ru](https://t.me/selenide_ru) (Selenide библиотека) 32 | - [@qa_load](https://t.me/qa_load) (Чат перфоманс тестировщиков) 33 | - [@qa_ru](https://t.me/qa_ru) 34 | - [@qa_automation](https://t.me/qa_automation) 35 | - [@qa_fin](https://t.me/qa_fin) 36 | - [@heisenbugconf](https://t.me/heisenbugconf) (Чат конференции по тестированию Heisenbug) 37 | - [@sqaunderhood](https://t.me/sqaunderhood) 38 | - [@allure_ru](https://t.me/allure_ru) (Чат Allure репортинг фреймворка) 39 | - [@qajuniors](https://t.me/qajuniors) 40 | - [@serious_tester](https://t.me/serious_tester) 41 | - [@aerokube](https://t.me/aerokube) (Чат ребят из команды Aerokube, которые делают Selenoid и Moon) 42 | 43 | Youtube: 44 | - [https://www.youtube.com/c/Heisenbugconf/videos](https://www.youtube.com/c/Heisenbugconf/videos) (Отличные видео с конференции Heisenbug) 45 | 46 | ### Занятие 2. Тестирование Frontend. Часть 1. 47 | 48 | - [Лекция 2] TBD 49 | - [Пример 1](https://github.com/Mervap/SoftwareTesting) Приложение с фронтендом, тестами и бекендом. 50 | - [Пример 2](https://github.com/PolinB/software-testing) 51 | - [Видео](https://youtu.be/xDiSgXWff8Y) 52 | 53 | - [Как стартовать приложение на React и разобраться быстро с TypeScript](https://github.com/typescript-cheatsheets/react) 54 | - [Пошаговая инструкция для тестирования компонент на React c Jest и Enzyme](https://medium.com/devschacht/what-and-how-to-test-with-jest-and-enzyme-full-instruction-on-react-components-testing-d3504f3fbc54) 55 | - [React Jest workshop](https://github.com/kentcdodds/react-jest-workshop) 56 | - [Modern React testing](https://dev.to/sapegin/modern-react-testing-part-1-best-practices-1o93) 57 | - [Testing library for React docs](https://testing-library.com/) 58 | - [Jest test runner framework](https://jestjs.io/) 59 | - [Playwright node.js library for cross-browser testing](https://github.com/microsoft/playwright) 60 | - [Тестирование Angular приложений](https://medium.com/fafnur/%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D0%BE%D0%B2-%D0%B2-angular-%D1%81-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E-jest-%D1%82%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D1%80%D0%B5%D0%B0%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D0%BE%D0%B9-%D0%B0%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%BD%D0%BE%D0%B9-%D0%BB%D0%BE%D0%B3%D0%B8%D0%BA%D0%B8-396ba5eca147) 61 | - [Unit тестирование Angular 5](https://habr.com/ru/post/349380/) 62 | - [Модульное тестирование Vue.js](https://ru.vuejs.org/v2/cookbook/unit-testing-vue-components.html) 63 | 64 | Youtube: 65 | - [Good video about frontend testing with Jasmine and Karma with samples](https://www.youtube.com/watch?v=zF1hlOxucHE) 66 | 67 | Notes: Looks like Angular is dying and that's why Protractor probably won't be used much in a long term. 68 | So use this video for understanding main principles and how you can handle jasmine, karma and mocking. 69 | 70 | --- 71 | Домашнее задание 72 | 73 | Main часть: 74 | - Создать репозиторий на GitHub, в котором создать папку `client` 75 | - В папке `client` создать приложение на React / Angular / Vue фреймворке на ваш выбор. У кого будет выбран самый редкий фреймворк -- получат дополнительные баллы. 76 | - Требования к приложению: должно содержать несколько страниц с роутингом, обязательно содержать сервис, который общается с node.js бекендом. 77 | - Присутствуют unit, component и e2e тесты в минимальном количестве 1шт каждый. 78 | 79 | Advanced часть: 80 | - Приложение содержит авторизацию и не собрано из генераторов вида JHipster. 81 | - Написаны тесты для проверки авторизации 82 | - Существует нескольно наборов тестов (несколько suites) 83 | 84 | Bonus часть: 85 | - Вы рассматриваете два любых фреймворка на выбор. Например: Mocha и Jest или Playwright и Cypress. 86 | - Пишете примеры с каждым из фреймовокров для своего приложения. 87 | - Короткая статья или заметки в чем именно для вас кажутся основные отличия. 88 | - Делаете в своем репозитории на GitHub активным GitHub pages и размещаете эту статью там. 89 | 90 | Вопросы для проверки знаний: 91 | - Зачем e2e тесты, если можно писать компонентные тесты? Чем отличается e2e тест от компонентных? 92 | - Терминология `describe` `it` `xit` мне понятна и я могу объяснить что это 93 | - Что такое test runner? 94 | - Тест зависит от запуска другого теста(ждет определенного состояния системы). Что может пойти не так? 95 | - `beforeTest()`,`afterTest()` зачем это? 96 | - Какие бывают `assertion frameworks`? 97 | - Почему рекомендуется писать немного e2e тестов? 98 | - Сколько проверок должно быть в одном юнит тесте? 99 | - Как называются тесты, которые то проходят, то не проходят? Почему так бывает и что с ними делать? 100 | 101 | ### Занятие 3. Тестирование Backend. Введение. 102 | 103 | - [Лекция 3](ST.Lecture3.pdf) 104 | - [Пример](https://spring.io/guides/tutorials/react-and-spring-data-rest/) 105 | - [Kotlin + ReactJS example](https://github.com/oktadev/okta-kotlin-react-crud-example) 106 | - [Видео](https://youtu.be/jE2VzZARoPA) 107 | 108 | Ссылки HowToDo(гайды) 109 | 110 | - [React + Redux + Spring](https://habr.com/ru/company/alfa/blog/340776/) 111 | - [Пошагово UI на ReactJS + Backend на Java w/Sptring](https://spring.io/guides/tutorials/react-and-spring-data-rest/) 112 | - [Chrome DevTools panel](https://developers.google.com/web/tools/chrome-devtools/network) 113 | - [SoapUI test](https://www.soapui.org/getting-started/soap-test/) 114 | 115 | Ссылки для ознакомления 116 | 117 | - [Http протокол](https://developer.mozilla.org/ru/docs/Web/HTTP/Overview) 118 | - [Серия постов про REST API простыми словами](https://habr.com/ru/post/483202/) 119 | - [Postman and cUrl](https://www.taniarascia.com/making-api-requests-postman-curl/#:~:text=Postman%20is%20an%20API%20testing,to%20do%20the%20same%20tasks.) 120 | - [Chrome DevTools panel](https://developers.google.com/web/tools/chrome-devtools/network) 121 | - [Uber:Prototool for testing proto and not only for that](https://github.com/uber/prototool) 122 | 123 | ### Занятие 4. Тестирование Backend. Unit testing. Component testing. 124 | 125 | - [Лекция 4] 126 | - [Пример](../lec_5_example) 127 | - [Видео](https://youtu.be/nSwc_jeeVNw) 128 | 129 | Ссылки 130 | - [Видео Кирилла Толкачева про тестирование с Spring Boot](https://www.youtube.com/watch?v=uc-cfX-5wQA) 131 | - [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide/) 132 | - [TestContainers](https://www.testcontainers.org/) 133 | - [Доклад Сергея Егорова про TestContainers](https://www.youtube.com/watch?v=GazYkGBamnE) 134 | - [Доклад Анатолия Коровина про тестирование микросервис и очередей с TestContainers](https://www.youtube.com/watch?v=UeQfaulJJDo&t=4s) 135 | - [Spring Testing Guide](https://spring.io/guides/gs/testing-web/) 136 | - [Доклад от Marc Philipp про JUnit 5](https://www.youtube.com/watch?v=751gMXH-lEE) 137 | 138 | --- 139 | Домашнее задание 140 | Main часть: 141 | Сделать сервис на Java+Spring+любая DB (или NodeJS + DI tool), который имеет как мин 1 Controller. 142 | Написать Unit и Component тесты для этого сервиса. 143 | Использовать TestContainers для одного теста с DB. 144 | Использовать Mockito для мокирования тестов с внешним сервисом. 145 | Написать документацию(README) какие тесты еще необходимо написать, но вы не успели. 146 | 147 | Advanced часть: 148 | Сделать взаимодействие сервиса и вашего Frontend приложения. 149 | Сделать тесты на авторизацию. 150 | Создать отдельные Spring Test Configruation, которые можно переключать с помощью флага при запуске тестов. 151 | Сделать генерацию тестовой документации через Asci Doctor(Spring Rest Docs). 152 | 153 | Bonus часть: 154 | 155 | Придумать функциональность, с которой можно использовать очереди/стримы вида RabbitMQ/Kafka streams. 156 | Написать компонентные тесты на эту функциональность(используя TestContainers). 157 | 158 | --- 159 | {#java #spring #springboot #springboottest #testcontainers #testng #junit5} 160 | 161 | Вопросы для проверки знаний: 162 | - Зачем нужен отдельный Config для тестов? 163 | - Как можно создать отдельный Suite для запуска разных тестов? 164 | - Можно ли в тесте создавать образ базы данных и работать с ней(не подымая саму базу данных для теста отдельно в окружении)? 165 | - Как сделать параметризованный тест с JUnit? 166 | - Есть ли hamcrest в JUnit 5? 167 | - Почему JUnit 5 лучше TestNG? (или нет? =) 168 | 169 | ### Занятие 5. CI/CD. GitHub actions. 170 | 171 | - [Лекция 5] 172 | - [Пример] 173 | - [Видео](https://youtu.be/PHJOMKU5hqc) 174 | 175 | Ссылки 176 | - [Видео Илья Климов про CI/CD](https://www.youtube.com/watch?v=CwU-OiS_PEQ) 177 | - [Что такое CI/CD](https://selectel.ru/blog/what-is-ci-cd/) 178 | - [GitHub Actions](https://github.com/features/actions) 179 | - [Azure GitHub action](https://github.com/Azure/actions) 180 | - [Spring Boot app to Azure](https://spring.io/guides/gs/spring-boot-for-azure/) 181 | - [CI/CD с GitHub actions статья](https://habr.com/ru/post/476368/) 182 | - [Spring example with CD to Azure](https://github.com/Azure-Samples/java-spring-petclinic) 183 | - [Repo with GitHub actions step by step](https://github.com/qagid/github-actions) 184 | - [Jenkins + Docker set up pipeline](https://www.youtube.com/watch?v=J0d-CbJKAwU) 185 | - [React app GitHub actions example](https://dev.to/dyarleniber/setting-up-a-ci-cd-workflow-on-github-actions-for-a-react-app-with-github-pages-and-codecov-4hnp) 186 | - [React app GitHub actions to AWS example](https://github.com/aws-samples/aws-starter-react-for-github-actions) 187 | - [LazyDocker to manage docker images and containers](https://github.com/jesseduffield/lazydocker) 188 | 189 | --- 190 | Домашнее задание 191 | Main часть: 192 | 193 | Добавить GitHub action для запуска тестов на UI и Backend по пушу из в master ветку. 194 | 195 | Advanced часть: 196 | 197 | Добавить GitHub action для деплоя приложения UI+BE на Azure/Vercel/Яндекс Облако. 198 | 199 | Bonus часть: 200 | 201 | Использовать Kubernetes в Azure/Яндекс Облаке для разворачивания среды. 202 | 203 | --- 204 | {#githubActions #cicd #azure #cloud} 205 | 206 | Вопросы для проверки знаний: 207 | - Что такое CI/CD? 208 | - Можно ли без Docker задеплоить приложение? 209 | - Какие основные шаги прописываются в CI пайплайне для UI/BE? 210 | - Можно ли настроить деплоймент на одно и тоже окружение из разных веток? 211 | - Зачем нужно деплоить по комиту/пушу в мастер? 212 | 213 | ## Занятие 6. Reporting. BDD. 214 | 215 | - [Лекция 7](ST.Lecture6.pdf) 216 | - [Пример](https://github.com/allure-examples) 217 | - [Видео](https://youtu.be/FkbMTEdSUxI) 218 | 219 | Ссылки 220 | - [Allure reporting](http://allure.qatools.ru/) 221 | - [Allure doc](https://docs.qameta.io/allure/) 222 | - [Allure examples](https://github.com/allure-examples) 223 | - [BDD.Gherkin syntax](https://cucumber.io/docs/gherkin/) 224 | - [Akita BDD framework used in Alpha-lab](https://github.com/alfa-laboratory/akita) 225 | 226 | --- 227 | Домашнее задание 228 | 229 | Bonus часть: 230 | 231 | Использовать Allure reporting для написанных тестов. 232 | 233 | --- 234 | {#reporting #bdd #allure} 235 | 236 | ### Занятие 7. Contract tests. Pact. Spring cloud contract. 237 | 238 | - [Лекция 7] 239 | - [Пример] 240 | - [Видео](https://youtu.be/QAmlyBdXjeY) 241 | 242 | Ссылки 243 | - [Pact workshop step by step](https://github.com/pact-foundation/pact-workshop-js) 244 | - [Spring cloud contract examples](https://github.com/spring-cloud-samples/spring-cloud-contract-samples) 245 | - [Spring cloud doc](https://cloud.spring.io/spring-cloud-contract/reference/html/using.html) 246 | - [Pact broker](https://docs.pact.io/getting_started/sharing_pacts/) 247 | - [Protocol buffers](https://developers.google.com/protocol-buffers) 248 | 249 | --- 250 | Домашнее задание 251 | 252 | Bonus часть: 253 | 254 | Написать Pact-тесты к своему сервису. 255 | 256 | --- 257 | {#contract-tests #pact #swagger} 258 | 259 | ### Занятие 8. A11Y. Instruments. 260 | 261 | - [Лекция 8](ST.Lecture8.pdf) 262 | - [Пример] 263 | - [Видео](https://youtu.be/lzeWAL6rE_o) 264 | 265 | Ссылки 266 | 267 | - [Rus | Пошаговое руководство для тестировщиков по проверке доступности сайта](https://adequatica.medium.com/accessibility-testing-d10e9f613c4d) 268 | - [WCAG doc](https://www.w3.org/WAI/WCAG21/Understanding/) 269 | - [Rus | Веблайнд. Рекомендации](https://weblind.ru/inner.html#image-content) 270 | - [Examples of accessibility patterns](https://www.accessibility-developer-guide.com/examples/) 271 | - [Chrome Dev-tools how-to](https://www.smashingmagazine.com/2020/08/accessibility-chrome-devtools/) 272 | - [Accessibility dev guide](https://www.accessibility-developer-guide.com/) 273 | - [Udacity course about accessibility](https://www.udacity.com/course/web-accessibility--ud891) 274 | 275 | --- 276 | Домашнее задание 277 | 278 | Bonus часть: 279 | 280 | Протестировать свой веб-сайт на а11y с помощью инструментов от Mozilla и Lighthouse. Пофиксить проблемы. Сделать автоматический тест с axe. 281 | 282 | --- 283 | {#a11y} 284 | 285 | 286 | ### Занятие 9. Selenium. Selenide. Selenoid. 287 | 288 | - [Лекция 9](ST.Lecture9.pdf) 289 | - [Пример](https://github.com/selenide-examples) 290 | - [Видео](https://youtu.be/UnZMRrHJQnw) 291 | 292 | Ссылки 293 | - [Selenide + Selenoid configuration](https://habr.com/ru/post/473454/) 294 | - [Selenium WebDriver step-by-step. PageObject](https://habr.com/ru/post/502292/) 295 | - [Selenium Grid story](https://habr.com/ru/company/avito/blog/352208/) 296 | - [Selenoid how-to install](https://medium.com/@eugenetruuts/how-to-install-selenoid-on-macos-751ee2955c70) 297 | 298 | --- 299 | Домашнее задание 300 | 301 | Main часть: 302 | - Написать e2e тесты, используя Selenide. 303 | 304 | Advanced часть: 305 | - Настроить запуск тестов с Selenoid. 306 | 307 | Bonus часть: 308 | - Настроить генерацию отчетов с Allure report, сделав полный сетап в GitHub: e2e тесты с selenide запускаются с 309 | использованием Selenoid на разных окружениях(браузерах) параллельно и собирают отчет с помощью Allure Report. 310 | - Написать статью как собрать такой сетап. 311 | 312 | --- 313 | {#selenium #selenide #selenoid #e2e-tests #parallel-run} 314 | 315 | ### Занятие 10. Performance testing. 316 | 317 | - [Лекция 10](ST.Lecture10.pdf) 318 | - [Пример](https://gitlab.com/tinkoffperfworkshop) 319 | - [Видео](https://youtu.be/dhKra9BQkM0) 320 | 321 | Ссылки 322 | - [Анализ ключевых показателей производительности](https://habr.com/ru/company/microsoft/blog/271547/) 323 | - [ISTQB термины](https://www.rstqb.org/ru/istqb-downloads.html?file=files/content/rstqb/downloads/ISTQB%20Downloads/ISTQB_CTFL_PT_Syllabus_2018_Russian.pdf) 324 | - [9 этапов тестирования](https://www.a1qa.ru/blog/9-etapov-testirovaniya-proizvoditelnosti/) 325 | - [Доклад Андрея Акиньшина](https://www.youtube.com/watch?v=jZ0quqA1Fn8) 326 | - [Доклад Владимира Ситникова про подвоные камни в нагрузочном тестировании](https://www.youtube.com/watch?v=3PWBBc7rZxw) 327 | - [Доклад Алексея Лавренюка про Pandora](https://www.youtube.com/watch?v=lkusMkIniq0) 328 | 329 | --- 330 | Домашнее задание 331 | 332 | Main часть: 333 | - Пройти воркшоп и выложить результат в гитхаб репозиторий. 334 | 335 | Advanced часть: 336 | - Настроить CI с Github actions. 337 | 338 | Bonus часть: 339 | - Настроить генерацию отчетов с Allure report. 340 | 341 | Notes: отдельное спасибо Сергею Чепкасову и Максиму Рогожникову за отличный воркшоп. 342 | 343 | --- 344 | {#performance #load #gatling #jmeter} 345 | 346 | ### Занятие 11. Data Quality. 347 | 348 | - [Лекция 11]() 349 | - [Пример]() 350 | - [Видео](https://youtu.be/k1zRUOooD38) 351 | 352 | Ссылки 353 | - [Great Expectations](https://github.com/great-expectations/great_expectations) 354 | - [Data quality at Uber](https://eng.uber.com/operational-excellence-data-quality/) 355 | - [Automating Large-Scale Data Quality Verification](https://www.vldb.org/pvldb/vol11/p1781-schelter.pdf) 356 | - [Full predicate coverage for testing SQL database queries](http://giis.uniovi.es/testing/papers/stvr-2010-sqlfpc.pdf) 357 | - [Software Testing Research - Publications](http://giis.uniovi.es/testing/#stvr10sqlfpc) 358 | - [Data mesh](https://francois-nguyen.blog/2021/03/07/towards-a-data-mesh-part-1-data-domains-and-teams-topologies/) 359 | - [Structural Coverage Criteria for Testing SQL Queries](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.216.1676&rep=rep1&type=pdf) 360 | - [Monitoring](https://databricks.com/blog/2020/03/04/how-to-monitor-data-stream-quality-using-spark-streaming-and-delta-lake.html) 361 | 362 | --- 363 | {#dataquality #data #greatexpectations #sql} 364 | 365 | 366 | ### Занятие 12. Best practices. Итоговая лекция. 367 | 368 | - [Лекция 11](ST.Lecture12.pdf) 369 | - [Видео](https://youtu.be/p-u6XWF_1AM) -------------------------------------------------------------------------------- /lectures_v_2021/ST.Lecture1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2021/ST.Lecture1.pdf -------------------------------------------------------------------------------- /lectures_v_2021/ST.Lecture10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2021/ST.Lecture10.pdf -------------------------------------------------------------------------------- /lectures_v_2021/ST.Lecture12.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2021/ST.Lecture12.pdf -------------------------------------------------------------------------------- /lectures_v_2021/ST.Lecture3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2021/ST.Lecture3.pdf -------------------------------------------------------------------------------- /lectures_v_2021/ST.Lecture6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2021/ST.Lecture6.pdf -------------------------------------------------------------------------------- /lectures_v_2021/ST.Lecture8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2021/ST.Lecture8.pdf -------------------------------------------------------------------------------- /lectures_v_2021/ST.Lecture9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/volekerb/testing-lectures/61a7f91cc111ddece73c582d02d797eadee1cbeb/lectures_v_2021/ST.Lecture9.pdf --------------------------------------------------------------------------------