Subsections of Example Projects

New Project Checklist

Steps to follow when setting up a new project in this course.

Java

New Project

  1. Clone empty GitHub repository into java folder:
git clone <url> java
  1. Install SDKMAN
curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal
  2. Install Gradle
sdk install gradle 7.6
  1. Change directory to java folder:
cd java
  1. Initialize Gradle Project
gradle init

Recommended options: 1. Type of project - 2: application 1. Implementation language - 3: Java 1. Split functionality across multiple subprojects - 1: no - only one application project 1. Build script DSL - 1: Groovy 1. Generate build using new APIs and behavior - no 1. Test Framework - 4: JUnit Jupiter 1. Project name - <name> 1. Source package - <name> 7. Add JaCoCo Plugin to build.gradle

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
    // JaCoCo plugin for code coverage
    id 'jacoco'
}

// ... rest of file here

// Configure Jacoco plugin
test {
    finalizedBy jacocoTestReport // report is always generated after tests run
}

jacocoTestReport {
    dependsOn test // tests are required to run before generating the report
    reports {
        xml.enabled false
        csv.enabled false
        html.destination file("${buildDir}/reports/jacoco")
    }
}
  1. Configure JavaDoc in build.gradle
// .. rest of file here

// Add tests to Javadoc
javadoc {
  classpath += project.sourceSets.test.compileClasspath
  source += project.sourceSets.test.allJava
}
  1. Add Checkstyle to build.gradle
plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
    // JaCoCo plugin for code coverage
    id 'jacoco'
    // Checkstyle plugin for linting
    id 'checkstyle'
}

// ... rest of file here

// Force Checkstyle to be more current version
checkstyle {
    toolVersion '10.6.0'
}
  1. Store Google Checkstyle XML at config/checkstyle/checkstyle.xml
    1. Optionally update Indentation section to match Codio values:
    <module name="Indentation">
      <property name="basicOffset" value="4"/>
      <property name="braceAdjustment" value="4"/>
      <property name="caseIndent" value="4"/>
      <property name="throwsIndent" value="8"/>
      <property name="lineWrappingIndentation" value="8"/>
      <property name="arrayInitIndent" value="4"/>
    </module>
  1. Add additional libraries to build.gradle as needed (Hamcrest, JUnit Parameters, etc.)

Existing Project or Example Project

  1. Clone existing project into java folder:
git clone <url> java
  1. Install SDKMAN
curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal
  2. Install Gradle
sdk install gradle 7.6
  1. Change directory to java folder:
cd java
  1. Compile and Test Existing Project
gradle run
gradle check

Python

New Project

  1. Clone empty GitHub repository into python folder:
git clone <url> python
  1. Change directory to python folder:
cd python
  1. Check Python Version:
python3 --version
  1. Create requirements.txt file
coverage
flake8<5.0.0
flake8-docstrings
flake8-html
lxml
mypy
pdoc3
pep8-naming
pyhamcrest
pytest
pytest-html
tox
  1. Install Python Libraries
pip3 install -r requirements.txt
  1. Create tox.ini file
[tox]
envlist = py39
skipsdist = True

[testenv]
deps = -rrequirements.txt
ignore_errors = true
commands = python3 -m mypy -p src --html-report reports/mypy
           python3 -m coverage run --source src -m pytest --html=reports/pytest/index.html
           python3 -m coverage html -d reports/coverage
           python3 -m flake8 --docstring-convention google --format=html --htmldir=reports/flake
           python3 -m pdoc --html --force --output-dir reports/doc .
  1. Create .gitignore file
__pycache__/
.tox
reports/
.coverage
  1. Create src and test directories
mkdir src
mkdir test
  1. Add empty conftest.py to src directory
  2. Add empty __init__.py to src and test directory to make them packages
  3. Create additional packages as needed
  4. Add __main__.py to src and include code to call main method

Existing Project

  1. Clone existing project into python folder:
git clone <url> python
  1. Change directory to python folder:
cd python
  1. Check Python Version:
python3 --version
  1. Confirm Python Version matches the envlist entry in tox.ini
    1. Python 3.6.x - py36
    2. Python 3.9.x - py39
    3. Python 3.10.x - py310
  2. Update Packages in requirements.txt
    1. Lock flake8 to version before 5.0: flake8<5.0.0
  3. Install Python Libraries
pip3 install -r requirements.txt
  1. Run Existing Project
python3 -m src

or (For flask projects)

python3 -m flask run
  1. Run tox using Recreate Option To Test Existing Project
tox -r

GitHub Commit and Release

  1. Git Commit and Push
git add .
git commit -m "Message"
git push
  1. On GitHub Site, create Release

Hello Real World

YouTube Video

In this example project, we will go through all the steps to create a new version of the classic “Hello World” program, called “Hello Real World.” We’ll explore how to create unit tests, calculate code coverage, write and generate useful documentation, and use a linter to make sure our code follows a proper coding standard.

Don’t worry if some of this doesn’t make sense right now - we’ll spend much more time later in this course going into more detail on many of these areas. For now, just follow along so you can see how it all works together. The goal is to give you a bigger picture view first, and then we’ll dive into the details later.

Example vs. Project

You might be wondering: what’s the difference between an example and a project in this course?

In short, an example, such as this one, is one where you are meant to follow along with a pretty detailed set of instructions and guidance to complete a task. Usually we’ll use these to introduce something new and give you a small task to explore how it works.

Then, you’ll be able to take what you learned and apply it as part of a project. The projects in this course do not include any instructions or guidance. Instead, you are given a set of requirements that you must fulfill, but you have some freedom to meet those requirements however you wish. Most projects will require you to apply what you learned through the example in some new or larger way.

In addition, several of the projects are designed as milestones of a larger overall project, so each project will involve building upon your previous work in a meaningful way.

Let’s get started!

Subsections of Hello Real World

Codio and Terminal

YouTube Video

One thing you’ll quickly notice is that the source code files are no longer created for you in this course. Instead, in most cases you’ll be responsible for creating the source code files and folders used by your program.

So, let’s cover some basics of using the Codio interface and the terminal.

Codio Interface

File Tree File Tree

To the left, you should see the Codio file tree interface. If you don’t see it for some reason, you can always show it by going to the View menu and selecting File Tree.

This is where you can manipulate files and folders within your project. You can right-click most things in the file tree to get a context menu like the one shown above, which can be used to create, delete, and rename files. You can also drag and drop elements in the file tree to move them between folders.

The Codio main interface supports panels and tabs, so you can create your own layouts as desired. The standard layout includes having this guide to the right and your code in a single panel to the left, but you may change that layout anytime. You can even minimize or close this guide if you need more space!

Codio has some great documentation for its IDE interface. A few of the more useful pages are linked below:

Feel free to play around with the Codio environment and editor, and find the settings that work best for you.

Java & Python Folders

We recommend placing all of your code and contents in either the java or python folder, whichever matches the programming language you are using. Those folders are already created for you in most assignments. The assignments in this course are designed to cover both languages, but to keep the files separate we use these folders. This also makes working with tools such as Gradle, Tox and Git much simpler later on.

The example videos will model this behavior. Make sure you pay special attention to the specific locations in the file tree where new contents are placed.

Linux Terminal

Linux Terminal Linux Terminal

You’ll also need to use the Linux terminal from time to time in this course. This is how you can compile and execute your programs, run your tests, and more. Codio includes several ways to open the terminal:

  1. Selecting the Tools menu, then choosing the Terminal option
  2. Pressing SHIFT + ALT + T in any Codio window (you can customize this shortcut in your Codio user preferences)
  3. Pressing the Open Terminal icon in the file tree

For the most part, you’ll learn all the commands you need to know by watching the videos we provide in these example assignments. However, this will only scratch the surface of what can be done via the Linux terminal. To learn more about Linux and using the Linux terminal, here are a couple of additional resources you may want to review:

  1. Command Line Interface Tutorials from Codio - available in the CC 410 Modules List on Canvas (look at the bottom of the page)
  2. Beginners Guide to the Bash Terminal by Joe Collins on YouTube
  3. Ubuntu Terminal from CIS 527 at K-State

Working with Directories

One important thing to keep in mind when working through the Linux terminal and the Codio interface is the path of the current directory. Many times we will either create or edit files from a certain directory location, or we’ll need to make sure the working directory of our Linux terminal matches the example given.

In any of the videos and screenshots, you can determine the current working directory of the Linux terminal by looking closely at the prompt. It will be shown between the colon : and dollar sign $ characters, as shown in the image below:

Path Terminal Path Terminal

The path ~/workspace corresponds to the top level of the Codio file tree. So, the full path ~/workspace/java/directory1/directory2 corresponds to the directory highlighted in the image below:

Path Directory Path Directory

Make sure you watch the videos and read the content closely to get the directories correct! The projects in this course are much more complex than previous courses, and include multiple different folders full of files, sometimes with identical names!

Directory vs. Folder

Computer programmers tend to use the terms “directory” and “folder” interchangeably when talking about a file system. Most Linux tools use the term “directory,” but many other operating systems refer to them colloquially as “folders” since that is the icon most commonly used to represent them. Rest assured that both generally mean the same thing - a location where other files or directories/folders can be stored in a hierarchical file system.

That should cover the basics! On the next few pages, we’ll dive into creating our new “Hello Real World” project!

GitHub Classroom Setup

YouTube Video

One major new concept in this project is to save the source code of our project on the code sharing website GitHub using a tool called Git. We’ll be using a feature of GitHub called GitHub Classroom to manage several assignments in this course, so let’s explore what it takes to get connected to GitHub Classroom and download this project.

Accept the Assignment on GitHub Classroom

The first step is to accept the assignment on GitHub classroom. You can do this by finding the invitation link in the assignment’s description on Canvas. For many assignments in this course you’ll find two different items in Canvas - one that links to Codio, such as the assignment that led you here, and another that contains a GitHub Classroom link to accept the assignment via GitHub classroom.

First, before you do anything else, you’ll need to create an account on GitHub if you don’t have one already. Once you’ve created your account and successfully confirmed your email address, you can move on to the next step.

GitHub Student Developer Pack

Once you’ve created your GitHub account, you can also get access to the GitHub Student Developer Pack, which gives you access to a ton of cool tools and free content for developers. We highly recommend checking it out!

GitHub Student Developer Pack GitHub Student Developer Pack

Next, click the invitation link in Canvas to accept the assignment in GitHub classroom. The first time you do this, you’ll be asked to authorize GitHub Classroom on your GitHub account. You should see a page that looks like this:

GitHub Classroom Authorize GitHub Classroom Authorize

Next, you’ll be asked to accept the invitation itself. Once you do that, GitHub will create a repository for you to store your code, which may take a few minutes. You can refresh the page to see the progress. Once it is done, you’ll see a page like this:

GitHub Classroom Done GitHub Classroom Done

However, before you can access the assignment, you may need to accept an invite that is sent to your email address that is registered with GitHub. However, once you’ve done that, you should be able to access your repository! The page should look like this:

GitHub Classroom Repo GitHub Classroom Repo

You’ll need to perform this process for each GitHub Classroom assignment in this course, but after the first time it should be as easy as clicking the link in Canvas and setting up a new repository.

Add your SSH Key to GitHub

Before we can upload code from Codio to GitHub, we must add your Codio Account’s SSH key to GitHub. This will allow GitHub to identify your account within Codio, and you won’t have to enter your username and password each time you want to send code to GitHub. Thankfully, Codio includes a way to easily link your Codio account to GitHub and make this process quick and easy. You only have to do this one time to link these two accounts, so let’s do it now.

First, you’ll need get to your Codio Account Settings. You can do so by either choosing the Dashboard option from the Codio menu at the top of any Codio project, and then clicking your username in the bottom left corner of the screen. Alternatively, you can just navigate to https://codio.com/home/settings while logged in to your Codio account.

Once there, click on the Applications tab at the top. That should bring you to a page that looks like this:

Codio Applications Codio Applications

Click the Connect Account button to connect your Codio account with GitHub, and then click the button to authorize Codio on the next page. Once that is done, you should be brought back to Codio, where you can click the Upload Public Key button to add your SSH key to GitHub. Once again, you’ll need to click the button to authorize Codio on GitHub. Once you get back to Codio, you should see a message stating that your public key is uploaded to GitHub.

Codio Key Done Codio Key Done

There you go! You can confirm that it works by going to your GitHub account settings, finding the SSH and GPG keys section, or by navigating to https://github.com/settings/keys. Once there, you should see an SSH key added by Codio, which would look something like this:

Codio Key on GitHub Codio Key on GitHub

Manually Adding SSH Keys to GitHub

If you prefer to do this manually, here are the steps:

  1. In Codio, go to your Account Settings and select the SSH Key tab: https://codio.com/home/settings?tab=ssh-key
  2. Copy the entire Codio SSH key found there. It should begin with ssh-rsa.
  3. In GitHub, navigate to the Account Settings and find the SSH and GPG Keys section: https://github.com/settings/keys
  4. On that page, click the New SSH Key button, and paste the SSH key from Codio. You must also give it a helpful title.
  5. Click Add SSH Key to save the key. It should now appear on the page in GitHub.

Now our GitHub and Codio accounts should be linked, allowing us to easily download and upload code through GitHub Classroom using Codio.

Clone Starter from GitHub

YouTube Video

Once we have accepted the assignment on GitHub classroom and have access to the repository, we can “clone” it in Codio, allowing us to access any starter code and then add our own code as we continue to work through this example project. This project doesn’t include any starter code, but we’ll go ahead and do this step first, just like we would with any other project.

Find GitHub Repository URL

At this point, you should have access to a repository page that looks like this:

GitHub Classroom Repository GitHub Classroom Repository

On that page, we need to get the SSH URL of this repository. We can find it by clicking the green Code button in the upper right. On the box that appears, make sure you select the SSH option to get the SSH URL. It should look similar to this:

GitHub Classroom SSH URL GitHub Classroom SSH URL

The SSH URL should begin with git@github.com:. If it starts with https:// then you need to select the SSH option. Once you have the correct URL, select it and copy it to your clipboard.

Clone Project in Codio

Next, we need to open a Linux terminal in Codio and use the git command line tool to clone that repository in Codio. Make sure your terminal is in the workspace folder by running this command first:

cd ~/workspace

It should be in that folder by default, but it is always a good idea to check by either running the command above or looking at the end of the command prompt.

Then, we will use a command structured like this to clone the repository. do not run this command - keep reading:

git clone <SSH URL> <language folder>

In that command, replace the <SSH URL> portion with the SSH URL you copied from GitHub above. Also, replace the <language folder> section with the name of the folder for the programming language you wish to use.

For example, if I would like to complete this exercise using the Java programming language, my command would look like this:

git clone git@github.com:K-State-Computational-Core/example-1-hello-real-world-russfeld-student.git java

For Python, I would use:

git clone git@github.com:K-State-Computational-Core/example-1-hello-real-world-russfeld-student.git python

Of course, your SSH URL will be different, so you must adjust the required command above to meet your needs. Basically, just type git clone, then a space, then paste in your SSH URL, and then another space, followed by the name of the programming language that you’d like to use. That’s all it takes!

When I run that command, I’ll get output that looks like this:

Git Clone Output Git Clone Output

If this is the first time you’ve used git in this Codio project, you’ll have to type yes and press enter at the prompt to accept the key for github.com. Once you do that, then the rest of the command will complete and you’ll see the following output:

Git Clone Success Git Clone Success

Since I used java as the last part of that command, I should now see at least two new items in the java folder in the Codio file tree:

Codio File Tree After Git Codio File Tree After Git

If you choose python, you’ll probably see the same items inside of the python directory instead. The .git and .github folders contain information about the repository and the classroom assignment, and should be left alone. DO NOT DELETE OR MODIFY THESE FOLDERS! You may notice that they are not present in many of the screenshots in this assignment, just as a reminder to ignore them completely.

Wrong Folder

If you accidentally forget to specify a folder at the end of the git clone command, or specify the wrong folder, your repository may not be in the java or python folder that already exists in Codio. In that case, there are a couple of remedies you may try:

  1. Delete the folder that was created in error, and try the command again with the correct folder name.
  2. Delete the existing java or python folder, and rename the new folder to the correct name.
Folder Not Empty

If you try to clone a repository into a folder that is not empty, you’ll get an error message. In that case, it is best to completely delete the folder and try again. If the folder doesn’t exist, the git command will create it for you.

Permission Errors

If you receive an error message stating “Could not read from remote repository,” double-check your URL and try again. If it still doesn’t work, let the instructor know. There is a chance that the instructor simply needs to authorize the Codio SSH keys on the new GitHub classroom instance. We have to do this each semester, and it can easily be forgotten. You may earn some bug bounty points for letting us know about this!

If you were able to successfully clone the project from GitHub Classroom into Codio, you are ready to begin coding! From here on out, the guide will refer to the java and python folders directly, assuming that you’ve properly cloned the assignment from GitHub correctly.

Next Steps

The next parts of this project are different for Java and Python. Click the link below to jump to the correct section for your language. You can also use the links in the menu to the left.

Java
Python

Subsections of Java

Install Gradle

YouTube Video
tl;dr

If you are reviewing this material or are familiar with these concepts, here’s the short version:

# Install tools on Ubuntu if doing this at home
sudo apt update && sudo apt install zip unzip curl sed
# Verify Java SDK version 8 or higher
java -version
# Install SDKMAN!
curl -s "https://get.sdkman.io" | bash
# Install Gradle
sdk install gradle 7.6

Working with Java on the Linux terminal can be complicated, especially as projects become larger and larger. Thankfully, there are many tools that have been developed to simplify this process. For this course, we’re going to use one such tool, called Gradle.

Gradle

Gradle is a multi-platform build tool that helps automate development and deployment of applications written in a variety of languages. It also includes a very robust set of plugins that allow developers to perform many different tasks, directly through the Gradle command interface.

In the Java ecosystem, similar tools include Ant and Maven. In addition, many desktop IDEs such as IntelliJ IDEA, Eclipse and NetBeans automatically perform many of the same functions as Gradle.

The main reason we’ve chosen to use Gradle in this course is the ease with which it can be used, and the fact that it is available on pretty much any modern platform.

Installing Gradle

First, let’s install Gradle following the instructions found in the Gradle User Guide. As a developer, you’ll want to get familiar with reading the documentation for various tools you use, and Gradle is no exception.

On that page, the first step is to confirm that we have a version of Java installed. So, we’ll need to open a terminal and run the following command:

java -version

All of these commands can be executed from any directory, but we recommend using the default location of ~/workspace. We should see output similar to the following:

Java Version Java Version

In this image, we see that we already have Java 8 installed, specifically version 1.8.0_275. This is included in our standard Codio stack, so we won’t have to worry about installing Java. Therefore, we meet the minimum installation requirement.

Installing SDKMAN!

Next, we’ll need to install the SDKMAN! tool, which is the recommended way to install Gradle on Linux systems such as Codio. To install SDKMAN!, we’ll just follow the installation instructions found online, which calls for us to run the following command:

curl -s "https://get.sdkman.io" | bash

When we do that, we’ll get output that looks like this:

Install SDKMAN! Install SDKMAN!

If it was successful, we’ll have to close and reopen the terminal in order to use it.

Three Quick Comments

Here are three quick comments to explain a bit more about what we just did that may be helpful in the future.

Linux Command Prefixes

One point of confusion for many new developers is the inconsistent way Linux terminal commands are denoted in the documentation of various tools. For example, in the Gradle documentation, Linux terminal commands are prefixed by a greater-than symbol >:

Arrow Prefix Arrow Prefix

In the SDKMAN! installation instructions, you’ll notice that the installation command includes a dollar sign $ in front of it, as seen below:

Dollar Sign Dollar Sign

However, when we executed those commands, we didn’t include the greater-than symbol or dollar sign. Why is that? Many places that include documentation for the Linux terminal helpfully include either the last symbol of the command prompt before the command, or some other symbol like an arrow, indicating that this is a command to be entered in the terminal. Linux typically uses two different types of command prompts:

  • Dollar sign $ - indicating that the terminal is logged in to a normal user account
  • Pound sign # - indicating that the terminal is logged in with administrator permissions (root)

Documentation also often uses the > symbol in front of commands to be entered by the user. So, in general, when you see a Linux terminal command in documentation that starts with a >, $ or #, you should omit that character when executing the command. This can be very confusing and frustrating to get used to, especially since it is not standardized. As we’ll learn later in this course, standardized documentation is a very important part of being a good programmer!

Linux Terminal Security

The command we executed to install SDKMAN!, curl -s "https://get.sdkman.io" | bash is a somewhat dangerous command to execute, unless you trust the source. In effect, the curl -s command will silently load the webpage at the URL given without presenting any error messages. Then the | bash part tells the command to execute that webpage as if it were a Linux terminal script. This can be especially dangerous if you are logged in with administrator permissions or run the command prefixed with sudo. If the webpage contained a malicious script, your system could easily be compromised.

We point this out simply to encourage you to be wary about any commands you execute in a Linux terminal without fully understanding them. In this case, we’re reasonably confident that the SDKMAN! installation script is safe, we aren’t running it as an administrator, and Codio provides a safe environment to try things without installing them locally.

When in doubt, it is perfectly OK to reach out to the instructors of this course or another, more experienced developer to get help reviewing whether a particular command is safe to execute.

Installing SDKMAN! At Home

A bit later in this course, we’ll discuss how to do this on your own computer at home. If you are installing SDKMAN! on a Linux system, you’ll need to make sure the following tools are installed:

  • zip and unzip
  • curl
  • sed

Most of these tools can be easily installed on any Linux system. For example, on Ubuntu you can use the following command to ensure those tools are installed:

sudo apt update && sudo apt install zip unzip curl sed

Installing Gradle using SDKMAN!

Once SDKMAN! is installed, we can use it to install Gradle using this command:

sdk install gradle 7.6

After that command executes, you should get a message similar to the following:

Gradle Install Gradle Install

If you receive that message, then Gradle has been successfully installed. You can test this by running the following command:

gradle -version

You should get a message from Gradle telling you what version is installed. If that works, you can move on to the next page.

Having Issues?

If you are having trouble getting Gradle installed, reach out to the course instructors for assistance.

Create New Project

YouTube Video

Once we have Gradle installed, we can use Gradle to create a scaffold for our new Java project. This is a great starting point for any Java application, but we’ll customize it a bit to suit our needs.

For this part of the project, we’ll loosely follow this guide on Building Java Applications from the Gradle documentation.

Gradle Init

First, we’ll run the Gradle init command. This handy script allows us to create a scaffold for all sorts of different programming languages and frameworks supported by Gradle.

When we run this command, we’ll want to be in the java folder. So, let’s open the Linux terminal and change our directory to that location:

cd ~/workspace/java

Of course, if you are already in the ~/workspace folder, you can just use cd java to get there. In the code above, we include the whole path so that it will always work, regardless of the current working directory.

Next, we’ll run the Gradle init command:

gradle init

This script will ask a few questions about what type of project we’d like to create. We’ll choose the following options:

  • Type of project - 2: application
  • Implementation language - 3: Java
  • Split functionality across multiple subprojects - 1: no - only one application project
  • Build script DSL - 1: Groovy
  • Generate build using new APIs - no
  • Test Framework - 4: JUnit Jupiter
  • Project name - hello
  • Source package - hello

If done correctly, your output should look like this:

Gradle Init Output Gradle Init Output

You should also now see a whole bunch of files and directories inside of the java directory, as shown in this screenshot:

Gradle Init Structure Gradle Init Structure

This is already looking quite a bit more complicated than our previous “Hello World” app! Let’s see if we can go through it and clear up a few things.

Project Structure

Here is a quick description of the contents of the java folder that were added by the Gradle init command:

  • .gradle - This is a working folder for Gradle, and contains information Gradle uses when running commands. You shouldn’t have to do anything with this folder. However, if you run into issues with Gradle, you can safely delete this folder to “reset” Gradle, similar to clearing the cache in a web browser.
  • app - This folder contains the source code for both the application itself as well as its unit tests.
    • app/build.gradle - This file contains the settings for Gradle for this application. This is the file we’ll need to edit to add additional features to Gradle for this application.
    • app/src/main/java - This folder contains the main source code for the application.
    • app/src/test/java - This folder contains the unit tests for this application.
  • gradle - This folder contains the Gradle “wrapper” that can be distributed along with this project. It allows you to run Gradle quickly and easily on other systems that develop this project. You can leave this folder alone for now.
  • .gitattributes - This is a settings file for the Git tool, which we’ll discuss later in this module. For now, we can just leave it as is.
  • gitignore - This is a settings file for the Git tool, which we’ll discuss later in this module. For now, we can just leave it as is.
  • gradlew - This is a script file that can be used to download and install Gradle on a Linux-based system that doesn’t currently have Gradle installed. It is useful for deploying this code on a different system. For now, we won’t worry about it.
  • gradlew.bat - This is the same as gradlew, but for Windows-based systems.
  • settings.gradle - This file tells Gradle what projects to include in our various build tasks. For now, we only have one project, so we can leave it alone. However, for larger applications that contain multiple projects, we’ll edit this file to include those as well.

So, as you can see, the files we’ll mostly be concerned about are the ones in the app directory. On the next page, we’ll start working with those files to build our new “Hello Real World” application.

JCenter Deprecated

The JCenter package repository was sunset in May 2021, and stopped serving packages in early 2022. Some older versions of Gradle will set the JCenter repository as the default location to find packages, but that should be updated to use Maven Central instead.

To make sure your project will continue to work properly, open the build.gradle file in the app folder and look for the following section:

repositories {
    // Use JCenter for resolving dependencies.
    jcenter()
}

If that section contains jcenter() as shown above, update it to use mavenCentral() instead:

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

This will ensure that the project will continue to work properly after JCenter is shut down.

Write Hello World

YouTube Video

Now, let’s write the basic source code for our “Hello Real World” application. Thankfully, we can adapt some of the code that is provided by the Gradle init command, but we’ll simplify it a bit to meet our needs.

Rename App.java

First, let’s rename the App.java file in app/src/main/java/hello to HelloWorld.java. This filename is a better representation of what we want our class to be. Recall that the filenames of .java files must exactly match the name of the Java class contained in the file.

Let’s do this through the Codio interface. Use the file tree to the left to navigate to the file App.java, and then right-click and choose Rename. You can then enter HelloWorld.java in the box. Once you have completed this task, you should now see the following structure in the file tree:

Renamed File Renamed File

Write Code in HelloWorld.java

The Gradle init command created some sample code that is still in the renamed HelloWorld.java file. Let’s replace it with this code:

package hello;

public class HelloWorld
{

    public static void main(String[] args)
    {
        System.out.println("Hello World");
    }
}

This is a pretty standard “Hello World” application, and all the code here should be pretty familiar. The only new line of code that you may not have seen before is the very first line, package hello;. That line is used to denote what package this file belongs to, and it corresponds to the folder named hello that contains this file. We’ll discuss what a package is later in this course, but in essence packages are a way to organize large programs by grouping similar classes together.

Write Code in AppTest.java

We’ll also need to update the code in the AppTest.java file, which can be found in app/src/test/java/hello. We’ll change it later, but for now we’ll just replace it with some blank code so that it will compile:

package hello;

public class AppTest {

}

Unfortunately, Gradle will not allow our application to compile if the tests won’t also compile! So, we’ll fix that for now until we get to that part of the project.

Update Gradle Settings

Also, since we changed the name of the main class in our application, we’ll also need to change it in the build.gradle file. To do this, open the file app/build.gradle and look for the following section:

application {
    // Define the main class for the application.
    mainClass = 'hello.App'
}

We’ll need to update it to use our new HelloWorld class. So, replace that section with the following:

application {
    // Define the main class for the application.
    mainClass = 'hello.HelloWorld'
}

Recall that our HelloWorld class is in the hello package, so we use the name hello.HelloWorld to refer to the class.

Compile Hello World

Now we can start to explore what Gradle is really used for - easily compiling and executing our program. Recall that previously we’d need to use the javac command to compile our program, and then java to run it. For small programs, that can be pretty simple. However, as programs become larger and larger, and include more packages and external libraries, it can be extremely difficult to write the correct javac and java commands to get it all working.

Gradle is able to analyze the project’s structure and its configuration files and do this work for us automatically. Let’s see how it works!

First, we’ll need to make sure our terminal is still in the java folder:

cd ~/workspace/java

Then, we can run the Gradle build command to compile our program:

gradle build

The first time you run this command, it may take a little while as Gradle downloads libraries that it needs. Once it has completed, if everything works correctly you should get the following output:

Gradle Build Gradle Build

That means that Gradle has now compiled our program! We should now see a new app/build folder, which contains all of the output of the compilation process:

Build Folder Build Folder

There is a lot of new stuff here that we’ll cover later in this example, but for now there are two folders that we really care about:

  • app/build/classes - This contains all of the compiled Java class files for both the application and the unit tests.
  • app/build/distributions - This folder contains distributable versions of our application, both as a .zip file and a Java .jar file. If we want to share the compiled version of our application with others, these are the files they’d need.

As we can see, Gradle already does a lot more than we could do on our own with just a couple of commands. That’s the power of using a build tool such as Gradle!

Execute Hello World

Once we’ve compiled our application, we can execute it using the Gradle run command. As before, we’ll do this from inside of the java folder:

gradle run

If everything is set up correctly, we should see the following output:

Hello World Run Hello World Run

There we go! We’ve successfully built and run our first application in Gradle! If you want, you can test different messages in HelloWorld.java to make sure the program is working correctly.

Other Gradle Commands

There are a couple other Gradle commands we should learn. First, from within the java folder, use the following command:

gradle clean

This command will delete the entire build folder, basically clearing out anything that Gradle created in the previous commands. This is helpful if you want to completely recompile your application from scratch. Gradle is an intelligent build tool that will only compile files that have changed since the last time it compiled the program, but sometimes it can get confused, so using gradle clean is a way to reset it and force it to compile the entire application again.

Now, once we’ve completely cleared out our project, try this command again:

gradle run

Does that work? You might think that Gradle will give you an error saying that you have to build the application before you can run it. However, Gradle is an intelligent build tool, so it will automatically compile the application before running it. In effect, you can simply tell Gradle what you’d like to do, and it will perform any intermediate steps to get you there. It’s really handy!

Finally, if you want to see all the commands that Gradle can perform, use the following command:

gradle tasks

There are lots of other commands available in Gradle that we won’t cover in this course. Feel free to explore the documentation and try them out!

Git Commit & Push

YouTube Video
tl;dr

If you are familiar with using Git, here is the short version:

git status
git add .
# check that the correct files are added
git status
# update the commit message below
git commit -m "Commit Message Here"
git push

That will commit and push your changes to GitHub, which can now be found in the repository for this assignment.

At this point, we’ve written some code for our application. Now is a great time to save that code to our git repository and push it to GitHub in the cloud. By doing so, we’ll make sure that our code is not accidentally lost, but we can also make it quick and easy to restore a previous version of our code at any time.

Git

At this point, you might be pretty confused about Git, GitHub, repositories, cloning, and everything that we’ve done so far. That’s fine! We’ve found that one of the best ways to learn how to use Git and GitHub is simply by doing it and observing what it does, then slowly building up your knowledge by trying new things. However, let’s take a few minutes to explain some of the basics so you know what we are talking about.

Git is the name of a “distributed version control system” that was created initially by Linus Torvalds (the originator of the Linux operating system kernel). It was meant to solve a lot of the problems that he experienced when working with other version control systems of the time, such as Subversion and Mercurial.

Git stores data in several different places, and there are several Git commands to transfer the data between those places. It is best summarized in this diagram:

Git Workflow Git Workflow^[https://commons.wikimedia.org/w/index.php?title=File:Git_data_flow_simplified.svg&oldid=511614601]

Let’s look at Git from the bottom up and see how it works.

Working Directory

For this project, our working directory in Git is the java folder where we’ve been storing all of our code. This is the folder that we cloned our initial repository from GitHub Classroom into, and its where we’re actually doing all of the coding. At this point, we’ve created several new files and directories, but we’ve not added them to our local Git repository yet. Before we can do that, we must discuss one other thing - the .gitignore file.

.gitignore File

You may have noticed already that Gradle created a special file called .gitignore in the java directory, or in the root of our working directory for Git. That file contains information that tells Git what files we want to “ignore” - files that Git shouldn’t try to save. Typically we only want Git to store the source code for our program, and not any compiled classes, reports, or temporary files.

Currently, our .gitignore file contains the following content:

# Ignore Gradle project-specific cache directory
.gradle

# Ignore Gradle build output directory
build

These are simply the names of files or folders, but may also include wildcards like an asterisk * as well. Anytime we want to ignore anything in our project we can just add its file or folder name or path to this file, and Git will dutifully ignore it. So, let’s add one more entry to this file, which may be useful later:

# Ignore Gradle project-specific cache directory
.gradle

# Ignore Gradle build output directory
build

# Ignore project files
.project

Sometimes Gradle will create files named .project in our code, we want Git to ignore them. So, we can just add that entry to our .gitignore file to handle that.

Screenshot Inconsistencies

Some of the screenshots in this section were taken later in the creation of this project, so the exact files listed may be a bit different in your version. That’s fine - just use these as a baseline for the overall look and feel of the process, and feel free to ask questions if you aren’t sure things are correct.

Adding to the Index

The next level of Git is the index, sometimes referred to as the stage. The index contains a list of files that we’ve modified and would like to store in our local repository. We can add and remove items from the index at any time, and it won’t affect our working directory.

To see what is in the index, we can use the following command from within the java folder:

git status

You should see output similar to this:

Java Git Status Java Git Status

At this point, all of the files are “untracked” and listed in red, which means that they aren’t part of the index. So, we need to add files to the index before we can commit them. To do that, we can use the git add command, followed by the files we’d like to add to the index. Of course, naming each file individually can be time consuming, so we can use the following shortcut to add all of the untracked files in our working directory to the index:

git add .

In Linux, a single period . on the command line refers to the current working directory, so in this case it will add all of the files in the java folder to the Git index. When we run that command, we won’t see any output. But, if we run this command again:

git status

We should now see a whole bunch of files listed as “Changes to be committed”:

Java Git Index Java Git Index

Those files listed in green are now “staged” and ready to commit to our local repository. So, before we move on to the next step, we’ll want to make sure that all the files we changed are included in the list of files to be committed. In this case, we see our HelloWorld.java file, as well as the build.gradle file we modified in the previous step. We may also see additional files that were created by Gradle - we’ll want to make sure those are included as well.

Committing to the Local Repository

Once we’ve added the new and updated files to the index, the next step is to commit, or save, those files in our local repository. This will permanently^[There are ways to undo a commit once it is made, but in general it is poor practice to do so unless a major mistake has been made. For now, we’ll consider it permanent.] save these changes in our local repository, helping us keep track of the changes we’ve made to the code.

To commit to the local repository, we will use the git commit command. When committing to a repository, you are required to include a message describing the changes you made to the code. At this point, the commit message is just for your use, but it is always a good idea to make your commit messages very concise and descriptive so that it is helpful for you later on if you need to go back and restore this version of the code.

There are two ways to use the git commit command. Let’s briefly cover both of them, just so you can choose your preferred method.

Using Nano

The first method just calls for you to execute the following command:

git commit

When you do, your terminal will open a text editor program called Nano, which you can use to place your commit message at the top of the file. It will look something like this:

Java Nano Java Nano

As you can see, we’ve already entered the message “Commit Message Here” at the top of the file. Once we’ve done that, we need to save and close the file. To do that, follow these steps:

  1. Press CTRL+X to exit Nano. That will open a message at the bottom that says “Save modified buffer?”
  2. Press Y to respond “yes”. That will change the message to “File Name to Write:”
  3. Press ENTER to save the file using the name given.

Using the Command Line

Commit messages can also be specified directly on the terminal using the -m command line option. For example, if we wish for our commit message to be “Commit Message Here” we would use the following command:

git commit -m "Commit Message Here"

This is a quick and easy way to add a commit message to our commit without using a text editor.

Successful Commit

Once we’ve specified our commit message, Git will commit our changes to the local repository. We should see output similar to this if it was successful:

Java Commit Success Java Commit Success

If so, we’ve just made our first commit to our local repository. Now, if we ever need to restore our code back to that point, we can! As we continue to work with Git in this course, we’ll explore some of these more advanced features. For now, we’ll just us it to track our work.

Pushing to a Remote

The last step we can take is to push our local repository to a remote location, called a remote. GitHub is a great example of a remote that works with Git, but there are many others as well. For this course, since we are using GitHub Classroom, we’ll be using GitHub as our remote.

Thankfully, because we’ve already cloned this assignment from GitHub Classroom, we don’t have to do anything to be able to use it. In Git, cloning is the term we use for the first time we download a remote repository and store it on our computer. Once we’ve downloaded it the first time, we can get access to any changes by pulling from the remote.

So, to push a copy of our local repository to the remote, we can run this command:

git push

We should then get output that looks like this:

Java Git Push Java Git Push

There we go! We’ve now pushed our code to GitHub. Now, let’s go check it out. So, navigate to your repository on GitHub classroom, and refresh the page if needed to see your changes. For me, it now looks like this:

Java GitHub Java GitHub

Now all of our code is visible on GitHub, and we can see our commit message next to all of the files that have changed. As we continue to make commits and push them to GitHub, we can use this website to explore the different versions of each file. In addition, the instructors for this course can explore your repository and help you resolve errors and assign grades based on your work here.

At the end of this example project, you’ll create a release on GitHub to signify that you are done with the assignment. We’ll cover how to do that later in this example. For now, let’s forge ahead and learn about unit tests.

Unit Tests

YouTube Video

At this point, we’ve written the basic code for our “Hello World” program. However, how can we automatically verify that the source code works properly? To do that, we’ll need to create some unit tests that verify our code.

Unit Tests

In our Java application, the unit tests are stored in the app/src/test/java directory. In that directory, there is a directory for hello that represents a package, just like the hello directory in the app/src/main/java where our program code lives. It is typically good practice to have packages in our unit tests that match the packages in our source code, so we’ll leave it as is.

However, just like we renamed App.java to HelloWorld.java, let’s do the same with the AppTest.java file, found in app/src/test/java/hello. After you rename it, it should look like this:

Rename AppTest Rename AppTest

Thankfully, we don’t have to do anything else after renaming this file, because, as we’ll see shortly, Gradle is able to automatically find all of the unit test classes that exist in the app/src/test directory.

Update JUnit to Version 5

Older versions of Gradle will default to JUnit 4 instead of JUnit 5, also known as JUnit Jupiter. If your project is set up this way, you’ll need to upgrade.

To do this, we need to edit a few lines in the build.gradle file, which can be found in the app directory. The instructions for this process can be found in the JUnit 5 User Guide

First, look for the following dependencies section:

dependencies {
    // Use JUnit test framework.
    testImplementation 'junit:junit:4.13'

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:29.0-jre'
}

As we can see, it uses version 4.13 of JUnit. Let’s update to JUnit 5, and also configure Gradle to use the JUnit Jupiter test engine. Replace that dependencies section with this one:

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1'

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:31.1-jre'
}

Lastly, we need to configure Gradle to use the JUnit platform for testing. So, at the bottom of our build.gradle file, we’ll add the following section, outside of any other sections:

tasks.named('test') {
    // Use junit platform for unit tests.
    useJUnitPlatform()
}

There we go! Now Gradle is configured to use JUnit 5 properly!

Write Code in HelloWorldTest.java

Let’s put some code in the HelloWorldTest.java file to test our “Hello World” application. Here’s a sample of what a unit test for this might look like:

package hello;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.junit.jupiter.api.Test;

public class HelloWorldTest {

    @Test 
    public void testHelloWorldMain() {
        HelloWorld hw = new HelloWorld();
        final PrintStream systemOut = System.out;
        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
        System.setOut(new PrintStream(testOut));
        hw.main(new String[]{});
        System.setOut(systemOut);
        assertEquals("Hello World\n", testOut.toString(), "Unexpected Output");
    }
}

We can break this code down line by line to discuss what it does and how it works. Let’s look at the package and import statements first:

  1. package hello - as we saw before, this line is used to show that this class is part of a package called hello.
  2. import static org.junit.jupiter.api.Assertions.assertEquals - this imports one of the tests that we need from the org.junit.jupiter library, the latest version of JUnit. You can find more information about JUnit by reading the JUnit 5 User Guide.
  3. import java.io.ByteArrayOutputStream - this is a library used to store data in an array of bytes.
  4. import java.io.PrintStream - similarly, this library is used to easily “print” data into another output stream.
  5. import org.junit.jupiter.api.Test - this is another class from JUnit that allows us to mark methods as unit tests.

Now, let’s look at the testHelloWorldMain() method itself. Here are the first few lines that we’ll need to understand:

  1. @Test - this is an example of an annotation in Java, which allows us to add additional information to a method. In this case, it tells JUnit that this method is a unit test method.
  2. testHelloWorldMain() - typically, the method name of a unit test is very descriptive. If possible, try to describe the exact nature of the test in the method name, since the failed unit tests are reported by the name of the method.
  3. HelloWorld hw = new HelloWorld() - to begin our test, we instantiate a copy of our HelloWorld class so we can test it. One major theory behind unit tests is that each test should be run in isolation from other tests. So, instead of having a global HelloWorld object that each test can use, we should write each test to create brand new objects. Later on in this course, we’ll see how we can simplify this and avoid code reuse.

Next, we have three lines of code that are closely related:

final PrintStream systemOut = System.out;
ByteArrayOutputStream testOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(testOut));

These three lines are used to redirect the output of the HelloWorld application we are testing to a different location. Recall that we use System.out.println to print text to the terminal, so these lines are used to store the existing value of System.out and then replace it with our own stream that we can use.

After that, we have the line hw.main(new String[]{}) which calls the main method of our Hello World class. We have to provide an empty array of strings as an argument, which matches the expected command-line arguments of the program itself. So, if we need to provide command-line arguments to this method, we can place them in this array!

Lastly, we do two more things. First, the line System.setOut(systemOut) will reset the System.out stream back to what it was previously.

Then, we reach the most important line of the program: assertEquals(testOut.toString(), "Hello World\n", "Unexpected Output"). This line is called an assertion, and it is the basis of most unit tests. In this line, we are stating that the output we receive from the program, stored in testOut and accessed via the toString() method, should exactly match "Hello World\n" in order for the test to pass. If it doesn’t, then we can share a helpful error message, in this case “Unexpected Output” when the test fails.

Typically, most unit tests contain several of these assertions, and there are many different assertion methods we can use, but for right now we’ll just use the assertEquals method.

There we go! That’s all it takes to write a simple unit test. However, as you’ve probably noticed, the code for a unit test is much more complex than the code that we are testing. That’s actually pretty typical - many times it is much more difficult to write tests that ensure the code works than writing the code itself. However, the usefulness of those tests will often outweigh the cost, as it can be even more costly to constantly fix bugs and errors that would have been caught by a proper set of unit tests.

Running Unit Tests

Now that we’ve written our unit test, we can use Gradle to execute them. To do that, simply open the Linux terminal, navigate to the java folder, and then run the following command:

gradle test

When you run this command for the first time, Gradle may take a moment to download the JUnit 5 libraries it needs. Once it is done, you’ll get the usual response that the build was successful, as shown below:

Test Success Test Success

However, we might want to learn a bit more about what tests were executed and make sure it is working properly. So, we can look for the test report in the build folder. Specifically, you’ll find it at app/build/reports/tests/test/index.html. So, find that file in the Codio file tree to the left and open it. When you do, you’ll see a file full of HTML like this one:

Test Report HTML Test Report HTML

That’s really difficult to read, isn’t it? Thankfully, we can tell Codio to open that HTML file as a webpage by right-clicking on it and selecting Preview Static:

Preview Static Preview Static

If done correctly, you should see a webpage that looks like this:

Test Report Test Report

Hopefully, we should see that our code passed the test! We can click on the links on that page to dig deeper and see the results for specific tests classes or even individual tests.

Test Failures

So, let’s explore a bit. First, let’s make a change to the unit test so that the program will fail. A great way to do this is to change the assertion on the last line of the unit test. Once you’ve made that change, re-run the tests using the following command:

gradle test

This time, you’ll get some different output:

Test Fail Test Fail

Here, we can see that JUnit will helpfully tell us the method name of each test that failed. We can now open the test report from before and dig into it to find exactly where the test failed and why. Here’s what you might see:

Test Fail Report Test Fail Report

The most important part is at the bottom of that file - it shows the exact assertion that failed and why. Below that, you’ll see a full stack trace of what was happening when it fails. We’ve highlighted the most important parts - the assertion that failed, and the line in the stack trace that shows exactly where in the unit test it failed.

So, when a test fails, you can use that information to work backwards and figure out why it failed. Was the test incorrect? Did the source code of the application have a bug? Either of those could be a reason for a test failure. Hopefully you can use the information from the test report to find it and fix it!

Code Coverage

YouTube Video

We’ve now written our program, as well as a unit test that runs our program and make sure it works. But, how can we be sure that our unit tests are adequately testing every part of our program? For that, we have to rely on another tool to help us calculate the code coverage of our unit tests.

Install JaCoCo in Gradle

Thankfully, there are many easy to use tools that will compute the code coverage of a set of tests. For Java, one of the most commonly used tools is JaCoCo. JaCoCo is a free code coverage library designed for Java, and it easily integrates with Gradle via the JaCoCo Plugin.

To install that plugin, all we have to do is open our build.gradle file, which can be found in the app directory. First, find the plugins section at the top of the file:

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
}

To install a plugin, we can simply add a new id line to that section, followed by the name of the plugin. For JaCoCo, we would update this section to the following:

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
    // JaCoCo plugin for code coverage
    id 'jacoco'
}

Once we’ve added the plugin, we may also want to add some additional information to the build.gradle file to help configure the plugin. In this case, we’ll add a few things. Most of these are taken directly from the JaCoCo Plugin documentation from Gradle. At the bottom of the build.gradle file, add the following sections:

// Configure Jacoco plugin
test {
    finalizedBy jacocoTestReport // report is always generated after tests run
}

jacocoTestReport {
    dependsOn test // tests are required to run before generating the report
    reports {
        xml.enabled false
        csv.enabled false
        html.destination file("${buildDir}/reports/jacoco")
    }
}

As described in the documentation, this will ensure that the JaCoCo report is generated each time the tests are run. In addition, we have configured the reports generated by JaCoCo to only include the HTML version of the report, and it will be placed in the reports folder in a folder called jacoco.

Compute Code Coverage with JaCoCo

Now that we’ve installed and configured the JaCoCo plugin, let’s use Gradle to run our tests again and see what happens. So, from within the java folder, we can run the following command:

gradle test

Once we do that, Gradle will install everything it needs to use JaCoCo, and in the app/build/reports folder, we should see a new folder called jacoco appear:

JaCoCo Folder JaCoCo Folder

Inside of that folder is another index.html file. So, let’s right-click it and select Preview Static to open it as a webpage. Hopefully we should see something like this:

JaCoCo Report JaCoCo Report

That tells us that we achieved 100% code coverage with our tests! That’s the goal, though it was pretty easy to achieve when our application really only contains one line of code. By clicking the links on the page, we can even see which lines are tested by our program, as shown below:

JaCoCo Highlight JaCoCo Highlight

More Complex Code

Let’s modify our application a bit and see how we can use JaCoCo to make sure we are really testing everything our application can do. In the HelloWorld.java file, found in app/src/main/java/hello, replace the existing code with this code:

package hello;

public class HelloWorld
{

    public static void main(String[] args)
    {
        if(args.length == 1)
        {
            System.out.println("Hello " + args[0]); 
        }
        else
        {
            System.out.println("Hello World");
        }
    }
}

This program will now print “Hello World” if executed without any command line arguments, but if one is provided it will use that argument in the message instead. So, let’s run our program again using this command from within the java folder:

gradle test

Once the tests have finished, we can open the JaCoCo report stored in app/build/reports/jacoco/index.html and we should find that it no longer achieves 100% coverage:

JaCoCo Not Full Coverage JaCoCo Not Full Coverage

If we drill down deeper, we can find the lines of code that aren’t covered by our tests:

JaCoCo Missing Lines JaCoCo Missing Lines

As we expected, our single unit test is not able to test each and every line of code in our application. That’s not good! So, we’ll need to update our tests to account for the change in our code.

Test-Driven Development

As a quick aside, if we were engaging in test-driven development, we would write the new unit test before changing the code. We won’t model that behavior right now, but it is worth noting that you don’t have to do these steps in the order presented here.

Update Unit Tests

So, let’s update our unit tests to account for this new code. There are a couple of ways we can do this:

  1. We can add more code to our existing testHelloWorldMain method to call the method multiple times, both with and without arguments.
  2. We can add additional test methods to test different behaviors.

In general, when working with unit tests, it is always preferred to add additional test methods to test additional functionality in the program. We want to keep our tests as simple and focused as possible, so that we can easily find the source of any errors it finds. If we simply added more code to the existing test, it would be difficult to tell exactly what caused the error. We’ll cover this in more detail when we formally discuss unit testing later in this course.

For now, let’s open the HelloWorldTest.java file stored in app/src/test/java/hello and add the following method to the HelloWorldTest class:

    @Test 
    public void testHelloWorldMainArgument() {
        HelloWorld hw = new HelloWorld();
        final PrintStream systemOut = System.out;
        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
        System.setOut(new PrintStream(testOut));
        hw.main(new String[]{"CC 410"});
        System.setOut(systemOut);
        assertEquals("Hello CC 410\n", testOut.toString(), "Unexpected Output");
    }

Notice that this is nearly identical to the previous unit test method - we simply changed the arguments that are provided to the main method, and also updated the assertion to account for the changed output we expect to receive. As discussed earlier, there are things we can do to prevent duplication of code like this in our unit tests, but we won’t worry about that for now.

Once again, let’s rerun our tests using this command:

gradle test

Once that is done, we can open the JaCoCo report and see if we are back to 100% coverage:

JaCoCo Fixed Coverage JaCoCo Fixed Coverage

If everything is working correctly, we should see that we are back at 100% coverage, and each line of code in our program is tested.

Of course, achieving 100% code coverage does not mean that you’ve completely tested everything that your application could possibly do - it simply means that you are at least testing every line of code at least once. It’s a great baseline to start with!

Git Commit and Push

This is a good point to stop and commit our code to our Git repository. So, like before, we’ll start by checking the status of our Git repository to see the files we’ve changed:

git status

In that list, we should see everything we’ve updated listed in red. Next, we’ll add them to our index using this command:

git add .

And then we can review our changes using the status command again:

git status

If we are satisfied that everything looks correctly, we can commit our changes using this command:

git commit -m "Unit Tests and Code Coverage"

And finally, we can push those changes to the remote repository on GitHub using this command:

git push

As you can quickly see, this is a pretty short set of 5 commands that we can use to quickly store our code in our local Git repository and on GitHub. We just have to carefully pay attention to the files we commit and make sure it is correct.

Documentation

YouTube Video

The next step in writing good code is adding proper documentation and comments to describe the code and what it does. By writing good documentation, we can pass on helpful information to other developers who need to maintain this code, including our future selves!

Javadoc

The Java platform includes a tool called Javadoc that can be used to generate documentation directly from the comments in a piece of source code. A great example that we already explored in the textbook is the entire Java API Documentation, which is generated directly from the Java source code using Javadoc. For instance, you can compare the comments at the top of the ArrayList source code file:

/**
 * Resizable-array implementation of the <tt>List</tt> interface.  Implements
 * all optional list operations, and permits all elements, including
 * <tt>null</tt>.  In addition to implementing the <tt>List</tt> interface,
 * this class provides methods to manipulate the size of the array that is
 * used internally to store the list.  (This class is roughly equivalent to
 * <tt>Vector</tt>, except that it is unsynchronized.)
 *
 * <p>The <tt>size</tt>, <tt>isEmpty</tt>, <tt>get</tt>, <tt>set</tt>,
 * <tt>iterator</tt>, and <tt>listIterator</tt> operations run in constant
 * time.  The <tt>add</tt> operation runs in <i>amortized constant time</i>,
 * that is, adding n elements requires O(n) time.  All of the other operations
 * run in linear time (roughly speaking).  The constant factor is low compared
 * to that for the <tt>LinkedList</tt> implementation.

and the top of the ArrayList documentation in the Java API:

ArrayList Doc ArrayList Doc

So, let’s explore how to create this documentation for our code.

Install Javadoc in Gradle

Thankfully, since the Javadoc tool is included as part of the Java software development kit, or SDK, so we won’t have to do anything to use it via Gradle. However, we’ll add one section to our build.gradle file to configure Javadoc. So, let’s open build.gradle again and add the following section to the bottom of that file:

// Add tests to Javadoc
javadoc {
  classpath += project.sourceSets.test.compileClasspath
  source += project.sourceSets.test.allJava
}

This configuration will tell the Javadoc tool to generate documentation for both our main application’s source code, but also the source code for our unit tests. While you may not want to do this for documentation you intend to release publicly, it can be very handy for developers to have an easy to refer to guide for both the unit tests and the source code itself.

Class Comments

The first type of Javadoc comments we’ll review are the comments that go along with classes. Let’s look at an example of comments that we could include at the top of our HelloWorld.java file:

package hello;

/** 
 * The HelloWorld class.
 *
 * <p>This is a sample HelloWorld program to demonstrate proper
 * Java coding style, testing, documentation, and more
 * 
 * @author Russell Feldhausen russfeld@ksu.edu
 * @version 0.1
 */
public class HelloWorld
{
...

There are lots of new things in this block of comments, so let’s look at each part individually:

  1. To create a large, multi-line comment in Java, we use /* prior to the comment and */ at the end of the comment.
  2. Comments for documentation include an extra asterisk at the beginning of the comment, so we actually see /**. This is known as the “begin-comment delimiter.”
  3. Typically, each line of the multi-line comment is prefixed by an asterisk * and placed such that it is aligned with the first asterisk of the start of the comment. Likewise, the first asterisk of the end of the comment is also aligned with the column of asterisks.
  4. The first paragraph of a Javadoc comment should give a short description of the class or method. It does not start with a <p> tag, but should use complete sentences.
  5. Any additional paragraphs must start with a <p> tag. Additional paragraphs provide more helpful information about the method or class. However, unlike HTML, no closing </p> tag is required at the end of the paragraph
  6. The end of the comment block consists of a set of optional tags. Javadoc includes several descriptive tags that begin with the at symbol @. They are used to denote important information about the class or method being documented. Classes typically include the following tags at a minimum:
    • @author - Denoting the author of this code. Multiple authors should be listed on separate lines with additional @author tags
    • @version - The version of the software or source code.

In most cases, every public class should include a documentation comment with at least a description, @author, and @version tags. If you haven’t already, go ahead and add comments similar to the example above to the HelloWorld class stored in HelloWorld.java! Make sure you update the @author tag to include your name.

Method Comments

Likewise, let’s look at the comments for a specific method. For example, here are some example comments for the main method in the HelloWorld class:

    /** 
     * Prints a hello message to the terminal.
     *
     * @param args    the command-line arguments
     */
    public static void main(String[] args)
    {
    ...

Similar to the class comments above, each method also includes a description, and may include additional paragraphs as needed. Methods use a different set of Javadoc tags to describe their functionality, and they are presented in the order listed below:

  • @param - Each method parameter should be described using an @param tag, followed by the name of the parameter and a description of what the parameter represents.
  • @return - If the method returns a value, it can be described using an @return tag.
  • @throws - If the method throws any exceptions, they can be described using @throws tags.

Similar to classes, most methods should also include a documentation comment. At a minimum, it should include a description, as well as any required @param, @return and @throws tags to describe the method in detail. So, go ahead and include comments for the main method in the HelloWorld class based on the example above.

Other Comments

Finally, individual variables in the source code can also be documented using Javadoc comments. Typically any attributes or fields stored directly within the class itself are documented in this way. We don’t have any attributes in our current program, so we won’t worry about this part for now.

We’ll discuss the creation of Javadoc comments in more detail later in this course. For now, feel free to refer to these resources for additional information:

Documenting Tests

Let’s briefly look at a documented version of our unit test code as well, just to see what that looks like. Some of the code has been omitted so we can just focus on the comments:

// package and imports omitted

/** 
 * The HelloWorldTest class.
 *
 * <p>This is the test class for the HelloWorld program.
 *
 * @author Russell Feldhausen russfeld@ksu.edu
 * @version 0.1
 */

public class HelloWorldTest {

    /**
     * Tests the HelloWorld.main() function with no arguments.
     *
     * @see    HelloWorld#main(String[])
     */
    @Test 
    public void testHelloWorldMain() {
        // code omitted
    }
  
    /**
     * Tests the HelloWorld.main() function with 1 argument.
     *
     * @see    HelloWorld#main(String[])
     */
    @Test 
    public void testHelloWorldMainArgument() {
        // code omitted
    }
}

Notice that these comments use the @see tag. That tag allows us to create a link to another part of our documentation. We’ll see how this works once we generate our documentation. For now, go ahead and place comments similar to what you see above in your HelloWorldTest.java file. As before, don’t forget to change the @author tag to your name!

Generating Documentation

Once we’ve added the comments to all of our files, we can use Gradle to create our documentation. To do that, we’ll simply open a Linux terminal, go to the java directory, and run this command:

gradle javadoc

That will create our documentation! Once it is done, we can go to the app/build/docs/javadoc directory to find our documentation. We should see something similar to this:

Javadoc Location Javadoc Location

As before, we can find the index.html file, right-click it, and select Preview Static to open our documentation as a webpage. That should open a page that looks like this:

Javadoc Javadoc

Doesn’t that look familiar? The design should hopefully remind you of the Java API Documentation that you are hopefully very used to working with at this point. From here, we can click through the links to learn more about each class. Let’s take a look at the documentation for our HelloWorldTest class. Toward the bottom of that page, we should see a description for the methods that looks like this:

Javadoc See Javadoc See

Notice that there is a “See Also” section at the bottom? That was created by the inclusion of the @see tag in the comment! So, we can add some useful information to our documentation comments that make our documentation easy to follow. If you look at the comments in the ArrayList file we’ve been using as an example, you’ll see even more ways to add helpful information to your documentation.

Checkstyle

YouTube Video

Finally, we’re at the point that we’ve written our application, a couple of unit tests, verified that the unit tests achieve a high level of code coverage, and we’ve written helpful documentation comments in our code. That’s a lot of content we’ve written, especially in our source code files. Now let’s see how good our coding style is by checking it using a linter.

Checkstyle

There are many different tools that can be used to check the style of source code. One of the most commonly used tools for Java is called Checkstyle. This tool can be easily integrated into Gradle, so it will be the tool we’ll use for now.

Add Checkstyle to Gradle

Just like with JaCoCo, we can use the Gradle Checkstyle Plugin to add Checkstyle to our Gradle configuration. First, we’ll need to open build.gradle and find the plugins section:

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
    // JaCoCo plugin for code coverage
    id 'jacoco'
}

We’ll add the checkstyle plugin, so our plugins section should now look like this:

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
    // JaCoCo plugin for code coverage
    id 'jacoco'
    // Checkstyle plugin for linting
    id 'checkstyle'
}

Unfortunately, the version of Checkstyle that is included with Gradle is bit out of date. So, we’ll also add some quick configuration information to build.gradle to configure the version of Checkstyle used. To do that, we’ll add this block at the bottom of that file:

// Force Checkstyle to be more current version
checkstyle {
    toolVersion '10.6.0'
}

Checkstyle Configuration

Checkstyle is a very powerful tool, but it requires a configuration file to operate. The configuration file tells Checkstyle what rules to enforce and what rules to ignore. There are a few standard Checkstyle configuration files that are used for Java, but the most common is the one based on the Google Style Guide. So, we’ll need to get a copy of that configuration file and add it to our project.

Here are the steps we’ll follow:

  1. Download the Google configuration file for Checkstyle version 10.6.0. It can be found at this location on GitHub.
  2. Create a folder named config inside of the java folder in Codio.
  3. Create a folder named checkstyle inside of the config folder we just created.
  4. Upload the google_checks.xml file that was downloaded in step 1 to the config/checkstyle folder in Codio created in step 3.
  5. Rename that file to checkstyle.xml.

Once you have done those steps, you should see a directory path similar to this:

Checkstyle Config Checkstyle Config

Finally, for this course we are going to make one modification to the standard Google-based configuration file for Checkstyle. The Google style guide says that Java code should be indented using only 2 spaces per level. However, most IDEs, including Codio, use 4 spaces by default. Instead of changing the configuration in Codio, let’s change the configuration of Checkstyle to account for that. So, we’ll need to open the checkstyle.xml file we just uploaded to the config/checkstyle directory, and search for the following section of the file (look for line 243):

    <module name="Indentation">
      <property name="basicOffset" value="2"/>
      <property name="braceAdjustment" value="2"/>
      <property name="caseIndent" value="2"/>
      <property name="throwsIndent" value="4"/>
      <property name="lineWrappingIndentation" value="4"/>
      <property name="arrayInitIndent" value="2"/>
    </module>

Once we’ve found that section, we’ll simply double each of the values there. So, all the 2s become 4s and 4s become 8s. When we are done, it should look like this:

    <module name="Indentation">
      <property name="basicOffset" value="4"/>
      <property name="braceAdjustment" value="4"/>
      <property name="caseIndent" value="4"/>
      <property name="throwsIndent" value="8"/>
      <property name="lineWrappingIndentation" value="8"/>
      <property name="arrayInitIndent" value="4"/>
    </module>

There we go! We should now be ready to use Checkstyle in our project.

Running Checkstyle

To run Checkstyle, we can use Gradle once again:

gradle check

The check task in Gradle will run all checks, including Checkstyle. When we run that command, we should get a bunch of errors like this:

Checkstyle Errors Checkstyle Errors

We can also see a friendlier version of this output by going to app/build/reports/checkstyle and opening either main.html or test.html. Remember to right-click and choose Preview Static to open these as webpages. For example, here’s what you might see in main.html:

Checkstyle Report Checkstyle Report

At the bottom of the report is a list of each line of code that contains an error, as well as a description of the error. Let’s look at a couple of these errors and see how we can fix them.

Checkstyle Errors

Javadoc Blank Lines

One of the errors we might receive tells us the following:

Javadoc tag ‘@author’ should be preceded with an empty line.

However, if we look at our code, it might appear that we have an empty line there:

/** 
 * The HelloWorld class.
 *
 * <p>This is a sample HelloWorld program to demonstrate proper
 * Java coding style, testing, documentation, and more
 * 
 * @author Russell Feldhausen russfeld@ksu.edu
 * @version 0.1
 */

However, use your cursor to look closely at that comment block - it turns out that there is a space after the asterisk on the line before @author, but there isn’t a space after the asterisk in the blank line above. According to the style guide, there should not be anything on a blank line in a comment, not even a space. So, we’ll have to remove it.

This can be really frustrating since Codio helpfully adds those spaces for us, but we really don’t want them. So, in order for our code to pass the Checkstyle tool, we’ll have to remove them. Don’t worry - Python developers have the same problem with Codio constantly adding spaces to blank lines, so it affects everyone!

Curly Brace Location

Another error we received gives us this message:

{’ at column 1 should be on the previous line.

There are a couple other errors on that line, but this is the important one. In our code, we see the following use of curly braces around our class declaration:

public class HelloWorld
{

Did you notice that the curly braces were after the class and method declarations instead of on the same line? Checkstyle did! In section 4.1.2. of the Google Style Guide, they discuss the use of “Kernighan and Ritchie” or “K&R” style of curly braces. This refers to the style of braces used in the famous C Programming Language book by those authors, which has become a common standard across many programming languages. In short, the opening curly brace should be on the same line as the previous statement, but the closing curly brace should be on a new line. The Google style guide includes the following visual example:

if (condition()) {
    try {
        something();
    } catch (ProblemException e) {
        recover();
    }
} else if (otherCondition()) {
    somethingElse();
} else {
    lastThing();
}

So, we can modify our code to use K&R style braces to fix this issue.

Space after Statements

One more error we might receive is this one:

‘if’ is not followed by whitespace.

As discussed above with K&R braces, part of the standard includes adding a space before the opening curly brace after method and class declarations, as well as conditionals and loop statements. So, anywhere we have an if or else statement, we’ll have to remember to include spaces where appropriate.

Passing Checkstyle

Before moving on with this example, modify the code in HelloWorld.java and HelloWorldTest.java to ensure that everything is able to pass the Checkstyle tool. In many professional software development roles, you aren’t even able to save your code unless it passes a style checking tool, so it is a very good habit to get into. In this course, part of your grade will depend on your code following the appropriate style!

Git Commit and Push

This is a good point to stop and commit our code to our Git repository. So, like before, we’ll start by checking the status of our Git repository to see the files we’ve changed:

git status

In that list, we should see everything we’ve updated listed in red. Next, we’ll add them to our index using this command:

git add .

And then we can review our changes using the status command again:

git status

If we are satisfied that everything looks correctly, we can commit our changes using this command:

git commit -m "Unit Tests and Code Coverage"

And finally, we can push those changes to the remote repository on GitHub using this command:

git push

There we go! We’ve updated our repository once again.

Hamcrest

YouTube Video

Let’s introduce one more useful tool as part of this example, the Hamcrest assertion library. Hamcrest is a library of unit test assertions that is available for multiple programming languages, including both Java and Python. Hamcrest makes it easy to write very advanced assertions in a way that is both readable and flexible. In fact, most of the autograders in prior CC courses use Hamcrest as the primary assertion library to make them easy to develop. Let’s explore what it takes to add Hamcrest to our project.

Installing Hamcrest in Gradle

To make Hamcrest available, we simply have to add an entry to our build.gradle file. This process is described in the Hamcrest Documentation. In that file, locate the “dependencies” section:

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1'

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:31.1-jre'
}

In the line for testImplementation, we’ll add an entry for the latest version of Hamcrest, org.hamcrest:hamcrest:2.2. When we are done, it should look like this:

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1', 'org.hamcrest:hamcrest:2.2'

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:31.1-jre'
}

That’s all there is to it! We now can use Hamcrest in our unit tests

Unit Test with Hamcrest

Now, let’s build a unit test that uses Hamcrest. So, in the app/src/test/java/hello directory, create a new file called HelloWorldHamcrestTest.java and paste the following code in that file:

package hello;

import static org.hamcrest.MatcherAssert.assertThat; 
import static org.hamcrest.Matchers.is;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.junit.jupiter.api.Test;

/** 
 * The HelloWorldTestHamcrest class.
 *
 * <p>This is the test class for the HelloWorld program using Hamcrest
 *
 * @author Russell Feldhausen russfeld@ksu.edu
 * @version 0.1
 */
public class HelloWorldHamcrestTest {
    
    /**
     * Tests the HelloWorld.main() function with no arguments.
     *
     * @see    HelloWorld#main(String[])
     */
    @Test 
    public void testHelloWorldMain() {
        HelloWorld hw = new HelloWorld();
        final PrintStream systemOut = System.out;
        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
        System.setOut(new PrintStream(testOut));
        hw.main(new String[]{});
        System.setOut(systemOut);
        assertThat("Unexpected Output", testOut.toString(), is("Hello World\n"));
    }
  
    /**
     * Tests the HelloWorld.main() function with 1 argument.
     *
     * @see    HelloWorld#main(String[])
     */
    @Test 
    public void testHelloWorldMainArgument() {
        HelloWorld hw = new HelloWorld();
        final PrintStream systemOut = System.out;
        ByteArrayOutputStream testOut = new ByteArrayOutputStream();
        System.setOut(new PrintStream(testOut));
        hw.main(new String[]{"CC 410"});
        System.setOut(systemOut);
        assertThat("Unexpected Output", testOut.toString(), is("Hello CC 410\n"));
    }
}

The code is nearly identical to the other unit test class, but with two major changes:

  1. There are a couple of new import statements at the top to include the assertThat and is methods from Hamcrest.
  2. Instead of using assertEquals the last line of each unit test uses assertThat. The order of the arguments is different, but the idea is the same. Also, note the use of the is method, which is simply stating that it should be equal.

Of course, a simple test case such as this doesn’t show the power of using Hamcrest instead of the built-in assertions in JUnit. If you want to know more about Hamcrest, feel free to check out the Hamcrest documentation. We’ll explore more about using Hamcrest in our unit tests later in this course.

Running Tests

Now that we’ve created a new unit test class, let’s go ahead and run it. Thankfully, we don’t have to do anything else - Gradle will automatically find the new unit test class and execute it along with all the others. So, in a Linux terminal in the java directory, run the following command to execute those tests:

gradle test

When the tests are complete, we can open the report and we should now see that there are 4 tests that executed successfully:

Hamcrest Test Report Hamcrest Test Report

While we’re at it, since we added new code and unit tests we should also check to make sure that our code coverage is still good:

Hamcrest Code Coverage Hamcrest Code Coverage

And that our code passes all style checks by running:

gradle check

And finally we can regenerate our documentation by executing:

gradle javadoc

If you run into any errors on either of those commands, now is a good time to get them resolved before moving on! This is the last step before we submit our code!

Click the link below to jump to the end where we submit our code.

Create GitHub Release

Subsections of Python

Create New Project

YouTube Video

Prior to this course, most of our Python projects consisted of a few source files, all contained in the same directory. However, as projects get larger, it can be very difficult to keep track of all the various source code files. In addition, we’ll want to keep our source code separate from other files, such as unit tests and the output from various tools we use. So, let’s look at how we can create a more professional structure for our Python “Hello Real World” project.

Directory Structure

Python, unlike many other languages, does not really have a standard structure for the source code of a professional project. There are many reasons for this, but most notably is Python’s focus on being completely flexible and not enforcing any particular structure upon developers. However, this flexibility can make it difficult for Python developers to move between projects or organizations if they use vastly different project structures.

So, in this course, we’re going to build a directory structure that is similar to those used by other object-oriented programming languages such as Java and C#. This structure may also be found in many open source projects written in Python.

Source and Test Folders

First, find the python folder in the Codio file tree to the left. We’ll create three folders inside of that folder:

  • src - this folder will store the source code for our application.
  • test - this folder will store the unit tests for our application.
  • reports - this folder will store reports generated by various tools we’ll use later in this module.

Once you’ve created those three folders, you should see a structure similar to this:

Python Folder Structure Python Folder Structure

As we work through this example, we’ll slowly populate these folders to build our application.

Creating a Package

Next, let’s create a package to store our Python source code. We’ll discuss what a package is later in this course, but in essence packages are a way to organize large programs by grouping similar classes together.

In Python, to create a package we must simply perform two steps:

  1. Create a directory in our src folder to store the Python source files, or modules that will be in this package
  2. Create a __init__.py file in that folder to tell Python to treat this directory as a package.

So, let’s create a package called hello for our application. First, we’ll need to create a directory called hello in the src directory we created earlier, and then we’ll create a blank file called __init__.py in that directory. Once we are done, we should see this structure:

Python Package Python Package

In most cases, the __init__.py file can be left blank. However, it is executed each time the package is used, so we can include some code in that file to help initialize the package. For now, let’s put the following line of code in the __init__.py file in the src/hello directory:

print("In /src/hello/__init__.py")

Later, when we execute our application, this will help us see when the package is loaded and how it is used.

You can read more about creating Python packages and modules in the Python Documentation

Write Hello World

YouTube Video

Now that we’ve created a package for our code, let’s write the code for our “Hello Real World” application. Traditionally, a Python “Hello World” program is a single line of code, but for this example we’ll follow all of the object-oriented standards by creating a class and a method.

Create a Class

First, we need to create a source code file for our application. We’ll place this code inside of the src/hello package we’ve already created. So, let’s create a file called HelloWorld.py in that directory. Once it is created, we should see the following structure:

Python Hello File Python Hello File

Then, inside of that file, we can place the following code:

class HelloWorld:
    @staticmethod
    def main(args):
        print("Hello World")

This code should be pretty familiar at this point. The one thing to notice is that this file does not include a main guard. We do this for a couple of reasons:

  1. If the class is executed directly, it will simply load the class but there isn’t any code outside of the class that would actually be executed.
  2. We will use a different process to start our entire application, which we will detail below.

So, by using this structure, we can actually simplify our code a bit by omitting the main guard!

Make an Application

Next, we’ll need to create a couple more files in order to make our application easily executable. In fact, what we’ll end up doing is making the entire src folder act like a “meta package” that includes all of the packages in the application.

To do this, we’ll need to create two more files directly inside of the src folder:

  • __init__.py - this will make Python treat the entire src directory as a package
  • __main__.py - this will allow Python to execute that package directly as an application

Once those files are created, we should have a structure similar to this image:

Python Meta Package Python Meta Package

Then, we need to populate those files with some code. So, in the __init__.py file in src, enter the following code:

print("In /src/__init__.py")

As before, this will just allow us to see when the package is loaded to help us understand how everything works together.

In the __main__.py file, we’ll put the following code:

import sys
from src.hello.HelloWorld import HelloWorld
print("In /src/__main__.py")
HelloWorld.main(sys.argv)

Hopefully this code is also pretty easy to understand. We’ll import the sys library so we can access the command line arguments, and then we’ll also import our HelloWorld class from the src.hello meta package we created. Finally, we’ll print a message stating which file we are in, and then call the main method of our application, passing along the command line arguments.

The __main__.py file is described in the Python Documentation.

Run Our Application

That’s all we need to make our application usable. Now, let’s see if we can execute it.

To use our application, we’ll need to use the Linux terminal from within the python folder. So, let’s open the Linux terminal and change our directory to that location:

cd ~/workspace/python

Of course, if you are already in the ~/workspace folder, you can just use cd python to get there. In the code above, we include the whole path so that it will always work, regardless of the current working directory.

Once we are in that directory, we can execute our application using the following command:

python3 -m src

That will tell Python to execute the application stored in our src folder as a Python module, or meta package. When we do that, we should receive output like this:

Python Output Python Output

As we can see, our application actually goes through a few steps before it is able to run the main function:

  1. First, Python finds the src meta package, which will reach the print statement in __init__.py. It will then find __main__.py and execute it to run the meta package as a program.
  2. Then, the src.hello package is loaded on line 2 of __main__.py. So, the __init__.py file in that package will be loaded and executed.
  3. Next, we reach the print statement on line 3 __main__.py.
  4. Finally, line 4 of __main__.py executes the main function of our HelloWorld class

There we go! We’ve successfully built and run our application using Python! If you want, you can test different messages in HelloWorld.py to make sure the program is working correctly.

Pycache Folders

When Python code is executed, the Python interpreter creates a “compiled” version of the code and stores it in a folder called __pycache__. Those folders can be safely ignored, but they may appear in various directories as you develop and test your application. Later in this module we’ll discuss how to omit those directories from version control applications such as Git.

You can read more about this process in the Python Documentation.

Git Commit & Push

YouTube Video
tl;dr

If you are familiar with using Git, here is the short version:

git status
git add .
# check that the correct files are added
git status
# update the commit message below
git commit -m "Commit Message Here"
git push

That will commit and push your changes to GitHub, which can now be found in the repository for this assignment.

At this point, we’ve written some code for our application. Now is a great time to save that code to our git repository and push it to GitHub in the cloud. By doing so, we’ll make sure that our code is not accidentally lost, but we can also make it quick and easy to restore a previous version of our code at any time.

Git

At this point, you might be pretty confused about Git, GitHub, repositories, cloning, and everything that we’ve done so far. That’s fine! We’ve found that one of the best ways to learn how to use Git and GitHub is simply by doing it and observing what it does, then slowly building up your knowledge by trying new things. However, let’s take a few minutes to explain some of the basics so you know what we are talking about.

Git is the name of a “distributed version control system” that was created initially by Linus Torvalds (the originator of the Linux operating system kernel). It was meant to solve a lot of the problems that he experienced when working with other version control systems of the time, such as Subversion and Mercurial.

Git stores data in several different places, and there are several Git commands to transfer the data between those places. It is best summarized in this diagram:

Git Workflow Git Workflow^[https://commons.wikimedia.org/w/index.php?title=File:Git_data_flow_simplified.svg&oldid=511614601]

Let’s look at Git from the bottom up and see how it works.

Working Directory

For this project, our working directory in Git is the python folder where we’ve been storing all of our code. This is the folder that we cloned our initial repository from GitHub Classroom into, and its where we’re actually doing all of the coding. At this point, we’ve created several new files and directories, but we’ve not added them to our local Git repository yet. Before we can do that, we must discuss one other thing - the .gitignore file.

.gitignore File

First, we’ll need to create a special file called .gitignore in the python directory, or in the root of our working directory for Git. That file contains information that tells Git what files we want to “ignore” - files that Git shouldn’t try to save. Typically we only want Git to store the source code for our program, and not any compiled classes, reports, or temporary files.

Python Gitignore Python Gitignore

Next, let’s place the following content into our .gitignore file:

.mypy_cache
.tox
reports
.coverage
__pycache__/

These are simply the names of files or folders, but may also include wildcards like an asterisk * as well. Anytime we want to ignore anything in our project we can just add its file or folder name or path to this file, and Git will dutifully ignore it.

Screenshot Inconsistencies

Some of the screenshots in this section were taken later in the creation of this project, so the exact files listed may be a bit different in your version. That’s fine - just use these as a baseline for the overall look and feel of the process, and feel free to ask questions if you aren’t sure things are correct.

Adding to the Index

The next level of Git is the index, sometimes referred to as the stage. The index contains a list of files that we’ve modified and would like to store in our local repository. We can add and remove items from the index at any time, and it won’t affect our working directory.

To see what is in the index, we can use the following command from within the python folder:

git status

You should see output similar to this:

Python Git Status Python Git Status

At this point, all of the files are “untracked” and listed in red, which means that they aren’t part of the index. So, we need to add files to the index before we can commit them. To do that, we can use the git add command, followed by the files we’d like to add to the index. Of course, naming each file individually can be time consuming, so we can use the following shortcut to add all of the untracked files in our working directory to the index:

git add .

In Linux, a single period . on the command line refers to the current working directory, so in this case it will add all of the files in the python folder to the Git index. When we run that command, we won’t see any output. But, if we run this command again:

git status

We should now see a whole bunch of files listed as “Changes to be committed”:

Python Git Index Python Git Index

Those files listed in green are now “staged” and ready to commit to our local repository. So, before we move on to the next step, we’ll want to make sure that all the files we changed are included in the list of files to be committed. In this case, we see our HelloWorld.py file, as well as the other file we modified in the previous step.

Committing to the Local Repository

Once we’ve added the new and updated files to the index, the next step is to commit, or save, those files in our local repository. This will permanently^[There are ways to undo a commit once it is made, but in general it is poor practice to do so unless a major mistake has been made. For now, we’ll consider it permanent.] save these changes in our local repository, helping us keep track of the changes we’ve made to the code.

To commit to the local repository, we will use the git commit command. When committing to a repository, you are required to include a message describing the changes you made to the code. At this point, the commit message is just for your use, but it is always a good idea to make your commit messages very concise and descriptive so that it is helpful for you later on if you need to go back and restore this version of the code.

There are two ways to use the git commit command. Let’s briefly cover both of them, just so you can choose your preferred method.

Using Nano

The first method just calls for you to execute the following command:

git commit

When you do, your terminal will open a text editor program called Nano, which you can use to place your commit message at the top of the file. It will look something like this:

Python Nano Python Nano

As you can see, we’ve already entered the message “Commit Message Here” at the top of the file. Once we’ve done that, we need to save and close the file. To do that, follow these steps:

  1. Press CTRL+X to exit Nano. That will open a message at the bottom that says “Save modified buffer?”
  2. Press Y to respond “yes”. That will change the message to “File Name to Write:”
  3. Press ENTER to save the file using the name given.

Using the Command Line

Commit messages can also be specified directly on the terminal using the -m command line option. For example, if we wish for our commit message to be “Commit Message Here” we would use the following command:

git commit -m "Commit Message Here"

This is a quick and easy way to add a commit message to our commit without using a text editor.

Successful Commit

Once we’ve specified our commit message, Git will commit our changes to the local repository. We should see output similar to this if it was successful:

Python Commit Success Python Commit Success

If so, we’ve just made our first commit to our local repository. Now, if we ever need to restore our code back to that point, we can! As we continue to work with Git in this course, we’ll explore some of these more advanced features. For now, we’ll just us it to track our work.

Pushing to a Remote

The last step we can take is to push our local repository to a remote location, called a remote. GitHub is a great example of a remote that works with Git, but there are many others as well. For this course, since we are using GitHub Classroom, we’ll be using GitHub as our remote.

Thankfully, because we’ve already cloned this assignment from GitHub Classroom, we don’t have to do anything to be able to use it. In Git, cloning is the term we use for the first time we download a remote repository and store it on our computer. Once we’ve downloaded it the first time, we can get access to any changes by pulling from the remote.

So, to push a copy of our local repository to the remote, we can run this command:

git push

We should then get output that looks like this:

Python Git Push Python Git Push

There we go! We’ve now pushed our code to GitHub. Now, let’s go check it out. So, navigate to your repository on GitHub classroom, and refresh the page if needed to see your changes. For me, it now looks like this:

Python GitHub Python GitHub

Now all of our code is visible on GitHub, and we can see our commit message next to all of the files that have changed. As we continue to make commits and push them to GitHub, we can use this website to explore the different versions of each file. In addition, the instructors for this course can explore your repository and help you resolve errors and assign grades based on your work here.

At the end of this example project, you’ll create a release on GitHub to signify that you are done with the assignment. We’ll cover how to do that later in this example. For now, let’s forge ahead and learn about unit tests.

Unit Tests

YouTube Video

At this point, we’ve written the basic code for our “Hello World” program. However, how can we automatically verify that the source code works properly? To do that, we’ll need to create some unit tests that verify our code.

pytest

There are many different libraries that can be used to create unit tests for Python. Python itself includes a package called unittest that can serve this purpose. However, many Python developers choose to use an external application called pytest for unit testing. There are many reasons for this, some of which are discussed in this blog post.

To use pytest, we’ll need to install it.

Installing External Python Libraries

Before we do that, let’s take a step back and discuss installing external libraries via Python. One of the major selling points of the Python programming language is the number of external libraries that can be easily installed. These range from simple game engines all the way to popular scientific and machine learning libraries such as SciPy and scikit-learn.

Thankfully, nearly every external library available for Python can be downloaded and installed from the Python Package Index, also known as PyPI. To do this, we use a tool that is included with Python called the Package Installer for Python or pip.

So, when we find an external library we’d like to use in Python, usually all we have to do to install it is to find the library’s name on PyPI, and then use the pip tool to install it. In fact, most libraries include those instructions as part of their documentation, as we can see in the Installation Instructions for pytest.

Install pytest

So, to install pytest, we should run the following command:

pip3 install pytest

Notice that this is a little bit different than the command given in the pytest Installation Instructions. First, we will use the command pip3 instead of pip to ensure that we are installing pytest for Python version 3. If we use the pip command instead, it will install pytest for Python 2, which we don’t want.

Additionally, the -U command line flag shown in the documentation simply tells pip to upgrade the package if it is already installed. Since we know it isn’t installed in this case, we’ll just omit it.

When we execute that command, we should get output similar to the following:

Install Pytest Install Pytest

The pip tool will automatically install any additional libraries that are required for pytest, so we might see several items listed. At the bottom of the output, it will list all of the libraries installed. Since we see pytest in that list, we have successfully installed it.

Linux Command Prefixes

One point of confusion for many new developers is the inconsistent way Linux terminal commands are denoted in the documentation of various tools. For example, in the pytest documentation, Linux terminal commands are sometimes, but not always, prefixed by a dollar sign $:

Dollar Sign Dollar Sign

In other documentation, you might see either a pound sign # or a greater than sign >, as seen in this example:

Arrow Prefix Arrow Prefix

However, when we execute those commands, we shouldn’t include the greater-than symbol or dollar sign. Why is that? Many places that include documentation for the Linux terminal helpfully include either the last symbol of the command prompt before the command, or some other symbol like an arrow, indicating that this is a command to be entered in the terminal. Linux typically uses two different types of command prompts:

  • Dollar sign $ - indicating that the terminal is logged in to a normal user account
  • Pound sign # - indicating that the terminal is logged in with administrator permissions (root)

Documentation also often uses the > symbol in front of commands to be entered by the user. So, in general, when you see a Linux terminal command in documentation that starts with a >, $ or #, you should omit that character when executing the command. This can be very confusing and frustrating to get used to, especially since it is not standardized. As we’ll learn later in this course, standardized documentation is a very important part of being a good programmer!

Create Unit Test Package

Now that we’ve installed pytest, let’s create a unit test for our “Hello World” application. As you might guess, we’ll use the test directory we created earlier to store all of our unit tests. It is typically good practice to have packages in our unit tests that match the packages in our source code, so we’ll start by creating a package named hello in that directory. To do that, we’ll need to do the following:

  1. Create a folder named hello in the test folder
  2. Create a file named __init__.py in the new test/hello folder
  3. Place the code print("In /test/hello/__init__.py") in the test/hello/__init__.py file

Once we are done, we should see the following structure in our python folder:

Python Unit Test Structure Python Unit Test Structure

Write Unit Test

Next, let’s create a file called test_HelloWorld.py in our test/hello package. Our file structure should now include that file:

Python Unit Test File Python Unit Test File

Let’s put some code in the test_HelloWorld.py file to test our “Hello World” application. Here’s a sample of what a unit test for this might look like:

from src.hello.HelloWorld import HelloWorld


class TestHelloWorld():
  
    def test_hello_world(self, capsys):
        HelloWorld.main(["HelloWorld"])
        captured = capsys.readouterr()
        assert captured.out == "Hello World\n", "Unexpected Output"

We can break this code down line by line to discuss what it does and how it works.

  1. from src.hello.HelloWorld import HelloWorld - in our unit tests, we can import our other packages using the src meta package we created earlier
  2. class TestHelloWorld(): - unlike Java, Python allows us to create classes with names that differ from the filename. For unit tests, our class names should begin with the word Test
  3. def test_hello_world(self, capsys): - Likewise, our test methods should begin with test_. The capsys argument tells pytest that we’d like to capture output printed to the terminal. This is documented in the pytest Documentation.
  4. HelloWorld.main(["HelloWorld"]) - this line executes the main method of our HelloWorld class. We must provide a list of strings that represent the expected command line arguments for the program. Recall that Python always includes the name of the script as the first command line argument, so we should simulate that in our unit tests. More about that convention can be found in the Python Documentation
  5. captured = capsys.readouterr() - this allows us to get the output printed to the terminal by our program. See the pytest Documentation.

Then, we reach the most important line of the program: assert captured.out == "Hello World\n", "Unexpected Output". This line is called an assertion, and it is the basis of most unit tests. In this line, we are stating that the output we receive from the program, stored in captured.out, should exactly match "Hello World\n" in order for the test to pass. If it doesn’t, then we can share a helpful error message, in this case “Unexpected Output” when the test fails.

Typically, most unit tests contain several of these assertions, and there are many different assertion methods we can use, but for right now we’ll just use the assert method.

There we go! That’s all it takes to write a simple unit test. However, as you’ve probably noticed, the code for a unit test is much more complex than the code that we are testing. That’s actually pretty typical - many times it is much more difficult to write tests that ensure the code works than writing the code itself. However, the usefulness of those tests will often outweigh the cost, as it can be even more costly to constantly fix bugs and errors that would have been caught by a proper set of unit tests.

pytest Naming Conventions

One important aspect of the pytest tool is how it uses naming conventions to easily find files that contain unit tests. The naming conventions are fully described in the pytest Documentation. In short, any Python file that matches either test_*.py or *_test.py will be included. For this course, we’ll use the test_*.py convention.

Then, inside of those files, it will look for classes that begin with the name Test, and methods inside of that class that begin with test. This is an example of a convention that dictates how the program functions. So, we’ll need to make sure our unit tests follow this pattern.

Running Unit Tests

Now that we’ve written our unit tests, let’s see if we can execute them. First, we’ll need to open a Linux terminal. Then, we’ll need to navigate to the python directory. Once we are there, we can run pytest using the following command:

pytest

When we do, we’ll probably get output that looks like this:

PyTest Error PyTest Error

If we read this error closely, we’ll see that it is unable to find our src.hello.HelloWorld class. That’s strange - we were able to find it earlier. Let’s dive into that and see what’s going on.

Since pytest is installed as a library in Python, there is another way we can execute it. Let’s try that and see what happens:

python3 -m pytest

When we do that, we’ll see that our tests work properly:

PyTest Works PyTest Works

What’s going on here? Well, this gets a bit complicated, but in short the pytest command does not include the current directory as part of the PYTHONPATH, which is a set of locations where Python should look for additional code. However, when we launch pytest using the standard python3 command, Python will add the current directory to the PYTHONPATH. That means that our src meta package will get included, and pytest is able to find it.

Thankfully, there are a number of ways to fix this. The simplest is actually a bit of a “hack” that involves creating a blank file named conftest.py in the src package. This will tell pytest to include this directory in the PYTHONPATH correctly. This is well described in this StackOverflow Post So, let’s go ahead and create that file:

Python Conftest Python Conftest

Then, in that file, we should put a note that explains what’s going on. So, let’s put the following comment in that file:

"""Sample test configuration file.

This file is used to configure testing parameters for PyTest. It is included
as a hack to get PyTest to recognize and include this directory in the Python
path. See https://stackoverflow.com/q/34466027.

Author: Russell Feldhausen russfeld@ksu.edu
Version: 0.1
"""

That will help us remember what that file is for.

Now, let’s try to run our tests again using this command:

pytest

If everything is configured correctly, we should see output similar to this:

Pytest Success Pytest Success

That means our unit tests are working! However, we aren’t getting any useful feedback beyond the number of tests that were executed and whether they succeed.

Pytest Cache

You might also have noticed a .pytest_cache directory appear after running pytest for the first time. This is simply a folder that pytest uses to cache data that it needs, and you can safely ignore it.

Unit Test Reports

Thankfully, we can install another Python library, pytest-html that will allow us to create HTML reports from our unit test results. So, in a Linux terminal, we can install that library using pip with the following command:

pip3 install pytest-html

Then, from within the python directory, we can run our tests using this new command to generate a report:

pytest --html=reports/pytest/index.html

In that command, we have specified that the report should be created in the reports/pytest/index.html file. So, after running that command, we should see the following folder structure:

PyTest HTML PyTest HTML

So, find that file in the Codio file tree to the left and open it. When you do, you’ll see a file full of HTML like this one:

Test Report HTML Test Report HTML

That’s really difficult to read, isn’t it? Thankfully, we can tell Codio to open that HTML file as a webpage by right-clicking on it and selecting Preview Static:

Preview Static Preview Static

If done correctly, you should see a webpage that looks like this:

Test Report Test Report

Hopefully, we should see that our code passed the test!

On the next page, we’ll explore how to expand the usefulness of our unit tests and automate this process a bit more.

Install Tox

YouTube Video

Working with Python on the Linux terminal can be complicated, especially as projects become larger and larger. Thankfully, there are many tools that have been developed to simplify this process. For this course, we’re going to use one such tool, called tox.

Tox

tox is a tool that is developed to help automate and standardize the process of testing applications written in Python. It vastly simplifies many of the steps we’d have to follow to do this in our own project.

tox works by creating virtual test environments and then running our code within those environments. This allows us to test our program using a clean copy of Python with the version and libraries we specify.

The main reason we’ve chosen to use tox in this course is the ease with which it can be used, and the fact that it is available directly as a Python library.

Install tox

To install tox, we can simply install it using pip. So, in a Linux terminal, enter the following command:

pip3 install tox

That’s all it takes! As we’ll quickly learn, using pip to install Python libraries is quick and easy to do.

Creating a Requirements File

One of the things that we’ll need in order to use tox is a requirements file. A requirements file simply lists the libraries that we’ve installed via pip as part of this project. Then, if we decide to work on this project either on a different computer or within a virtual test environment, like the one we’ll create with tox, we can easily reinstall all of those libraries again. You can learn more about requirements files in the pip Documentation

To create a requirements file, navigate to the python directory in the Linux terminal, and then run the following command:

pip3 freeze > requirements.txt

That should create a file called requirements.txt in the python directory. When we open it, we should see contents similar to this:

Requirements File Requirements File

As we can see, there are a lot of libraries already installed - many more than we installed manually. This is because the pip3 freeze command will list all libraries installed, even if they were installed as a dependency for another library.

There are some pros and cons to this approach:

  • Pro: Listing all of the libraries makes it easy to see exactly what is required, including the specific versions
  • Con: It is difficult to tell which of the libraries are required, and which ones are simply dependencies

Because of this, we’re going to create our own requirements file that just lists that basic libraries we need. This will make it easier for us to keep track of the libraries we are using for this application.

So, go ahead and open up the requirements.txt file in the python directory, and replace it’s contents with the following:

pytest
pytest-html
tox

Then, whenever we need to reinstall the requirements for our program, we can use the following command when we are in the python directory:

pip3 install -r requirements.txt

tox will also use this requirements file to set up our test environment, as we’ll see in the next section.

tox Configuration

Next, we’ll need to create a configuration file for tox. The tox configuration file should be stored directly in the python directory with the name tox.ini. So, we should have the following structure once that file is created:

Tox Config File Tox Config File

The basics of creating a configuration file for tox are detailed in the tox documentation. For now, we can use a quick starter file. So, place the following contents in the tox.ini file:

[tox]
envlist = py310
skipsdist = True

[testenv]
deps = -rrequirements.txt
commands = python3 -m pytest --html=reports/pytest/index.html

Let’s go through the settings in this configuration file to understand what it does

  1. [tox] - the [tox] entry is the header for a section of the file. In this case, it stores the global settings for tox.
  2. envlist = py310 - this tells tox to only use Python version 3.10 for testing. If we want tox to test our program using multiple versions of Python, we can add them to this list.
  3. skipsdist = True - this tells tox to skip the step of installing our application in the test environment. We do this because we haven’t set configured our application to be installed. We’ll cover how to do this later in this course.
  4. [testenv] - this section defines the test environments that tox will create
  5. deps = -rrequirements.txt - this tells tox to install the requirements listed in the requirements.txt file. We include a -r at the beginning to match the pip command. There is no space between -r and requirements for some reason.
  6. commands = python3 -m pytest --html=reports/pytest/index.html - this is a list of command that tox should execute to test our application. We’ll use the python -m pytest method to execute pytest here, since that will better match the commands we’ll add to this file as we continue to build our project.

That’s a very basic configuration file for tox, but it allows us to begin automating the process of testing our code.

Running tox

Now that we’ve configured tox, let’s try to run it. Thankfully, running tox is super simple. In a Linux terminal that is in the python directory, simply run this command:

tox

If everything is working correctly, we should get the following output:

Tox Output Tox Output

When we execute tox, it performs a few tasks:

  1. tox will create a virtual environment for Python 3.10 in the .tox folder it creates. This allows it to run tests in isolation, away from the version of Python we are using in Codio.
  2. It will then install all of the requirements listed in the requirements.txt file. This makes sure our application and unit tests can execute.
  3. Then, it will run all of the test commands in the commands list in tox.ini. Right now there is just one command, so it will run our unit tests with pytest.

The first time we run tox it may take a little while to set up the virtual environment. Thankfully, subsequent runs will be much faster since the virtual environment is already set up.

However, if we ever want to force tox to completely reset the virtual environment, we can just delete the .tox folder it creates.

Now that we have successfully set up and configured tox, let’s explore some of the other tools we can use with tox.

Code Coverage

YouTube Video

We’ve now written our program, as well as a unit test that runs our program and make sure it works. But, how can we be sure that our unit tests are adequately testing every part of our program? For that, we have to rely on another tool to help us calculate the code coverage of our unit tests.

Install Coverage.py

Thankfully, there are many easy to use tools that will compute the code coverage of a set of tests. For Python, one of the most commonly used tools is the aptly-named Coverage.py. Coverage.py is a free code coverage library designed for Python, and it is easy to install.

As we’ve already learned, we could easily install it using pip. However, since we are now using tox and a requirements file, we need to make sure that we update our requirements file as well. One easy way to do that is to just update the requirements file to include the new library, then use pip to make sure everything is installed properly.

So, let’s open requirements.txt and make sure it now includes the following content:

coverage
pytest
pytest-html
tox

Once we’ve updated our requirements file, we can then install it by opening a Linux terminal, going to the python folder, and then using pip to install everything from the requirements list:

pip3 install -r requirements.txt

As you might guess, the -r command line argument for pip3 will allow us to install the requirements listed in a requirements file. Once we run that command, the last line of output will list the packages installed, and we should see that Coverage.py is now installed:

Install Coverage Install Coverage

Compute Code Coverage with Coverage.py

Now that we’ve installed and configured Coverage.py, let’s execute it and see what happens. Coverage.py uses a two step process to compute the code coverage from a set of tests - first we must execute the tests using the Coverage.py tool, then we can use another command to generate a report. So, from within the python folder, we can run the following command:

python3 -m coverage run --source src -m pytest --html=reports/pytest/index.html

This command is getting pretty complex, so let’s break it down:

  • python3 - as always, we are running these commands using Python 3. From here onward, we’ll run each library as as module in Python instead of running the commands themselves.
  • -m coverage run - we want to execute the Coverage.py run command. We use -m to tell Python that we are executing the Coverage.py library as a Python module
  • --source src - this tells Coverage.py where our source code is located. It will compute the coverage only for files in that directory
  • -m pytest - we also have to tell Coverage.py how to execute the tests, so we include a second -m followed by pytest. Basically, we are taking our existing command for pytest and adding a few bits in front of it for Coverage.py
  • --html=reports/pytest/index.html - as we saw earlier, this will tell pytest to create and store a report of the test results.

When we execute that command, it will tell Python to run our unit tests and generate a test report. Since we are now using Coverage.py to compute code coverage, we’ll also see a new file appear, named .coverage:

Dot Coverage File Dot Coverage File

This file contains the data the Coverage.py collected from the unit tests. If needed, we can run multiple sets of unit tests and combine the data files using other Coverage.py commands. However, for now we won’t worry about that.

Once we’ve run our unit tests, we need to run one more command to generate a report. So, once again from within the python directory, run the following command:

python3 -m coverage html -d reports/coverage

The coverage html command will generate a report, and the -d command line option sets the directory where the report will be stored. Once we execute this command, we should see the coverage directory structure appear in reports:

Coverage Report Structure Coverage Report Structure

Inside of that folder is another index.html file. So, let’s right-click it and select Preview Static to open it as a webpage. Hopefully we should see something like this:

Coverage Report Coverage Report

While our test only reports that it achieved 56% code coverage, we can see that it is because the __main__.py file was not executed. If we look at the other source files, we’ll see that we achieved 100% code coverage with our tests! That’s the goal, though it was pretty easy to achieve when our application really only contains one line of code. By clicking the links on the page, we can even see which lines are tested by our program, as shown below:

Coverage Highlight Coverage Highlight

Code Coverage in Tox

Now that we have our Coverage.py library working, let’s update our tox configuration file to allow us to run those commands automatically via tox. All we have to do is open tox.ini in the python folder and update the commands section at the end of the file to look like this:

commands = python3 -m coverage run --source src -m pytest --html=reports/pytest/index.html
           python3 -m coverage html -d reports/coverage

Notice that those are the exact same commands we used earlier to execute our tests and generate a report using Coverage.py. That’s one of the most powerful features of tox - you are able to use the same commands within tox that you would use to manually execute the program.

Once we’ve updated tox.ini, let’s run it once to make sure it works. This time, since we’ve installed a new requirement, we’ll need to tell tox to rebuild its environment by using the -r command line flag:

tox -r

That will tell tox to completely rebuild its virtual environment and reinstall any libraries listed in the requirements file.

We should once again be able to see tox execute our tests and generate a report:

Tox Rebuild Tox Rebuild

More Complex Code

Let’s modify our application a bit and see how we can use Coverage.py to make sure we are really testing everything our application can do. In the HelloWorld.py file, found in src/hello, replace the existing code with this code:

class HelloWorld:
    @staticmethod
    def main(args):
        if len(args) == 2:
            print("Hello {}".format(args[1]))
        else:
            print("Hello World")

This program will now print “Hello World” if executed without any command line arguments, but if one is provided it will use that argument in the message instead. So, let’s run our program again using this command from within the python folder:

tox

Once the tests have finished, we can open the Coverage.py report stored in reports/coverage/index.html and we should find that it no longer achieves 100% coverage:

Coverage.py Not Full Coverage Coverage.py Not Full Coverage

If we drill down deeper, we can find the lines of code that aren’t covered by our tests:

Coverage.py Missing Lines Coverage.py Missing Lines

As we expected, our single unit test is not able to test each and every line of code in our application. That’s not good! So, we’ll need to update our tests to account for the change in our code.

Test-Driven Development

As a quick aside, if we were engaging in test-driven development, we would write the new unit test before changing the code. We won’t model that behavior right now, but it is worth noting that you don’t have to do these steps in the order presented here.

Update Unit Tests

So, let’s update our unit tests to account for this new code. There are a couple of ways we can do this:

  1. We can add more code to our existing test_hello_world method to call the method multiple times, both with and without arguments.
  2. We can add additional test methods to test different behaviors.

In general, when working with unit tests, it is always preferred to add additional test methods to test additional functionality in the program. We want to keep our tests as simple and focused as possible, so that we can easily find the source of any errors it finds. If we simply added more code to the existing test, it would be difficult to tell exactly what caused the error. We’ll cover this in more detail when we formally discuss unit testing later in this course.

For now, let’s open the test_HelloWorld.py file stored in test/hello and add the following method to the TestHelloWorld class:

    def test_hello_world_arg(self, capsys):
        HelloWorld.main(["HelloWorld", "CC 410"])
        captured = capsys.readouterr()
        assert captured.out == "Hello CC 410\n", "Unexpected Output"

Notice that this is nearly identical to the previous unit test method - we simply changed the arguments that are provided to the main method, and also updated the assertion to account for the changed output we expect to receive. As discussed earlier, there are things we can do to prevent duplication of code like this in our unit tests, but we won’t worry about that for now.

Once again, let’s rerun our tests using this command:

tox

Once that is done, we can open the JaCoCo report and see if we are back to 100% coverage:

Coverage.py Fixed Coverage Coverage.py Fixed Coverage

If everything is working correctly, we should see that we are back at 100% coverage, and each line of code in our program is tested.

Of course, achieving 100% code coverage does not mean that you’ve completely tested everything that your application could possibly do - it simply means that you are at least testing every line of code at least once. It’s a great baseline to start with!

Git Commit and Push

This is a good point to stop and commit our code to our Git repository. So, like before, we’ll start by checking the status of our Git repository to see the files we’ve changed:

git status

In that list, we should see everything we’ve updated listed in red. Next, we’ll add them to our index using this command:

git add .

And then we can review our changes using the status command again:

git status

If we are satisfied that everything looks correctly, we can commit our changes using this command:

git commit -m "Unit Tests and Code Coverage"

And finally, we can push those changes to the remote repository on GitHub using this command:

git push

As you can quickly see, this is a pretty short set of 5 commands that we can use to quickly store our code in our local Git repository and on GitHub. We just have to carefully pay attention to the files we commit and make sure it is correct.

Documentation

YouTube Video

The next step in writing good code is adding proper documentation and comments to describe the code and what it does. By writing good documentation, we can pass on helpful information to other developers who need to maintain this code, including our future selves!

Python Docstrings

Unlike most other programming languages, which specify that documentation for classes and functions should be in a large comment above the class or function, Python uses a different documentation structure known as docstrings. A docstring is a comment that is surrounded by three sets of double quotation marks """ and can be found starting on the first line of the class or function declaration.

The Python programming language specification doesn’t include a whole lot of information about the specific structure of docstrings, but there are a few resources that developers can look to for examples. The original docstring format was specified in PEP 257. However, many Python developers prefer to follow the Python Style Guide published by Google, which includes examples and specifications for docstrings.

PEPs

In Python documentation, many times you’ll see references to various “PEP” documents. “PEP” is short for “Python Enhancement Proposal,” and the collected set of PEP documents form the basis for the Python programming language. If you are familiar with the Requests for Comment, or RFCs, that provide the technical information underpinning much of the Internet, PEPs serve a very similar purpose for Python. The full archive of PEPs can be found in PEP 0.

So, let’s explore how to create this documentation for our code.

File Docstring

First, let’s look at a docstring that would be placed at the top of a source code file for Python. This is a sample docstring that we would place at the top of our HelloWorld.py file in the src/hello directory:

"""Sample HelloWorld Program.

This is a sample HelloWorld program to demonstrate proper
Python coding style, testing, documentation, and more.

Author: Russell Feldhausen russfeld@ksu.edu
Version: 0.1
"""

There are lots of new things in this block of comments, so let’s look at each part individually:

  • Each docstring in Python is surrounded by three pairs of double quotation marks """.
  • Immediately following the opening quotation marks, we include a short description for the file. It should be a single line summary of the file, with a period at the end.
  • After the summary, there should be a single blank line.
  • Then, any additional paragraphs can be included to further explain the code.
  • While not required as part of the Google style guide, it is a good practice to place both the author and the version in this docstring, as shown in the example above.

In most cases, it is recommended that every Python source file contain a docstring at the top of the file that describes its purpose. So, let’s look at a few other docstrings we may want to include in our program:

src/__init__.py

"""Meta package for all project packages.

This is the __init__ file for this package.

Typically this file can be left blank, but for this example we have
included a print statement so we can see what it does and when.

Usage:
    python3 -m src - execute this program (when run from project root).

Author: Russell Feldhausen russfeld@ksu.edu
Version: 0.1
"""

src/__main__.py

"""Sample Main Project File.

This file is executed when the entire src directory is run using Python
and serves as the main entry point for the application.

Usage:
    python3 -m src - execute this program (when run from project root).
    
Author: Russell Feldhausen russfeld@ksu.edu
Version: 0.1
"""

src/conftest.py

"""Sample test configuration file.

This file is used to configure testing parameters for PyTest. It is included
as a hack to get PyTest to recognize and include this directory in the Python
path. See https://stackoverflow.com/q/34466027.

Author: Russell Feldhausen russfeld@ksu.edu
Version: 0.1
"""

src/hello/__init__.py

"""hello Package.

This is the __init__ file for this package.

Typically this file can be left blank, but for this example we have
included a print statement so we can see what it does and when.

Author: Russell Feldhausen russfeld@ksu.edu
Version: 0.1
"""

If you haven’t already, go ahead and include the above docstrings in the files specified. Don’t forget to update the author and version information as needed.

Class Docstring

Next, we should also include a docstring at the beginning of each class. This will include information about the class itself, including any attributes stored at the class level. Here’s an example of a class docstring for our HelloWorld class:

class HelloWorld:
    """Simple HelloWorld Class.

    Prints "Hello World" to the terminal when the main function is executed.
    """
    # code goes here

It follows the same structure as the file docstring - a one line description first, followed by a period and a blank line, then additional paragraphs as required.

Function Docstring

Finally, we should also include a docstring at the beginning of most functions. Here’s an example of a docstring for the main function in our HelloWorld class:

    @staticmethod
    def main(args):
        """Prints a hello message.

        This method prints the standard "Hello World" message to the terminal.

        Args:
            args: The command-line arguments provided to the program.
        """
        # code goes here

Notice that this docstring has a section named “Args:” that describes the parameters for the method. The Google Style Guide describes three sections that could be included in a function docstring:

  • Args - list and describe each parameter by name
  • Returns - describe the value returned by the function
  • Raises - list all exceptions which could be raised by this code and why

Feel free to refer to the Google Style Guide for examples as well.

Other Comments

Finally, individual variables and tricky sections in the source code can also be documented using comments. Typically any attributes or fields stored directly within the class itself are documented in this way. We don’t have any attributes in our current program, so we won’t worry about this part for now.

We’ll discuss the creation of Python docstrings in more detail later in this course. For now, feel free to refer to these resources for additional information:

Documenting Tests

Let’s briefly look at a documented version of our unit test code as well, just to see what that looks like. Some of the code has been omitted so we can just focus on the comments:

test/hello/__init__.py

"""test package for hello.

This is the __init__ file for this package.

Typically this file can be left blank, but for this example we have
included a print statement so we can see what it does and when.

Author: Russell Feldhausen russfeld@ksu.edu
Version: 0.1
"""

test/hello/test_HelloWorld.py

"""Test Class for HelloWorld.

Author: Russell Feldhausen russfeld@ksu.edu
Version: 0.1
"""

# imports omitted

class TestHelloWorld():
    """Test Class for `src.hello.HelloWorld`."""

    def test_hello_world(self, capsys):
        """Test Method for `src.hello.HelloWorld.main`.
        
        This will test the main method with no arguments.

        Args:
            capsys: PyUnit fixture to capture output.
        """
        # code omitted
        
    def test_hello_world_arg(self, capsys):
        """Test Method for `src.hello.HelloWorld.main`.
        
        This will test the main method with 1 argument.

        Args:
            capsys: PyUnit fixture to capture output.
        """
        # code omitted

Go ahead and place comments similar to what you see above in the appropriate files in the test folder. As before, don’t forget to change the author to your name!

Generating Documentation with pdoc3

One of the most powerful uses of the docstrings is to generate documentation for users and developers automatically based on the comments stored in the docstrings. This can make it much easier for developers to understand how the code works, even without exploring the code itself.

One of the best tools to create documentation for Python is pdoc3. [pdoc3] makes it quick and easy to generate easy to read documentation based on the docstrings in the source code, and it supports Google style docstrings and even markdown!

To install pdoc3, we’ll simply add it to our requirements.txt file:

coverage
pdoc3
pytest
pytest-html
tox

Make sure it is pdoc3 and not pdoc, which is an older version that pdoc3 is based on.

Once it is there, we can install it by going to the python directory in a Linux terminal and running this command:

pip3 install -r requirements.txt

As before, if everything works correctly we should see that pdoc3 was installed:

Install pdoc Install pdoc

Once we’ve installed pdoc3, we can use the following command from within the python directory to generate our documentation:

python3 -m pdoc --html --force --output-dir reports/doc .

Let’s look at that command to understand what it does:

  • python3 -m pdoc - like always, we are running pdoc as a module using python3.
  • --html - this tells pdoc to create HTML documentation.
  • --force - this tells pdoc to overwrite any existing documentation if needed.
  • --output-dir reports/doc - this tells pdoc where to place the completed documentation.
  • . - the period at the end . represents the current directory in Linux. So, it is telling pdoc to generate documentation starting at the current directory, which should be python. This will make sure that both our src and test folders and all the code they contain are included.

Once it has been executed, we should now see a new directory called doc in our reports folder:

Pdoc3 Output Pdoc3 Output

To open the report, we can right-click on the reports/doc/python/index.html file and choose Preview Static. We should see a webpage that looks like this:

pdoc HTML pdoc HTML

We can follow the links on that page to explore the entirety of our project. For example, if we drill down to the main method in the HelloWorld class, we can see the “Args” section and how it appears in the documentation. We can even show the original source code for the method directly from the documentation:

pdoc Main Method pdoc Main Method

As we can see, this documentation would be very valuable to anyone who wants to explore and learn more about our code. So, we’ll need to make sure we always include helpful docstrings in our Python code!

Integrating with tox

Integrating pdoc into tox is simple! We’ll just add the command we used above to our commands list in tox.ini. Once we’ve done that, it should look like this:

commands = python3 -m coverage run --source src -m pytest --html=reports/pytest/index.html
           python3 -m coverage html -d reports/coverage
           python3 -m pdoc --html --force --output-dir reports/doc .

Then, we can execute it by using the tox command. Since we’ve installed a new library and updated our requirements file since the last time we executed tox, we’ll need to use the -r flag so it will rebuild its virtual environment:

tox -r

Once we run that command, it should successfully update our documentation.

Flake8

YouTube Video

Finally, we’re at the point that we’ve written our application, a couple of unit tests, verified that the unit tests achieve a high level of code coverage, and we’ve written helpful documentation comments in our code. That’s a lot of content we’ve written, especially in our source code files. Now let’s see how good our coding style is by checking it using a linter.

Flake8

There are many different tools that can be used to check the style of source code. Python has a very thorough style guide available in PEP 8 that most developers follow. In addition, we’ve already looked at the Google Style Guide for Python. So, we’ll want to find a tool that can help us follow the style guide of our choice.

There are many different linters available for Python. One of the most commonly used tools is called Flake8. Flake8 is a very powerful linter that can be configured to perform all sorts of style checks on our code.

Installing Flake8

As expected, we can install Flake8 by simply adding it to our requirements.txt file. We’ll also add a few additional plugins for Flake8 that allow us to perform additional checks and generate a helpful HTML report. So, let’s update our requirements.txt to look like this:

coverage
flake8
flake8-docstrings
flake8-html
pdoc3
pep8-naming
pytest
pytest-html
tox

This will install several new items:

  • flake8 - the base library for Flake8
  • flake8-docstrings - an extension for Flake8 to validate the structure of docstrings
  • flake8-html - a plugin to create HTML reports of the violations found by Flake8
  • pep8-naming - a plugin to enforce naming conventions described in PEP 8

Once we’ve updated requirements.txt, we can install those libraries by opening a Linux terminal, navigating to the python folder, and running this command:

pip3 install -r requirements.txt

After that command executes, we should see that those libraries are now installed:

Pip install Flake8 Pip install Flake8

Running Flake8

Once we’ve installed Flake8, we can execute it and see what errors are contained in our code. So, to do this, we can use the following command from within the python directory:

python3 -m flake8 --docstring-convention google --format=html --htmldir=reports/flake

Let’s look at this command and see what it does:

  • python3 -m flake8 - as always, we’ll execute Flake8 as a module using Python 3.
  • --docstring-convention google - this will configure the flake-docstrings extension to enforce the docstring format specified in the Google Style Guide for Python.
  • --format=html --htmldir=reports/flake - these two arguments will create an HTML report in the reports/flake directory.

Once we run that command, we’ll probably get a bunch of output that looks like this:

Flake8 Output Flake8 Output

Resolving Flake8 Errors

As we can see, our code has several errors in it. So, let’s look at the errors in our HelloWorld.py file and see if we can resolve them. To view the errors, we can right-click the reports/flake/index.html file and select Preview Static to view it as a webpage:

Flake8 Report Flake8 Report

By clicking on the links on that page and expanding the code, we can clearly see the errors in our HelloWorld.py file:

HelloWorld Errors HelloWorld Errors

It looks like there are two errors:

  • On line 10, it expects to find 2 blank lines but only found 1.
  • On line 27, it expects to find a newline at the end of the file, but didn’t find one.

If we open that file in the Codio editor, we may also see similar errors:

HelloWorld LSP HelloWorld LSP

Thankfully, the Codio editor includes a feature that performs many of the same checks as Flake8, so we can see many of the same messages by looking for yellow or red dots to the left of the code. Of course, Codio isn’t guaranteed to find the same errors as Flake8, so we always have to use the Flake8 tool to be sure we found all the errors.

In this case, we need to resolve two things:

  • The Python coding standard requires 2 blank lines before a class definition if there is anything above it. So, we’ll need to add an extra blank line at line 10. See Blank Lines in PEP 8.
  • Likewise, all Python files should end with a newline. In this case, we see that the last line of the file contains text, so we need to add a newline at the end by pressing enter.

Once we’ve made those changes, we can rerun Flake8:

python3 -m flake8 --docstring-convention google --format=html --htmldir=reports/flake

and then check the report to see if we fixed the problems:

Flake8 Errors Fixed Flake8 Errors Fixed

Yup! Since the file is no longer listed, we are good to go.

One other error that is very common with Python code is this one:

Whitespace in Blank Lines Whitespace in Blank Lines

Our Python style guide requires that blank lines be completely blank, without any whitespace in them. However, the Codio editor (and many other IDEs) will try to be helpful and automatically indent a blank line for us, which causes these errors. The Codio editor even highlights them to tell us that it is wrong, but it still does it:

Whitespace in Blank Lines in Code Whitespace in Blank Lines in Code

So, we’ll also have to remember to completely remove any whitespace from blank lines in our files in order to pass the Flake8 style checker. Before moving on with this example, modify the code in the various Python source files to ensure that everything is able to pass the Flake8 tool. In many professional software development roles, you aren’t even able to save your code unless it passes a style checking tool, so it is a very good habit to get into. In this course, part of your grade will depend on your code following the appropriate style!

Once you’ve resolved all the errors, you should get the following message in the Flake8 report:

Flake8 Good Flake8 Good

Integrating Flake8 with tox

As we’ve already seen, integrating Flake8 with tox is as simple as adding the command to the list of commands in our tox.ini file:

commands = python3 -m coverage run --source src -m pytest --html=reports/pytest/index.html
           python3 -m coverage html -d reports/coverage
           python3 -m flake8 --docstring-convention google --format=html --htmldir=reports/flake
           python3 -m pdoc --html --force --output-dir reports/doc .

In this case, notice that we placed the Flake8 command before pdoc - we want to make sure that our code passes all style checks before generating our documentation. If the Flake8 command exits with any errors, it will stop executing the tests and prevent the documentation from being generated.

As before, we can then rerun tox using the -r flag to reset the virtual environment and install the new libraries:

tox -r

That should allow us to run our tests, check the style of our code, and regenerate our documentation quickly and easily.

Git Commit and Push

This is a good point to stop and commit our code to our Git repository. So, like before, we’ll start by checking the status of our Git repository to see the files we’ve changed:

git status

In that list, we should see everything we’ve updated listed in red. Next, we’ll add them to our index using this command:

git add .

And then we can review our changes using the status command again:

git status

If we are satisfied that everything looks correctly, we can commit our changes using this command:

git commit -m "Unit Tests and Code Coverage"

And finally, we can push those changes to the remote repository on GitHub using this command:

git push

There we go! We’ve updated our repository once again.

Type Checking

YouTube Video

One of the major selling points of Python is that it is dynamically typed. This means that the data type of variables is determined at runtime, and a single variable can store multiple data types throughout the execution of the program.

While this can make development seem quick and easy, it can also cause programmers to make mistakes related to the handling of various data types that wouldn’t be present in statically typed languages such as Java, C++, or C#. Those languages require compilation before they can be executed, and one step that the compiler performs is type checking. Type checking is a process that makes sure each value that is ever assigned to a variable has the correct type - otherwise the program won’t compile properly.

Thankfully, with the addition of type hinting in Python, we can use a tool to perform type checking on our code as well. Let’s see what that would look like!

Type Hinting in Python

Let’s review our existing code in HelloWorld.py and see what it would look like with type hints added. The code is shown here without any docstrings just to make it easier to read:

class HelloWorld:

    @staticmethod
    def main(args):
        if len(args) == 2:
            print("Hello {}".format(args[1]))
        else:
            print("Hello World")

To add type hinting, we need to add information to each variable and function at a bare minimum. So, let’s look at the function first.

Recall that functions can return a value using the return keyword. We don’t see that in our main function, so it doesn’t return a value, right? In fact, any Python function that doesn’t explicitly return a value within the code actually returns the value None by default. So, we would say that the “return type” of the function main is None

To annotate that with a type hint, we would modify the code to look like this:

class HelloWorld:

    @staticmethod
    def main(args) -> None:
        if len(args) == 2:
            print("Hello {}".format(args[1]))
        else:
            print("Hello World")

Notice that we simply added -> None after the function name and arguments, but before the colon :. That’s all it takes!

Likewise, we should annotate each variable, including all of the function parameters. So, in our function, we are expecting a parameter named args. What type of data would be stored in args?

Recall that args is a stand in for sys.argv, which are the command line arguments provided to Python when the program is executed. sys.argv is a list of strings, so the type of args would also be a list of strings. Therefore, we can annotate it in this way:

from typing import List


class HelloWorld:

    @staticmethod
    def main(args: List[str]) -> None:
        if len(args) == 2:
            print("Hello {}".format(args[1]))
        else:
            print("Hello World")

In this case, we added two things:

  • For some special types, such as collections like lists, we’ll need to import the proper type from the typing library.
  • After each variable is named, we can place a colon followed by the type of the variable. In the case of args, we set the type to List[str], which represents a list of strings.

There we go! We’ve added type hints to our source code. If you want to learn more about how to add type hints to your code, here are some great resources:

Codio IDE and Type Hinting

Unfortunately, the Codio IDE does not properly deal with type hinting in Python, and will tell us that our code contains a syntax error:

Codio LSP Type Hinting Codio LSP Type Hinting

We can safely ignore that error, provided that our code passes the Flake8 style checker itself.

Type Checking with Mypy

Now that we’ve added some type hints to our code, how can we check to make sure our code doesn’t violate those hints? To do that, we can use a static type checker called Mypy. [Mypy] is a powerful tool that allows us to quickly and easily spot typing errors in our Python code. However, it has one major caveat - it will only catch type errors for variables or functions that include type hints. It will not report all type errors in code that does not include type hints, nor will it tell us if some type hints are missing. So, it is up to us as developers to include all of the appropriate type hints ourselves!

To install Mypy, we can simply add the appropriate line to our requirements.txt file. We’ll also install the lxml library, so we can generate HTML reports from Mypy. Once we’ve added those lines, our requirements file should now look like this:

coverage
flake8
flake8-docstrings
flake8-html
lxml
mypy
pdoc3
pep8-naming
pytest
pytest-html
tox

Then, we can make sure those libraries are installed by running this command from within the python directory:

pip3 install -r requirements.txt

As always, once we’ve run that command we can verify that the library was installed by looking at the output:

Install Mypy Install Mypy

Running Mypy

Once we’ve installed Mypy, we can execute it using the following command from within the python directory:

python3 -m mypy -p src --strict --html-report reports/mypy

As always, let’s break this command down into its separate parts:

  • python3 -m mpyp - we want to run the Mypy library as a Python module
  • -p src - this tells Mypy to execute the code in our src meta package, which will include all source code files inside of that directory.
  • --strict - we want to enable all optional checks that Mypy can perform
  • --html-report reports/mypy - this will create an HTML report in the reports/mypy directory

If everything is working correctly, we should get the following output:

Mypy Success Mypy Success

We may notice a new .mypy_cache folder, which can be safely ignored just like the .pytest_cache folder next to it. We can also find an HTML report in reports/mypy/index.html:

Mypy Report Mypy Report

To open that file, simply right-click on it and select Preview Static. When we do that, we should get a report that looks like this:

Mypy HTML Mypy HTML

As we can see, Mypy shows that we’ve properly type hinted all of the code! That’s great!

Integrating with Tox

We can also integrate Mypy with tox by simply adding the command above to the tox.ini file in the commands section, which should now look like this:

commands = python3 -m mypy -p src --strict --html-report reports/mypy
           python3 -m coverage run --source src -m pytest --html=reports/pytest/index.html
           python3 -m coverage html -d reports/coverage
           python3 -m flake8 --docstring-convention google --format=html --htmldir=reports/flake
           python3 -m pdoc --html --force --output-dir reports/doc .

Notice that the Mypy command is now first in the list of commands. This mimics what most other programming languages would do - the code must pass the type checker in the compiler before the unit tests can be executed. So, we’ll do the same with our Python code here.

Once we’ve updated tox.ini, we can rerun tox using this command:

tox -r

That should now run all of our test commands at once!

Type Checking Unit Tests

Now, let’s briefly review how to type check our unit tests. This is a much more difficult task, since our unit tests build upon several external libraries. Thankfully, the Mypy library includes a way for us to explore the types that it is able to infer.

So, in our test_HelloWorld.py file, let’s add the line reveal_locals() to the bottom of one of our test methods. We’ll also need to annotate the function’s return type so that Mypy will analyze it, so we’ll set it to None as we did above. As before, the docstrings have been omitted from this code:

from src.hello.HelloWorld import HelloWorld


class TestHelloWorld():
    
    def test_hello_world(self, capsys) -> None:
        HelloWorld.main(["HelloWorld"])
        captured = capsys.readouterr()
        assert captured.out == "Hello World\n", "Unexpected Output"
        reveal_locals()
Mypy Expressions

The line reveal_locals() is a handy expression for adding type checking to our Python code. However, the Python interpreter itself won’t recognize that line as valid Python code, so we’ll have to remove it before we can actually execute our tests again. We’re just using it temporarily to help us determine the types that Mypy finds for the variables in our code

Now, let’s analyze the code in our test folder using Mypy:

python3 -m mypy --strict test

Notice that we are just giving it the name of the directory test instead of loading it as a Python meta package. This is because we didn’t include an __init__.py file inside of the test directory itself. Don’t worry - Mypy is able to handle it just fine! When we run that command, we’ll see output like this:

Local Variable Types Local Variable Types

In that output, we’ll see that the Mypy library was not able to determine the type of the capsys and captured variables. Instead, it just reports that they could be Any type, which is a special type annotation that matches any possible type. This is due to the fact that we didn’t actually have to import the pytest library to use those variables. This is a powerful feature of Python, but it makes it more difficult to perform proper type checking. It will also complain that we now have method arguments that are missing annotations, so let’s see if we can resolve that.

Read the Source, Luke

Unfortunately, figuring out the rest of this required lots of poking around the pytest source code, specifically in the file for capturing system output. Static type checking in Python is simply more difficult than in other languages because of the way it handles dynamic typing and library imports at runtime.

We’ll go ahead and explore what it takes to properly add type hinting to unit tests here, but you will not be required to be this thorough in your own unit tests in this course. That is simply asking too much!

So, let’s import those libraries by adding them to the top of the file. We’ll start by importing the CaptureFixture class from pytest, as well as the AnyStr type from the typing library. Then, we can annotate the capsys parameter with the appropriate type, which is CaptureFixture[Any]:

from pytest import CaptureFixture
from typing import Any
from src.hello.HelloWorld import HelloWorld


class TestHelloWorld():
    
    def test_hello_world(self, capsys: CaptureFixture[Any]) -> None:
        HelloWorld.main(["HelloWorld"])
        captured = capsys.readouterr()
        assert captured.out == "Hello World\n", "Unexpected Output"
        reveal_locals()

Once that is in place, we can rerun our Mypy check and should get the following output:

Mypy Fixtures Mypy Fixtures

That’s closer! Now Mypy is finding the correct types for capsys and was able to use the information in the pytest library to infer the type of the captured variable, which is returned from the capsys.readouterr() function call. So, let’s annotate that variable as well. To do that, we’ll need to import the CaptureResult class from the _pytest.capture library. As you might guess by the underscore at the beginning of the library name, we are importing a class that is meant to be internal to pytest. This is generally considered bad practice, but it helps for type checking, so we’ll do it for now. Our updated code looks like this:

from pytest import CaptureFixture
from _pytest.capture import CaptureResult
from typing import Any
from src.hello.HelloWorld import HelloWorld


class TestHelloWorld():
    
    def test_hello_world(self, capsys: CaptureFixture[Any]) -> None:
        HelloWorld.main(["HelloWorld"])
        captured: CaptureResult[Any] = capsys.readouterr()
        assert captured.out == "Hello World\n", "Unexpected Output"
        reveal_locals()

Notice that we can easily add a type hint to a variable in the same way we added type hints to method parameters - we just place a colon : after the variable name and then add the type. Now, when we run the Mypy command, we should get the same output, which confirms that we have the correct type hints:

Mypy Result Mypy Result

Once we’ve done that, we can remove the reveal_locals() line to make sure the tests will still execute.

Feel free to add the appropriate type hints to your unit tests if you’d like. However you are not required to include type hints in your unit tests, due to the complexity that we encountered here.

Hamcrest

YouTube Video

Let’s introduce one more useful tool as part of this example, the Hamcrest assertion library. Hamcrest is a library of unit test assertions that is available for multiple programming languages, including both Java and Python. Hamcrest makes it easy to write very advanced assertions in a way that is both readable and flexible. In fact, most of the autograders in prior CC courses use Hamcrest as the primary assertion library to make them easy to develop. Let’s explore what it takes to add Hamcrest to our project.

Installing Hamcrest

To make Hamcrest available, we simply have to add an entry for pyhamcrest to our requirements.txt file. Once we update that file, it will look like this:

coverage
flake8
flake8-docstrings
flake8-html
lxml
mypy
pdoc3
pep8-naming
pyhamcrest
pytest
pytest-html
tox

Then we can install it using this command from within the python folder:

pip3 install -r requirements.txt

That’s all there is to it! We now can use Hamcrest in our unit tests

Unit Test with Hamcrest

Now, let’s build a unit test that uses Hamcrest. So, in the test/hello directory, create a new file called test_HelloWorldHamcrest.py and paste the following code in that file:

"""Test Class for HelloWorld using Hamcrest.

Author: Russell Feldhausen russfeld@ksu.edu
Version: 0.1
"""

from hamcrest.core.assert_that import assert_that
from hamcrest.core.core.is_ import is_
from pytest import CaptureFixture
from _pytest.capture import CaptureResult
from typing import Any
from src.hello.HelloWorld import HelloWorld


class TestHelloWorldHamcrest():
    """Test Class for `src.hello.HelloWorld`."""

    def test_hello_world(self, capsys: CaptureFixture[Any]) -> None:
        """Test Method for `src.hello.HelloWorld.main`.

        This will test the main method with no arguments.

        Args:
            capsys: PyUnit fixture to capture output.
        """
        HelloWorld.main(["HelloWorld"])
        captured: CaptureResult[Any] = capsys.readouterr()
        assert_that(captured.out, is_("Hello World\n"), "Unexpected Output")

    def test_hello_world_arg(self, capsys: CaptureFixture[Any]) -> None:
        """Test Method for `src.hello.HelloWorld.main`.

        This will test the main method with 1 argument.

        Args:
            capsys: PyUnit fixture to capture output.
        """
        HelloWorld.main(["HelloWorld", "CC 410"])
        captured: CaptureResult[Any] = capsys.readouterr()
        assert_that(captured.out, is_("Hello CC 410\n"), "Unexpected Output")

The code is nearly identical to the other unit test class, but with two major changes:

  1. There are a couple of new import statements at the top to include the assert_that and is_ methods from Hamcrest.
  2. Instead of using assert the last line of each unit test uses assert_that. The order of the arguments and the basic idea is pretty much the same. Also, note the use of the is_ method, which is simply stating that it should be equal. That method name includes an underscore to differentiate it from the is keyword in Python.

Of course, a simple test case such as this doesn’t show the power of using Hamcrest instead of the built-in assertions in pyunit. If you want to know more about Hamcrest, feel free to check out the Hamcrest documentation. We’ll explore more about using Hamcrest in our unit tests later in this course.

Running Tests

Now that we’ve created a new unit test class, let’s go ahead and run it. Thankfully, we don’t have to do anything else - pyunit will automatically find the new unit test class and execute it along with all the others. So, in a Linux terminal in the python directory, run the following command to execute those tests, along with the rest of our commands:

tox -r

When the tests are complete, we can open the report and we should now see that there are 4 tests that executed successfully:

Hamcrest Test Report Hamcrest Test Report

While we’re at it, since we added new code and unit tests we should also check to make sure that our code coverage is still good:

Hamcrest Code Coverage Hamcrest Code Coverage

As long as the tox command executes, we also know that the code passed all of our Flake8 style checks, and updated the documentation using pdoc3 as well.

Hamcrest Flake Hamcrest Flake

If you run into any errors on any of those commands, now is a good time to get them resolved before moving on! This is the last step before we submit our code!

Click the link below to jump to the end where we submit our code.

Create GitHub Release

Creating GitHub Release

YouTube Video

The last step in this project is to create a GitHub release containing the completed version of your assignment. This is a common part of the software development process - once we are completely ready to go, we can package our code and make it available for others. We’ll cover the process of actually packaging our code for deployment later in this chapter, but for now we’ll learn to create a release on GitHub that we can submit for this project.

Git Commit and Push

Before we create a release, let’s make sure our code is completely up to date. So, like before, we’ll start by checking the status of our Git repository to see the files we’ve changed:

git status

In that list, we should see everything we’ve updated listed in red. Next, we’ll add them to our index using this command:

git add .

And then we can review our changes using the status command again:

git status

If we are satisfied that everything looks correctly, we can commit our changes using this command:

git commit -m "Unit Tests and Code Coverage"

And finally, we can push those changes to the remote repository on GitHub using this command:

git push

There we go! We’ve updated our repository once again, and are ready to create a release

GitHub Release

To create a release on GitHub, we can click the handy link to the right of our repository that is labeled “Create a new release”:

Create Release Create Release

So, let’s click that link to create a new release. On the page that appears, we have a few things to fill out:

  • Tag Version: GitHub support the use of “tags” which can be assigned to individual commits to make them easy to find. GitHub requires each release to be associated with a tag, so we can create one here. See the discussion below for thoughts on good versioning practice. For now, we’ll use “v0.1.0”
  • Release Title: This is the title for this release. It can be anything, but it is recommended to be easy to understand. In this case, we’ll use “Example 1 Submission”
  • Describe This Release: The description can have more information about the release, such as any known bugs or issues. This is a place where you can add comments for the instructor and anyone else who will be reviewing your releases. For now, we’ll just put “Everything works!”
  • This is a pre-release: Since we don’t intend for this program to be used officially, we should always checkmark this box on our projects.

Once we’ve filled out that page, it will look like this:

Release Ready Release Ready

Finally, we can click the Publish Release button to create our release!

Semantic Versioning

Creating version numbers is actually an important topic to discuss in software development. As we continually make changes to our code, how can we keep track of changes that are compatible with previous versions, or major updates that break old functionality?

One way is through the use of Semantic Versioning. Semantic versioning involves creating a three-part version number that looks like this:

MAJOR.MINOR.PATCH

So, when we used “v0.1.0” in our tag, we are stating that our code is at MAJOR version 0, MINOR version 1, and PATH 0.

Then, following the rules outlined on the Semantic Versioning website, we would increment those numbers following these rules:

  1. MAJOR version when you make incompatible API changes,
  2. MINOR version when you add functionality in a backwards compatible manner, and
  3. PATCH version when you make backwards compatible bug fixes.

So, let’s say we realize we made a mistake in our project and want to resubmit it. When we create that new release, we would use “v0.1.1” to denote that it is a bug fix on a previous release. As we add new features in new projects, we would increment the MINOR version number. Finally, if we completely restructure the project and make it so that old functionality no longer works, we would increment the MAJOR version number.

Throughout this course, we’ll use Semantic Versioning to help us keep track of the progress of our various projects.

Submitting the Release

Finally, we are ready to submit this project! To do that, all we have to do is copy the URL from GitHub that includes our release tag. To find that, we can click on the Tags link at the top of the page:

GitHub Tags Link GitHub Tags Link

Then, we can click on our release tag from the list:

GitHub Tags List GitHub Tags List

That should take us to a page containing the release. From there, we’ll need to copy the URL from our web browser. For me, my URL looked like this:

https://github.com/K-State-Computational-Core/example-1-hello-real-world-russfeld-student/releases/tag/v0.1.0

Notice that it has the “v0.1.0” tag at the end - that’s the URL we need!

Then, we can find this assignment in Canvas, and submit the URL as our project. That’s all we need to do! From there, the instructors and TAs will be able to access our work via GitHub, and we can continue to work in Codio if we need to make a change and resubmit it later.

We may also need to mark one or more items as “done” in the Canvas modules list to get access to the next content.

Milestone Requirements

However, before we get too exited, there is one more page to review. This next page will give you an idea of what the actual milestone requirements for a project in this class might look like. So, review the next page and make sure your project meets the requirements listed before finalizing your submission. You can always create a new release and submit a new entry in Canvas until the assignment is locked.

Assignment Requirements

Sample Assignment

The assignment description below is meant to mimic the structure that you’ll find in the larger project milestones in this course. It clearly describes the overall desired outcome of the project and how it will be graded.

For the example projects in this course, this document will be accompanied by a video or instructions showing you how to complete most of the project. For the project milestones, however, you will only be given this document. It is up to you to apply what you’ve learned to meet the requirements listed.

This page lists the milestone requirements for the Example 1 - Hello Real World project. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This assignment mimics the traditional “Hello World” project that most programmers learn as their first program, but done following professional coding standards and guidelines. In effect, this is how a professional coder would write “Hello World” as a project for work.

General Requirements

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • All projects must include automation for testing, style checking, and documentation generation.
    • Java: Use Gradle with the application, jacoco, and checkstyle plugins.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries.
  • All code must properly compile and be executable.
    • Java: It must compile and execute using Gradle.
    • Python: It must execute using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
  • Where specified, code should contain appropriate unit tests that achieve the specified level of code coverage.
    • Java: Use JUnit 5. You may choose to use Hamcrest for assertions.
    • Python: Use pytest. You may choose to use Hamcrest for assertions.
  • Where specified, code should contain appropriate documentation comments following the language’s style guide.
    • In any class that should be documented, every method in that class should have complete documentation comments.
    • Java: Use javadoc to generate documentation.
    • Python: Use pdoc3 to generate documentation.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

Assignment Requirements

This project should include the following features:

  • A HelloWorld class that contains a main method.
    • The main method should print “Hello World” if no command line arguments are received.
    • The main method should print “Hello {arg}” if a command line argument is received.
  • Unit tests that achieve 100% code coverage in the HelloWorld class, properly testing both with and without command-line arguments.
  • Documentation comments following the language’s documentation standards for each class, method, and any class attributes.
    • Python: All .py files should also include a file docstring, including __init__.py and __main__.py files for packages.
  • All variables and methods in the HelloWorld class must include explicit data types
    • Java: no changes are needed since Java already requires this.
    • Python: add type hints to all methods and variables. Type hints in the HelloWorld class must not use Any as a type.

Time Requirements

Completing this project is estimated to require 2-5 hours depending on familiarity with the tools involved.

Grading Rubric

This assignment will be graded based on the rubric below:

  • HelloWorld class - 30%
  • Unit Tests - 30%
  • Documentation - 20%
  • Automation - 20%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.
  • Any portion of the project that does not pass a style check will have its grade reduced by 30% of the total points available on that portion.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio or mark it as complete in Codio, in case you need to come back to it and make changes later.

Next Steps

Next Steps

This project is a major step toward developing our skills as professional programmers. We introduced a lot of new topics:

  • Unit Tests
  • Code Coverage Tests
  • Documentation Comments and Tools
  • Style Checkers
  • Tools to Automate Everything

In the next assignment, we’ll start building the first major project in this course. In doing so, you’ll end up redoing a lot of this initial setup, so you’ll be referring back to this project over time. So, if anything didn’t work or you ran into issues, now is the time to ask questions and get help. Good luck!

Submitting this Assignment

Remember, you should not mark this assignment as complete via Codio - just submit the GitHub URL for your release via Canvas. By leaving this assignment open, you may return to it later and make changes. In addition, it becomes a good testing ground that you can use to try new code or tools before integrating them into your later projects.

Object-Oriented Programming

In this example project, we’ll go through the steps of creating a couple new packages, an enumeration, and a class within a project. This will give you the background you need to complete the first milestone of the restaurant project.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first. We’ll show you how to clone it into Codio so you can use the starter code.

Good luck!

Subsections of Object-Oriented Programming

Assignment Requirements

This page lists the example project requirements for Example 2 in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover creating new packages, classes, and enumerations within an existing project. Similar to the restaurant project, the examples will cover a smaller subset of those requirements as part of a fictional ice cream shop.

General Requirements

Warning

The first couple of milestones only require a subset of the general requirements introduced in the “Hello Real World” project. Read this section carefully to see what is required for this particular milestone.

This milestone must follow these professional coding standards:

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation and execution.
    • Java: Use Gradle with the application plugin. The project should compile without errors. You may include a main class in a separate package for testing purposes only.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries. You may include a main class in a separate package for testing purposes only.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.109. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

The following requirements ARE NOT enforced for this milestone, but will be enforced in later milestones that use the same code. We will focus on learning to meet each of these requirements in future modules. However, you are welcome to “plan ahead” by minimizing the number of style errors in your code and adding some basic documentation where desired.

Naming Standards

You can make things easier on yourself by following proper naming standards for your language of choice, even though we aren’t enforcing a style guide for this milestone.

  • Java - All names are in CamelCase. Classes start with uppercase, like ClassName, methods and attributes start with lowercase like methodName. See the Google Style Guide.
  • Python - All names are lowercase with underscores like method_name, with the exception of classes, which are named in CamelCase starting with an uppercase letter like ClassName. See the Google Style Guide.

It is easier to get this correct from the start, then having to refactor your code later. Of course, major refactoring is also a good lesson that guarantees you’ll get it right in the future!

  • (Milestone 3) All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
  • (Milestone 2) Where specified, code should contain appropriate unit tests that achieve the specified level of code coverage.
    • Java: Use JUnit 5. You may choose to use Hamcrest for assertions.
    • Python: Use pytest. You may choose to use Hamcrest for assertions.
  • (Milestone 2) Where specified, code should contain appropriate documentation comments following the language’s style guide.
    • Java: Use javadoc to generate documentation.
    • Python: Use pdoc3 to generate documentation.

Assignment Requirements

This milestone should include the following features:

  • Sundae classes - 1
    • Declared in the starfleettreats.data.sundaes package
  • Enumeration classes - 2
    • Declared in the starfleettreats.data.enums package

See the Starfleet Treats Menu section below for descriptions of what each class should contain.

Time Requirements

Completing this project is estimated to require 1 hour.

Grading Rubric

This assignment will be graded based on the rubric below:

  • Sundae classes - 70%
  • Enumeration classes - 30%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.




Sundaes

Each sundae should be stored in an appropriately named class in the starfleettreats.data.sundaes package. Each sundae should include an attribute for the following data:

  • Container - a Container value (see below). It should have a getter and setter method.
  • Toppings - a Java HashSet or a Python set of Topping values (see below).
    • This attribute should have a getter method that returns a shallow copy of the set to prevent external modification. See HashSet’s Copy Constructor (Java) or set.copy (Python).
    • This attribute should also have methods for Add Topping and Remove Topping to modify the list of toppings.

In addition, each sundae should have the ability to return the following data through an appropriate getter method. The data may be stored as attributes or hard coded directly into the method.

Each sundae class should also override the default string representation method (toString() in Java or __str__() in Python) and return a string that properly describes the sundae. The string should be formatted as “{sundae name} in a {container}”, such as “The Classic in a Waffle Cone”.

It should also override the default equality method (equals() in Java or __eq__() in Python). Two items should be considered equal only if the values of all attributes are equal.

Each sundae description will include a list of ingredients included on the sundae. Those ingredients should be represented using Boolean attributes that are set to true by default, with appropriate getter and setter methods. Changing any of these to false will cause a “Hold {ingredient}” message, such as “Hold Vanilla”, to be added to the Special Instructions list. Likewise, changing it back to true will remove the appropriate message. If all ingredients are at their default values, the Special Instructions list should be empty.

Likewise, each sundae description will include a Price, number of Calories, a default value for Container and a default set of Toppings. Those attributes should be populated appropriately in the constructor for the sundae. Changes to the Container and Toppings attributes will not affect the Special Instructions attribute. Likewise, the Price and number of Calories will remain constant, regardless of other attributes.

The Classic (Banana Split)

it doesn’t get more traditional that this

starfleettreats.data.sundaes.TheClassic - The price is $5.50 and it is 1050 calories. Served in a Waffle Cone with Vanilla and Banana. Comes with Chocolate, Whipped Cream, and Cherry.


Enumerations

Each enumeration should be stored in an appropriately named class in the starfleetsubs.data.enums package. Each enumeration should be stored in an appropriately named class in the starfleetsubs.data.enums package. Each enumeration class should also override the default string representation method (toString() in Java or __str__() in Python) and return a string that properly describes the item. Python developers may also wish to override the __repr__() method to return this value as well.

Containers

a vessel to contain the deliciousness

starfleettreats.data.enums.Container - Dish, Cake Cone, Waffle Cone

Toppings

build your own top-notch treat

starfleettreats.data.enums.Topping - Chocolate, Whipped Cream, Cherry, Caramel, Peanuts

Java

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man
  2. Install Gradle
sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle test
gradle check
gradle javadoc
  1. Confirm that project runs, all unit tests pass with 100% coverage, no style errors, and Javadoc generates properly.

  2. Create New Packages, Classes, and Enums. Continuously commit to Git as changes are made!

  3. Update HelloWorld.java to use new classes (optional). This is just for testing purposes.

  4. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Python

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs, all unit tests pass with 100% coverage, no style errors, no type errors, and documentation generates properly.

  2. Create New Packages, Classes, and Enums. Continuously commit to Git as changes are made!

  3. Update HelloWorld.py to use new classes (optional). This is just for testing purposes.

  4. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Unit Testing

In this example project, we’ll go through the steps of creating unit tests for an existing project. This is a great example of white-box testing since we’ll have access to the source code of the application. As we are developing our tests, we’ll also improve the code of the application itself as we find some errors and edge cases that it doesn’t handle very well.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of Unit Testing

Assignment Requirements

This page lists the example project requirements for Example 3 in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover creating adding unit tests to an existing project. For this example, we’ll focus on a simple guessing game.

General Requirements

Warning

The first couple of milestones only require a subset of the general requirements introduced in the “Hello Real World” project. Read this section carefully to see what is required for this particular milestone.

This milestone must follow these professional coding standards:

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation, unit testing, documentation generation, and execution.
    • Java: Use Gradle with the application plugin. The project should compile without errors. You may include a main class in a separate package for testing purposes only.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries. You may include a main class in a separate package for testing purposes only.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
  • Where specified, code should contain appropriate unit tests that achieve the specified level of code coverage.
    • Java: Use JUnit 5. You may choose to use Hamcrest for assertions.
    • Python: Use pytest. You may choose to use Hamcrest for assertions.
  • Where specified, code should contain appropriate documentation comments following the language’s style guide.
    • Java: Use javadoc to generate documentation.
    • Python: Use pdoc3 to generate documentation.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

The following requirements ARE NOT enforced for this milestone, but will be enforced in later milestones that use the same code. We will focus on learning to meet each of these requirements in future modules. However, you are welcome to “plan ahead” by minimizing the number of style errors in your code and adding some basic documentation where desired.

Naming Standards

You can make things easier on yourself by following proper naming standards for your language of choice, even though we aren’t enforcing a style guide for this milestone.

  • Java - All names are in CamelCase. Classes start with uppercase, like ClassName, methods and attributes start with lowercase like methodName. See the Google Style Guide.
  • Python - All names are lowercase with underscores like method_name, with the exception of classes, which are named in CamelCase starting with an uppercase letter like ClassName. See the Google Style Guide.

It is easier to get this correct from the start, then having to refactor your code later. Of course, major refactoring is also a good lesson that guarantees you’ll get it right in the future!

  • (Milestone 3) All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.

Assignment Requirements

This milestone should include the following features:

  • Complete Unit Tests for the GuessingGame class that achieve 100% code coverage and adequately test all aspects of the code.
  • Update GuessingGame class to use an enumeration as a return value in the guess method.
  • Update GuessingGame to properly handle punctuation, uppercase and lowercase, and require a minimum length for the secret.
  • Complete documentation comments in all code files. Checkstyle or Flake8 should not report any missing documentation.
  • Create a README.md file in the root of the project, and describe unit tests you feel should be added to the example to cover untested aspects of GuessingGame. You do not have to write the tests, just discuss aspects you feel are not adequately tested by the tests covered in the video.
  • Create a UML Class Diagram for the entire GuessingGame program (just the source code, you may omit the unit tests). Store the diagram in the root of the project next to README.md as an image file (PNG preferred). You may also wish to include any other files used to create the diagram.

Time Requirements

Completing this project is estimated to require 1 hour.

Grading Rubric

This assignment will be graded based on the rubric below:

  • Updates to GuessingGame class - 10%
  • Unit Tests - 40%
  • Documentation - 30%
  • README.md file discussing additional tests - 10%
  • UML Class Diagram - 10%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.

Java

YouTube Video

Resources

Gradle Changes

In the build.gradle file, the JUnit 5 parameters library was added:

dependencies {
    // Use JUnit Jupiter API for testing.
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2', 'org.hamcrest:hamcrest:2.2', 'org.junit.jupiter:junit-jupiter-params'

    // Use JUnit Jupiter Engine for testing.
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:29.0-jre'
}

Also, we added a quick section at the bottom to allow Gradle tasks to read input from System.in:

// Allow run to read from System.in
run{
    standardInput = System.in
}

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle test
gradle check
gradle javadoc
  1. Confirm that project runs, no style errors, and Javadoc generates properly.

  2. Create New Unit Tests and Update Code as Needed. Continuously commit to Git as changes are made!

  3. Add Documentation Comments. Continuously commit to Git as changes are made!

  4. Add README.md and discuss unit tests you feel should be added to adequately test the GuessingGame class.

  5. Create a UML Class Diagram for the source code and include it in the project.

  6. Confirm that project runs, no style errors related to comments, and Javadoc generates properly.

  7. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Python

YouTube Video

Resources

Tox Changes

In the tox.ini file, we added the following line under the [testenv] heading:

ignore_errors = True

This will allow the full Tox script to execute, even if there are errors earlier in the process.

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs, all unit tests pass, no style errors, no type errors, and documentation generates properly.

  2. Create New Unit Tests and Update Code as Needed. Continuously commit to Git as changes are made!

  3. Add Documentation Comments. Continuously commit to Git as changes are made!

  4. Add README.md and discuss unit tests you feel should be added to adequately test the GuessingGame class.

  5. Create a UML Class Diagram for the source code and include it in the project.

  6. Confirm that project runs, no style errors related to comments, and pydoc3 generates properly.

  7. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Inheritance

In this example project, we’ll go through different forms of inheritance and how they work - including direct inheritance, abstract classes, and interfaces. This will give us a hands-on way to explore the use of inheritance in our code.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of Inheritance

Assignment Requirements

This page lists the example project requirements for Example 4 in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover adapting an existing project to use inheritance and interfaces

General Requirements

Warning

This project is the first that requires ALL general requirements introduced in the “Hello Real World” project. Read this section carefully to see what is required for this particular milestone.

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • All projects must include automation for testing, style checking, and documentation generation.
    • Java: Use Gradle with the application, jacoco, and checkstyle plugins.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries.
  • All code must properly compile and be executable.
    • Java: It must compile and execute using Gradle.
    • Python: It must execute using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
  • Where specified, code should contain appropriate unit tests that achieve the specified level of code coverage.
    • Java: Use JUnit 5. You may choose to use Hamcrest for assertions.
    • Python: Use pytest. You may choose to use Hamcrest for assertions.
  • Where specified, code should contain appropriate documentation comments following the language’s style guide.
    • In any class that should be documented, every method in that class should have complete documentation comments.
    • Java: Use javadoc to generate documentation.
    • Python: Use pdoc3 to generate documentation.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

Assignment Requirements

This milestone should include the following features:

  • Update all fruit classes to directly inherit from the Fruit abstract class.
    • The Fruit class should include an abstract getter for the name attribute that returns the name of the fruit.
  • Add an IBlendable interface that defines a blend() method, and all classes that include the blend() method should implement that interface.
  • Add a new Apple class that properly inherits the Fruit superclass and the IBlendable interface.
  • Update the main() method in the Main class to do the following:
    • Create a list containing an instance of each class implementing the IBlendable interface. The list should have the IBlendable data type.
    • Iterate through the list and call the blend method on each object.
    • If the object is a subclass of Fruit, print the name of the fruit before calling blend.

Time Requirements

Completing this project is estimated to require 1 hour.

Grading Rubric

This assignment will be graded based on the rubric below:

  • Fruit abstract class - 30%
  • Fruit classes properly inherit Fruit - 10%
  • IBlendable interface - 30%
  • Blendable classes properly implement IBlendable - 10%
  • Main method code - 20%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.

Java

YouTube Video

Resources

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle check
gradle javadoc
  1. Confirm that project runs, no style errors, and Javadoc generates properly.

  2. Follow the Video to Refactor the Code Continuously commit to Git as changes are made!

  3. Confirm that project runs and has no style errors other than comments.

  4. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Python

YouTube Video

Resources

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python

2, Run Project

cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs, all unit tests pass, no style errors, no type errors, and documentation generates properly.

  2. Follow the Video to Refactor the Code Continuously commit to Git as changes are made!

  3. Confirm that project runs and has no style errors other than comments.

  4. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Debugging & Logging

In this example project, we’ll go through how to use the Codio debugger to explore our program’s state and call stack. We’ll explore a couple of programs that contain bugs and see if we can fix them. In addition, we’ll explore how to add some simple logging to our code.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of Debugging & Logging

Assignment Requirements

This page lists the example project requirements for Example 5 in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover debugging errors in a couple of existing projects.

General Requirements

This milestone must follow these professional coding standards:

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation, unit testing, documentation generation, and execution.
    • Java: Use Gradle with the application plugin. The project should compile without errors. You may include a main class in a separate package for testing purposes only.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries. You may include a main class in a separate package for testing purposes only.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

The following requirements ARE NOT enforced for this milestone:

  • Where specified, code should contain appropriate unit tests that achieve the specified level of code coverage.
    • Unit tests are already provided.
  • Where specified, code should contain appropriate documentation comments following the language’s style guide.
    • Documentation comments will not be graded.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Provided code already is free of style errors.
    • Good style is recommended, but will not be graded.

Assignment Requirements

This milestone should include the following features:

  • Find and fix the errors in the provided TicTacToe and SudokuFourModel files such that the unit tests will pass.
  • Include some basic logging in both files as directed in the video.
  • Create/Update README.md to describe how you used the Codio debugger and/or logger to fix the errors.

Time Requirements

Completing this project is estimated to require 1 hour.

Grading Rubric

This assignment will be graded based on the rubric below:

  • TicTacToe passes tests - 10%
  • SudokuFourModel passes tests - 10%
  • Logging - 20%
    • TicTacToe - 10%
    • SudokuFourModel - 10%
  • README.md file discussion - 60%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.

Java

YouTube Video

Resources

Configuring Codio Debugger

Java Java

Java 2 Java 2

Make sure the application section of build.gradle is set to the correct application.

Can copy unit test code to a test main function for testing. Codio doesn’t have a good way to individually debug a single unit test, but some IDEs do.

Gradle Changes

Added entries in the application section for both programs. Can comment/uncomment as needed.

application {
    // Define the main class for the application.
    mainClass = 'tictactoe.TicTacToe'
    // mainClass = 'sudoku.SudokuFourModel'
}

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle test
gradle check
  1. Confirm that project runs, no style errors, and tests execute (some tests will fail).

  2. Fix Code to Pass Unit Tests. Use Codio debugger and/or Logging

  3. Add logging to both files

  4. Add README.md and discuss how you solved the errors.

  5. Confirm that project runs and all tests pass.

  6. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Python

Update to Python

If you run into issues running Python unit tests with Tox after enabling the logging, you may have to add some additional code not covered in the video. See this page: Example 5 Python Updates

YouTube Video

Resources

Configuring Codio Debugger

Python Python

Python 2 Python 2

Edit src/__main__.py to set Python path:

import sys
sys.path.append("/home/codio/workspace/python/")

Make sure src/__main__.py imports and calls correct main method.

Can copy unit test code to a test main function for testing. Codio doesn’t have a good way to individually debug a single unit test, but some IDEs do.

Tox Changes

In the tox.ini file, we added the following line under the [testenv] heading:

ignore_errors = True

This will allow the full Tox script to execute, even if there are errors earlier in the process.

We also removed the line to generate documentation, as it is not needed.

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs, no style errors, and tests execute (some tests will fail).

  2. Fix Code to Pass Unit Tests. Use Codio debugger and/or Logging

  3. Add logging to both files

  4. Add README.md and discuss how you solved the errors.

  5. Confirm that project runs and all tests pass.

  6. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Update to Example 5 Python

It appears that I missed an interesting error when developing Example 5 for Python. It is a bit complex, so I’m relying on the advanced formatting of this webpage to help explain it better than I can via email or in a short post to the class. I’ll start with a short version, and then include a longer discussion of the problem and how I came to a solution that I feel is very helpful reading for anyone learning to program and solve these issues in their own work.

tl;dr - The Short Version

Currently pytest has a bug that causes errors when logging to sys.stderr when running code inside of pytest.

As best I can tell, pytest tries to capture all output being printed to sys.stderr by redirecting it to a buffer (a virtual file) when running the tests. Once it is done, it will close the buffer and redirect output back to sys.stderr. Unfortunately, our logger does not realize this, and it may continue to try and write data to the buffer that is now closed, resulting in the ValueError: I/O operation on closed file error message seen in the output.

There are several methods to determine if code is running under pytest and disable logging in that case.

I recommend this method:

import sys

# get the root logger
logger = logging.getLogger()
# disable if pytest is running
if "pytest" in sys.modules:
    logger.disabled = True

You will need to add this code to any file that you add logging to, in order to prevent errors from pytest. Alternatively, you can disable the handler that prints to sys.stderr and instead just use a file handler.

The Long Version

Since this is an advanced programming course, I figure that it is worth a bit of a “deep dive” into this situation so you can understand what I found, the efforts I went through to solve it, and how things are really working behind the scenes.

This is a bit of cognitive apprenticeship, where I attempt to show you my thought processes and how I go about solving a problem like this. My hope is that you’ll be able to learn from this process and possibly use this knowledge to help you solve your own problems in the future.

Unfortunately, in the world of higher education, we spend way too much time focusing on narrowly-scoped, previously-solved problems, to allow you to learn in an environment where we know a solution is possible in a set amount of time. In the real world, however, you’ll be constantly presented with broadly-scoped, open-ended problems like this one, where you’ll have to do some exploration to find possible causes and solutions, and then use your own background and knowledge to determine what solutions, if any, are available.

So, here goes.

The Error

When you run pytest in Example 5 after adding some logging code as directed in the video, you will see many pages of errors printed to the terminal. In my testing, the terminal in Codio printed errors for several minutes before finally stopping. A screenshot of a small portion of those errors is below.

Errors Errors

When this happens, you may be able to use CTRL + C to stop the output, but in many cases I simply had to close the terminal tab in Codio.

What Happened

When I developed this example, I focused on the debugging portion first, and then later added the logging code. However, I neglected to run pytest after adding the logging code to my model solution, and did not encounter this error in my initial testing. That was an oversight on my part.

As you work on this project, you may end up adding the logging code first while still working on debugging the errors in the project. In that case, you will most likely run tox or pytest to run the unit tests contained in the project with the logging code in place. That will cause the error to appear. As soon as I ran tox in my existing model solution, my code presented this error.

How I solved the problem

The process of finding a solution for this problem went in three phases.

Phase 1 - Searching

First, I attempted to Google some of the error message and a few things that I suspected were at play. I already had a hunch that the error itself was coming from the logging code, since I had added that to my model solution last. After reproducing the bug in my solution, I set out to solve it. Some Google search phrases I used:

  1. pytest logging stderr write to closed file - Including keywords pytest and logging as well as the stderr stream and a bit of the error message.
  2. pytest stream.write(msg) I/O operation on closed file - adding more details such as the line of code causing the error and the exact error messages.
  3. "pytest" stream.write(msg) I/O operation on closed file - putting "pytest" in quotes will find results that always include that keyword

There were others, but this was the most fruitful.

Phase 2 - Isolate the Error

In several of those searches, I came across a few bug reports on GitHub, specifically within the pytest project’s repository. Bug reports and discussions on GitHub are usually very fruitful when looking for technical errors that include code and error messages, so I looked into a few of them.

  1. ValueError: I/O Operation on closed file (#14) - this was the first one I found. However, I quickly ruled it out, as it was first posted in 2010 and mainly seemed to use Python 2 instead of Python 3. After scrolling through the discussion, nothing really seemed to fit the situation I was in, so I ignored it and moved on. However, it did reference the next issue…
  2. Improve error message when tests use closed stdout/stderr (capture) (#5743) - this one felt like it was a bit closer. In this report, they discuss the fact that pytest will redirect and close system streams such as sys.stderr as part of the test. It was also much more recent, and some of the error messages they were running into were similar to what I was seeing.
  3. pytest 4.5 floods the output with logging errors when logging from atexit handlers (#5282) - similar to the one above, this one was getting closer to the issue I was seeing, though it wasn’t an exact match. By reading these three thread, I was starting to get a feel for the crux of the error - if our logger is trying to write to any of the output streams, like sys.stderr or sys.stdout, then most likely pytest would interfere with that and cause this error. Thankfully, the last two issues both referenced this issue…
  4. pytest capture logging error still happening (#5502) - this report had a lot of discussion on it, but pretty much sealed the deal for me. One of the core pytest developers posted a message that included this text:

What I believe is happening is:

  1. pytest changes sys.stdout and sys.stderr to a buffer while importing test modules.
  2. If there’s user code setting up logging and/or creating a logging.StreamHandler at the import level, it will attach itself to pytest’s buffer.
  3. When pytest is about to finish the test session, it will restore sys.stdout and sys.stderr to the original values, and close the “capture” buffer.
  4. Here the problem happens: if any message is emitted at this point, the StreamHandler will try to attach itself to the buffer, hence the error.

So, we’ve now found what we suspect is the error. All we have to do is figure out how to resolve it.

Phase 3 - The Fix

Unfortunately, issue #5502 is still open as of this writing, so we needed a way to get around this error. With some quick testing, I was able to confirm the error went away if I removed the StreamHandler from the existing logging code. So, I decided that the best way to deal with this was to find some way to disable logging while the code is running as part of a unit test. This is a somewhat common, though discouraged, trick in programming. Ideally you don’t want to hide any code from the unit tests, but in some instances you want to make sure that the unit tests don’t actually change live data, such as the actual database used by this program. So, you can “protect” the code that connects to the database and make sure it cannot run as part of a unit test.

A quick Google search for determine if code is running under pytest python quickly lead me to a StackOverflow post discussing this very issue. Great! I had quickly found a pretty good resource that might lead me to a solution.

Within the discussion, there are a few solutions suggested, and helpfully ranked by the upvotes from other users.

  1. Solution 1 - simply check if "pytest" in sys.modules: since the pytest application will always be loaded when running a test. This solution seemed pretty simple and didn’t have many obvious side effects, provided your application didn’t load pytest as part of its normal execution.
  2. Solution 2 - a solution that points to a section of the pytest Manual that shows the preferred way of doing this. In short, we place some code in the conftest.py file, which is only executed as part of a unit test, to update a value in our code, and then check that value where needed. This looks promising, and is probably the correct answer that would work in all cases, but also requires significantly more code and adds a structural dependency between our code and the conftest.py file.
  3. Solution 3 - a third solution suggests checking for the existence of the PYTEST_CURRENT_TEST environment variable, which is set when pytest is running. This may also work, but has the side effect of being outside of our control - any other application on our system could also set that variable, including another instance of pytest, so it may not work as reliably as the other two.

In the end, I chose Solution 1, and updated the code at the top of my main() method in TicTacToe.py to the following:

import sys

# get the root logger
logger = logging.getLogger()
# disable if pytest is running
if "pytest" in sys.modules:
    logger.disabled = True

That code will simply load the logger, and immediately check if the "pytest" module is loaded. If so, it will disable the logger globally in my program.

An alternative solution would be to just disable the StreamHandler and allow the FileHandler to remain enabled, but I felt that logging from unit tests is not helpful and chose to disable it entirely.

Summary

I hope this discussion is helpful - I’ve found that sometimes the best opportunities for cognitive apprenticeship happen directly as a result of the class, so I wanted to take this chance and share a bit of my own problem solving process here.

If you have any follow up questions about this, please let me know!

Design Patterns

In this example project, we’ll go through the steps of building several creational design patterns in our code: the builder, factory method, and singleton pattern, as well as the iterator behavioral pattern.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of Design Patterns

Assignment Requirements

This page lists the example project requirements for Example 6 in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover creating various design patterns to represent types of dice.

General Requirements

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation and execution.
    • Java: Use Gradle with the application and jacoco plugins. The project should compile without errors.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
      • There are instances where Mypy is unable to determine the type of lambda expressions used as commands with buttons. This error can be ignored.
  • No Unit Tests are required for this example. They are added in a later example.
  • Documentation comments are not required for this example, but they are recommended for your own use.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Style errors related to documentation comments (or lack thereof) will be ignored.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

Assignment Requirements

Dice Names

In this assignment, we’ll refer to standard numerical dice following the method used by many RPGs. A “dx” is a dice with “x” sides, numbered from 1 through x". So, a standard six-sided cube die numbered 1 through 6 is referred to as a “d6”.

This milestone should include the following features:

  • A SingleDie class that implements a single die (this is provided)
  • A DiceSet class that implements the iterator pattern (part of this is provided).
  • A DiceSetBuilder interface that follows the builder pattern.
  • The following implementations of the DiceSetBuilder interface
    • TwoDsixBuilder - A pair of d6
    • YachtDiceBuilder - Five d6’s
    • RpgDiceBuilder - A standard set of RPG dice - d4, d6, d8, d10, d12, d20, d100
    • HauntedDiceBuilder - Eight d3’s numbered 0 through 2.
    • PigDiceBuilder - Two d6 with the following faces: nose, tail, feet, back, left, right.
  • A DiceSetFactory that implements the singleton and factory method patterns. It should provide a method to build the dice sets above based on the following names:
    • 2d6
    • yacht
    • rpg
    • haunted
    • pig
  • Main class that includes a game() method that will create a set of two d6 dice, roll them, and then return a string of the results in the form [die 1] + [die 2] = [sum] (this is provided).

Time Requirements

Completing this project is estimated to require 1-2 hours.

Grading Rubric

This assignment will be graded based on the rubric below:

  • DiceSet class - 20%
  • DiceSetBuilder interface - 20%
  • DiceSetBuilder implementations - 20%
  • DiceSetFactory class - 40%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.

Java

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle check
  1. Confirm that project runs.
  2. Build DiceSet class
  3. Build DiceSetBuilder interface and implementations
  4. Build DiceSetFactory
  5. Update Main class
  6. When complete, use Git to commit and push updated code.
git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Python

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs.
  2. Build DiceSet class
  3. Build DiceSetBuilder interface and implementations
  4. Build DiceSetFactory
  5. Update Main class
  6. When complete, use Git to commit and push updated code.
git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Parallel Programming

In this example project, we’ll explore some concepts related to parallel programming. You won’t be expected to actually create a fully parallel program in this course, but it is helpful to explore these concepts and see how they impact our program’s performance.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of Parallel Programming

Assignment Requirements

This page lists the example project requirements for Example 7A in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover some concepts related to parallel programming. This is meant to be an exploratory project only, so requirements are very loose.

General Requirements

  • No style or documentation requirements will be enforced for this example.

Assignment Requirements

  • ParallelOne
    • Update ParallelOne following the video to use 4 Threads.
    • Run the program a few times and observe a race condition. Take a screenshot of the race condition and store it in a file named race in the project folder.
    • Update ParallelOne to properly use locks to prevent a race condition.
    • Run the program a few times and verify that no race condition occurs. Take a screenshot of the program running correctly and store it in a file named lock in the project folder.
    • See Take a Screenshot on Wikihow for details on how to take a screenshot. On Windows, you can use the “Snipping Tool” to grab only a portion of the screen.
    • See Uploading Files in the Codio documentation for how to upload a screenshot.
  • ParallelTwo
    • Update ParallelTwo following the video to handle blocking and an arbitrary number of threads.
    • Run the program with varying numbers of threads (1 - 10 recommended) and graph the number of threads vs. the time taken.
    • Submit your graph as an image file named graph in the project folder.
    • See How to Make a Single Line Graph in Excel for instructions.
  • Based on the results of the ParallelTwo exercise, write a short blurb in a README.md file in the project folder:
    • How did the number of threads impact amount of time taken to complete the work?
    • What does that result tell us about the hardware available on the Codio system?

See below for some example screenshots and graphs. The sample graph shows results for both Java and Python, but your graph will only include one language.

Time Requirements

Completing this project is estimated to require 1 hour.

Grading Rubric

This assignment will be graded based on the rubric below:

  • Updates to ParallelOne - 30%
  • race screenshot - 10%
  • lock screenshot - 10%
  • Updates to ParallelTwo - 20%
  • graph image - 20%
  • README.md file - 10%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.




Sample Screenshots

Race

Race Screenshot Race Screenshot

Lock

Lock Screenshot Lock Screenshot

Graph

Graph Graph

Java

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Update ParallelOne to use 4 threads.

  2. Take a screenshot showing a race condition.

  3. Update ParallelOne to use a lock.

  4. Take a screenshot showing no race condition.

  5. Update ParallelTwo to use blocking and an arbitrary number of threads.

  6. Run several times with different number of threads (1 - 10) and graph results.

  7. Write in README.md to answer two questions

  8. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Compiling and Running Java

Recall that you can compile a Java program using javac:

javac ParallelOne.java

You can then run it using java:

java ParallelOne

Python

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Update ParallelOne to use 4 threads.

  2. Take a screenshot showing a race condition.

  3. Update ParallelOne to use a lock.

  4. Take a screenshot showing no race condition.

  5. Update ParallelTwo to use blocking and an arbitrary number of threads.

  6. Run several times with different number of threads (1 - 10) and graph results.

  7. Write in README.md to answer two questions

  8. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Test Doubles

In this example project, we’ll go through the steps of adding unit tests to our prior example with design patterns. These unit test will use test doubles to mimic the functionality of various other parts of the program.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of Test Doubles

Assignment Requirements

This page lists the example project requirements for Example 7 in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover creating new packages, classes, and enumerations within an existing project. Similar to the restaurant project, the examples will cover a smaller subset of those requirements as part of a fictional ice cream shop.

General Requirements

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation and execution.
    • Java: Use Gradle with the application and jacoco plugins. The project should compile without errors.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
      • There are instances where Mypy is unable to determine the type of lambda expressions used as commands with buttons. This error can be ignored.
  • Some unit tests are required for this example - see below.
  • Documentation comments are not required for this example, but they are recommended for your own use.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Style errors related to documentation comments (or lack thereof) will be ignored.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

Assignment Requirements

Dice Names

In this assignment, we’ll refer to standard numerical dice following the method used by many RPGs. A “dx” is a dice with “x” sides, numbered from 1 through x". So, a standard six-sided cube die numbered 1 through 6 is referred to as a “d6”.

This milestone should include the following unit test classes and methods:

  • DiceSet tests
    • SumWorksCorrectly - confirm that the sum method in DiceSet works properly by using fake dice and method stubs.
  • Main tests
    • TestGameMethod - confirm that the game method acquires a set of two d6 from the factory, and then make sure it prints the correct output and calls roll at least once, using fakes, method stubs, test spies, and faking the static class.

Time Requirements

Completing this project is estimated to require 1 hour.

Grading Rubric

This assignment will be graded based on the rubric below:

  • DiceSet unit test - 50%
  • Main unit test - 50%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.

Java

YouTube Video

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle check
  1. Confirm that project runs.
  2. Build unit tests
  3. When complete, use Git to commit and push updated code.
git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Python

YouTube Video

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs.
  2. Build unit tests
  3. When complete, use Git to commit and push updated code.
git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Event-Driven Programming

In this example project, we’ll go through the steps of modifying our previously created GUI and adding in the code to handle events generated by users clicking on various buttons. This will allow us to construct an order of several sundaes within our GUI.

We’ll also add some simple unit tests to verify that our GUI is correctly populated by the object it is given, and that it correctly updates the object when saving it.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of Event-Driven Programming

Assignment Requirements

This page lists the example project requirements for Example 7B in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover updating an existing GUI to handle events, as well as adding some unit tests for our GUI panels.

General Requirements

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation and execution.
    • Java: Use Gradle with the application and jacoco plugins. The project should compile without errors.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
      • There are instances where Mypy is unable to determine the type of lambda expressions used as commands with buttons. This error can be ignored.
  • Unit tests for TheChocoPanel and TheClassicPanel are required.
  • Documentation comments are not required for this example, but they are recommended for your own use.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Style errors related to documentation comments (or lack thereof) will be ignored.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

Assignment Requirements

This milestone should include the following new GUI features:

  • Changes to the previous milestone:
    • Add a “Cancel” button to each of the sundae panels. (This is included in the starter)
    • Add a “Delete” button to the SidebarPanel panel. (This is included in the starter)
    • Switch the list box in the SidebarPanel to a tree element (Java JTree or tkinter Treeview).

Once the entire project is working, you should observe the following behavior on the new tree element.

  • When an item is added to the tree element in the SidebarPanel, the following should happen:
    • The item’s string representation should be a top-level node in the tree.
    • Any special instructions for that item should be represented as child nodes of the top-level node.
    • The item should be shown fully expanded by default.
    • The tree element should only allow the user to select one item at a time.

In addition, the following events should be implemented in the GUI:

  • When the “Save” button in any of the sundae panels is clicked, the following should happen:
    • The item currently represented by the panel should be updated such that each attribute matches the current status of the associated GUI element.
    • The item should be placed into the tree in the SidebarPanel if it is a new item, or the item should be updated if it is being edited.
    • The main panel in MainWindow should be replaced with the OrderScreen (This was part of the previous milestone).
  • When the “Cancel” button in any of the sundae panels is clicked, the following should happen:
    • If an item is being edited, any changes made in the GUI should be discarded (the item should not be changed).
    • The main panel in MainWindow should be replaced with the OrderScreen (This was part of the previous milestone).
  • When the “Edit” button in the SidebarPanel is clicked, the following should happen:
    • The OrderItem that is currently selected should be determined. If the selection is an ingredient of that item, the code should work upwards in the tree to find the OrderItem.
    • The appropriate sundae panel should be loaded into the main panel in MainWindow and populated with the current status of the item (Most of this should work from the previous milestone).
    • If the item is saved via the “Save” button, it’s entry in the tree element should be updated without changing the order of the items in the tree.
    • If the changes are cancelled via the “Cancel” button, no changes should be made.
  • When the “Delete” button in the SidebarPanel is clicked, the following should happen:
    • The OrderItem that is currently selected should be determined. If the selection is an ingredient of that item, the code should work upwards in the tree to find the OrderItem.
    • That item should be removed from the tree element and any other relevant data structures in the SidebarPanel class.

Unit tests should be added to the corresponding test package for the following classes:

  • Each sundae panel in starfleettreats.gui.sundaes

See below for a list of suggested unit tests. You should achieve at or near 100% coverage on these classes. We will not unit test the MainWindow, OrderPanel, or SidebarPanel classes in this milestone.

Time Requirements

Completing this project is estimated to require 1-2 hours.

Grading Rubric

This assignment will be graded based on the rubric below:

  • Tree element displays order items correctly: 10%
  • “Save” buttons work properly for all items: 20%
  • “Cancel” buttons work properly for all items: 10%
  • “Edit” button works properly for all items: 20%
  • “Delete” button works properly for all items: 10%
  • Unit Tests: 30%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.




Sundae Panels

Each sundae panel test class should contain unit tests for the following:

  • testDefaultConstructor() - create the panel without providing an existing element, and assert that it creates a new instance of the correct item.
  • testBadActionCommand() - call the actionPerformed() method with a bad action command, and assert that an exception is not thrown.
  • testContainerComboBox(Container) - instantiate a panel with an existing item, change the value of the container combo box in the GUI to the Container value, and fire a “save” action, then verify that the item has the correct container value.
  • testContainerComboBoxSetCorrectly(Container) - instantiate a panel with an existing item using the given Container, and assert that the Container combo box is set to the correct value.
  • test<Ingredient>CheckBox() - instantiate a panel with an existing item, change the value of the ingredient check box in the GUI to a value, and fire a “save” action, then verify that the item has the correct value. Do this for both true and false.
  • test<Ingredient>CheckBoxSetCorrectly() - instantiate a panel with an existing item with a given value for ingredient, and assert that the ingredient checkbox is set to the correct value. Do this for both true and false.
  • testToppingCheckBox(Topping) - instantiate a panel with an existing item, change the value of the topping check box in the GUI to a value, and fire a “save” action, then verify that the item has the correct value. Do this for both true and false.
  • testToppingCheckBoxSetCorrectly(Topping) - instantiate a panel with an existing item with a given value for topping, and assert that the topping checkbox is set to the correct value. Do this for both true and false.
  • testCancelButton() - instantiate a panel with an existing item, change several values in the GUI, and fire a “cancel” action, then assert that the item is unchanged from its previous state.
Update Permissions

To allow proper unit testing, you may need to relax the permissions on several elements inside of your GUI classes. I recommend using package-private in Java, with no modifier - see this document. Then, any unit tests that are in the same package can have access to those members. For Python, switching from double underscore private attributes to single underscore protected attributes is sufficient.

This has already been done on the item attribute in the sundae panels.

Java

Part 1
YouTube Video
Part 2
YouTube Video

Gradle Changes

The build.gradle file includes the library for JUnit5 parameterized tests:

dependencies {
    // Use JUnit Jupiter API for testing.
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2', 'org.hamcrest:hamcrest:2.2', 'org.junit.jupiter:junit-jupiter-params'

    // Use JUnit Jupiter Engine for testing.
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:29.0-jre'
}

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle check
  1. Confirm that project runs and has no style errors.

  2. Handle Save Events

  3. Handle Cancel Events

  4. Switch Listbox to Tree in Sidebar

  5. Populate Tree with Elements

  6. Handle Edit Events

  7. Handle Delete Events

  8. Create Unit Tests

  9. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Python

Part 1
YouTube Video
Part 2
YouTube Video

Tox Changes

The tox.ini file has been updated to allow testing with a display, and will now run the full unit test suite.

[testenv]
deps = -rrequirements.txt
ignore_errors = True
passenv = DISPLAY
commands = python3 -m mypy -p src --html-report reports/mypy
           python3 -m coverage run --source src -m pytest --html=reports/pytest/index.html
           python3 -m coverage html -d reports/coverage
           python3 -m flake8 --docstring-convention google --format=html --htmldir=reports/flake

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs and has no style errors.

  2. Handle Save Events

  3. Handle Cancel Events

  4. Switch Listbox to Tree in Sidebar

  5. Populate Tree with Elements

  6. Handle Edit Events

  7. Handle Delete Events

  8. Create Unit Tests

  9. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

GUI Basics

In this example project, we’ll go through the steps of creating a simple GUI using either Java Swing or Python tkinter. This builds on top of a sample restaurant project, so many of the things we’ll cover in this example can be directly applied to the upcoming milestone.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of GUI Basics

Assignment Requirements

This page lists the example project requirements for Example 6 in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover creating new packages, classes, and enumerations within an existing project. Similar to the restaurant project, the examples will cover a smaller subset of those requirements as part of a fictional ice cream shop.

General Requirements

This milestone must follow these professional coding standards:

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation and execution.
    • Java: Use Gradle with the application and jacoco plugins. The project should compile without errors.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
      • There are instances where Mypy is unable to determine the type of lambda expressions used as commands with buttons. This error can be ignored.
  • Unit tests are not required for this example.
  • Documentation comments are not required for this example, but they are recommended for your own use.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Style errors related to documentation comments (or lack thereof) will be ignored.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

Assignment Requirements

This milestone should include the following features:

  • A starfleettreats.Main class that properly loads and displays the program’s GUI.
  • A starfleettreats.gui.MainWindow class that represents the main GUI window.
    • It should contain two panels - a main panel and a sidebar panel.
    • It should also contain two methods: one to load a particular panel into the main panel, and another to load the order screen into the main panel.
  • A starfleettreats.gui.OrderPanel class to represent the main order screen panel.
    • It should contain two buttons, one for each sundae. They should be automatically generated from the menu.
    • When clicked, those buttons should call a method to load the appropriate panel in to the main panel.
  • A starfleettreats.gui.SidebarPanel class to represent the sidebar panel.
    • It should contain labels for order number, subtotal, tax, and total.
    • It should contain an “Edit” button, that does nothing when clicked.
    • It should also include a list box that can be used to keep track of the order. The list box should expand to fill all remaining vertical space in the window.
  • A starfleettreats.gui.sundaes.TheClassicPanel class to represent an instance of TheClassic.
    • It should include appropriate controls for modifying the ingredients, container, and toppings.
    • When given an instance of TheClassic as a parameter to the constructor, the values of the controls should be set to match the values in the instance.
    • It should include a “Save” button that, when clicked, will replace the main panel with the order screen. It does not have to store the values in the controls (that will be handled in a later example)
  • A starfleettreats.gui.sundaes.TheChocoPanel class to represent an instance of TheChoco. You will create this class on your own after watching the video.
    • It should include appropriate controls for modifying the ingredients, container, and toppings.
    • When given an instance of TheChoco as a parameter to the constructor, the values of the controls should be set to match the values in the instance.
    • It should include a “Save” button that, when clicked, will replace the main panel with the order screen. It does not have to store the values in the controls (that will be handled in a later example)

See below for GUI sketches.

Time Requirements

Completing this project is estimated to require 1-2 hours.

Grading Rubric

This assignment will be graded based on the rubric below:

  • Main class - 10%
  • MainWindow class - 10%
  • OrderScreen class - 10%
  • Sidebar class - 10%
  • TheClassic class - 20%
  • TheChoco class - 40%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.




Sundaes

The Classic (Banana Split)

it doesn’t get more traditional that this

starfleettreats.data.sundaes.TheClassic - The price is $5.50 and it is 1050 calories. Served in a Waffle Cone with Vanilla Ice Cream and Banana. Comes with Chocolate, Whipped Cream, and Cherry.

The Choco (Chocolate and Brownie)

a chocolate lovers’ dream

starfleettreats.data.sundaes.TheChoco - The price is $7.35 and it is 1275 calories. Served in a Dish with Chocolate Ice Cream and Brownie. Can add Vanilla Ice Cream. Comes with Chocolate, Caramel, and Peanuts.

GUI Sketches

Main Window with Order Panel

Main Screen Main Screen

Main Window with Sundae Panel

Main Screen Main Screen

Java

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle check
  1. Confirm that project runs and has no style errors.

  2. Create New GUI Classes. Continuously commit to Git as changes are made!

  3. Update Main.java to use new GUI. This is just for testing purposes.

  4. Add GUI Panel for TheChoco. You’ll do this on your own.

  5. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Completed GUIs

Main Window with Order Panel

Main Screen Main Screen

Main Window with Sundae Panel

Main Screen Main Screen

Python

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs and has no style errors.

  2. Create New GUI Classes. Continuously commit to Git as changes are made!

  3. Update Main.py to use new GUI. This is just for testing purposes.

  4. Add GUI Panel for TheChoco. You’ll do this on your own.

  5. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Completed GUIs

Main Window with Order Panel

Main Screen Main Screen

Main Window with Sundae Panel

Main Screen Main Screen

Combos & Libraries

In this example project, we’ll discuss how to modify our GUI a bit to handle some of the process of building combos. We’ll also look at how we can download and install external libraries into our project, and how we can use them directly within our code.

We’ll also add some simple unit tests to verify that our updated GUI is properly loading the panels it should.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of Combos & Libraries

Assignment Requirements

This page lists the example project requirements for Example 9 in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover updating an existing GUI to handle events, as well as adding some unit tests for our GUI panels.

General Requirements

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation and execution.
    • Java: Use Gradle with the application and jacoco plugins. The project should compile without errors.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
      • There are instances where Mypy is unable to determine the type of lambda expressions used as commands with buttons. This error can be ignored.
  • Unit tests for ComboPanel are required.
  • Documentation comments are not required for this example, but they are recommended for your own use.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Style errors related to documentation comments (or lack thereof) will be ignored.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

Assignment Requirements

This milestone should include the following new GUI features:

Combo Panel

Demonstrate the ability to select a panel based on a combo box, and display that panel within a new panel that is not MainWindow

  • Create a new ParentPanel interface to act as a shared interface between MainWindow and ComboPanel
    • Both MainWindow and ComboPanel should implement this interface.
    • The interface should have methods for loadOrderPanel(), and either addItem() (Java) or save_item (Python).
  • Create a new SundaePanel base class for the Sundae panels
    • It should include the method for reacting to events
    • It should extend the base Panel class for the GUI being used
  • Create a new ComboPanel class
    • It should include a combo box used to select the menu item, and then it should load and display the panel for that item within itself.
    • The “Save” button in the item’s panel should be hidden, and instead the “Save” and “Cancel” buttons should be included in the ComboPanel.
    • When clicked, the “Save” and “Cancel” buttons should properly save the item selected.

Create a set of unit tests for ComboPanel that verify it will load the correct panel when the combo box value is updated.

Checkout

Create the ability to check out using a credit card via the RestaurantRegister library. We will also mock up the portions required for checking out via cash.

  • Java
    • Download and install the latest JAR file release from GitHub.
    • View the Javadoc for specifics of how to use it.
    • Review the Source Code if desired.
    • All library classes are in the edu.ksu.cs.cc410.register package.
    • Post any questions or bugs regarding the library to the GitHub Issues page.
    • Pull requests for bug fixes are welcome! However, in general we won’t greatly change or enhance the functionality of the library overall.
  • Python
    • Download and install the latest wheel file release from GitHub.
    • View the Documentation for specifics of how to use it.
    • Review the Source Code if desired.
    • All library classes are in the cc410.register package.
    • Post any questions or bugs regarding the library to the GitHub Issues page.
    • Pull requests for bug fixes are welcome! However, in general we won’t greatly change or enhance the functionality of the library overall.

Time Requirements

Completing this project is estimated to require 1-2 hours.

Grading Rubric

This assignment will be graded based on the rubric below:

  • ComboPanel works correctly - 30%
  • ComboPanel unit tests - 20%
  • Install RestaurantRegister library - 20%
  • Card payment works correctly - 30%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.

Java

Part 1
YouTube Video
Part 2
YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle check
  1. Confirm that project runs and has no style errors.

  2. Add ParentPanel interface

  3. Add ComboPanel class

  4. Add Unit Tests for ComboPanel

  5. Install RestaurantRegister library

  6. Set up Card Payments

  7. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Python

Part 1
YouTube Video
Part 2
YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs and has no style errors.

  2. Add ParentPanel interface

  3. Add ComboPanel class

  4. Add Unit Tests for ComboPanel

  5. Install RestaurantRegister library

  6. Set up Card Payments

  7. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Releases

In this example project, we’ll explore taking an existing project and preparing it for release. We’ll then create the actual release package and upload them to GitHub along with a release tag.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of Releases

Assignment Requirements

This page lists the example project requirements for Example 10 in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover some concepts related to parallel programming. This is meant to be an exploratory project only, so requirements are very loose.

General Requirements

  • No style or documentation requirements will be enforced for this example.

Assignment Requirements

This one is simple - follow the steps to create a proper release package for this project, and then upload it to GitHub as part of a release. You should be able to then download your package and execute it directly.

Time Requirements

Completing this project is estimated to require 1 hour.

Grading Rubric

This assignment will be graded based on the rubric below:

  • Package Created - 70%
  • Package on GitHub - 10%
  • Documentation on GitHub - 10%
  • Package Works - 10%

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.

Java

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle check
  1. Confirm that project runs and has no style errors.

  2. See the textbook or video for steps to create a release.

  3. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Python

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs and has no style errors.

  2. See the textbook or video for steps to create a release.

  3. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Web Basics

In this example project, we’ll explore taking an existing project and integrating a lightweight web framework into it. In that way, we can use our existing code base within a web application and use it to generate dynamic web pages.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of Web Basics

Assignment Requirements

This page lists the example project requirements for Example 11 in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover updating an existing project to include a lightweight web framework and generate dynamic web pages.

General Requirements

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation and execution.
    • Java: Use Gradle with the application and jacoco plugins. The project should compile without errors.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
      • There are instances where Mypy is unable to determine the type of lambda expressions used as commands with buttons. This error can be ignored.
  • Unit tests are not required.
  • Documentation comments are not required for this example, but they are recommended for your own use.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Style errors related to documentation comments (or lack thereof) will be ignored.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
    • All HTML must conform to the HTML5 standard. Use the W3C Validator to check your rendered pages if desired.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

Assignment Requirements

This milestone should include the following new GUI features:

  • Install a Web Framework into the project
    • Java - Install the Spring framework using the Spring Initializr. It should include the Spring Boot DevTools, Spring Web, and Thymeleaf Template Engine as dependencies.
    • Python - Install the Flask framework and the Flask-Classful extension. You may also wish to install python-dotenv to use a .flaskenv file.
  • Create a movies.web package.
  • Create a new Web class in the movies package that will launch the website. Configure the project to use this class as the main class.
  • Create a MoviesController class in the movies.web package that will handle the following pages:
    • / - a static index page that will list all of the movies in the database
    • /about/ - a static about page. You will create this part on your own!
    • /greeting/ - a “Hello World” greeting
    • /greeting/name/ - a “Hello name” greeting
  • Each page should be rendered using an appropriate template that uses/inherits from a standard base template.
  • The base template should include an appropriate website title, navigation, body, and a footer.

Time Requirements

Completing this project is estimated to require 1-2 hours.

Grading Rubric

This assignment will be graded based on the rubric below:

  • Install Web Framework - 10%
  • Web Class - 10%
  • Controller Class - 10%
  • Base Layout - 10%
  • Index Page - 30%
  • About Page - 20%
  • Greeting Page - 10%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.

Java

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle check
  1. Confirm that project runs and has no style errors.

  2. Follow along with the video to create the project.

  3. Add the about page yourself!

  4. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

When using Spring Initializr, below is a screenshot showing what it should look like.

Spring Initializr Spring Initializr

The generated build.gradle file looks like this as of Spring 2021:

plugins {
	id 'org.springframework.boot' version '2.4.4'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
}

group = 'web'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
	useJUnitPlatform()
}

Python

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs and has no style errors.

  2. Follow along with the video to create the project.

  3. Add the about page yourself!

  4. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Forms

In this example project, we’ll explore adding a simple search form to our existing movies website. This form will allow us to filter our movies based on a few criteria, and we’ll be able to explore how to receive form data in our web applications.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of Forms

Assignment Requirements

This page lists the example project requirements for Example 12 in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover updating an existing project to include a lightweight web framework and generate dynamic web pages.

General Requirements

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation and execution.
    • Java: Use Gradle with the application and jacoco plugins. The project should compile without errors.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
      • There are instances where Mypy is unable to determine the type of lambda expressions used as commands with buttons. This error can be ignored.
  • Unit tests are not required.
  • Documentation comments are not required for this example, but they are recommended for your own use.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Style errors related to documentation comments (or lack thereof) will be ignored.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
    • All HTML must conform to the HTML5 standard. Use the W3C Validator to check your rendered pages if desired.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

Assignment Requirements

This milestone should include the following new GUI features:

  • Create a route advancedsearch that allows users to filter the movies list based on various options.
    • Allow filtering based on: keywords, MPAA rating, IMDB rating, Rotten Tomatoes rating, and Genre.
    • A GET request to that route should display the advanced search form from the template advanced_search.html. It should be easily understandable to users.
    • A POST request should display the form as filled in by the user, as well as any results, using the same advanced_search.html template.

We will work on filtering by keywords, MPAA rating and IMDB rating together. You’ll add the Rotten Tomatoes rating and Genre filter yourself.

Time Requirements

Completing this project is estimated to require 1-2 hours.

Grading Rubric

This assignment will be graded based on the rubric below:

  • Advanced Search Form - 75%
    • Keywords, MPAA and IMDB - 15%
    • Rotten Tomatoes - 30%
    • Genre - 30%
  • Advanced Search Results - 25%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.

Java

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle check
  1. Confirm that project runs and has no style errors.

  2. Follow along with the video to update the project.

  3. Add the option to filter by Rotten Tomatoes Rating and Genre yourself. Test and make sure it works.

  4. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Python

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs and has no style errors.

  2. Follow along with the video to update the project.

  3. Add the option to filter by Rotten Tomatoes Rating and Genre yourself. Test and make sure it works.

  4. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

REST

In this example project, we’ll add the ability to update and delete movies in our website using a RESTful architectural style.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of REST

Assignment Requirements

This page lists the example project requirements for Example 13 in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover updating an existing project to allow editing and deleting of movies following a RESTful architectural style.

General Requirements

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation and execution.
    • Java: Use Gradle with the application and jacoco plugins. The project should compile without errors.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
      • There are instances where Mypy is unable to determine the type of lambda expressions used as commands with buttons. This error can be ignored.
  • Unit tests are not required.
  • Documentation comments are not required for this example, but they are recommended for your own use.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Style errors related to documentation comments (or lack thereof) will be ignored.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
    • All HTML must conform to the HTML5 standard. Use the W3C Validator to check your rendered pages if desired.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

Assignment Requirements

This milestone should include a new controller RestMovieContoller that follows a RESTful architectural style. Specifically, it should include the following URL routes:

HTTP Method URL Path Description CRUD Method
GET /movies Display all movies. Read All
GET /movies/{id} Display a single movie Read One
GET /movies/{id}/edit Display a form to edit the movie N/A
POST /movies/{id} Update the movie Update
GET /movies/{id}/delete Display a warning page before deleting a movie N/A
POST /movies/{id}/delete Delete the movie Destroy

Time Requirements

Completing this project is estimated to require 1-2 hours.

Grading Rubric

This assignment will be graded based on the rubric below:

  • Read All Route - 10%
  • Read One Route - 30%
  • Edit Form & Route - 30%
  • Delete Form & Route - 30%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes

Java

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle check
  1. Confirm that project runs and has no style errors.

  2. Follow along with the video to update the project.

  3. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Python

Errata

This video uses the html5 widget module in WTForms, which is no longer present. See below the video for a fix!

YouTube Video
Widget Fix

Since this video was recorded, Flask-WTF updated and is now using a newer version of the underlying WTForms library. That library has since deprecated the html5 widgets module and moved them into the main widgets module.

When running the code as shown in the video, you may receive this error:

Example 13 Import Error Example 13 Import Error

To resolve this, in MovieForm.py we can simply change the import to be from wtforms.widgets import NumberInput and then remove the html5 in front of each instance where we use NumberInput in the code. See the screenshot below for a corrected version:

Example 13 Corrected Code Example 13 Corrected Code

For more information, check out the relevant pull request and the WTForms Widgets Documentation.

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs and has no style errors.

  2. Follow along with the video to update the project.

  3. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Form Validation

In this example project, we’ll update our previous RESTful web application to perform server-side form validation, making sure that each piece of data submitted to the server is valid.

The next page will discuss the overall requirements that this example project should include. After that, there is a page for each programming language with some basic steps and a video to walk you through the whole process.

For these example projects, you’ll be given some starter code via the GitHub Classroom assignment, so make sure you’ve accepted the assignment and created your repository in GitHub first.

Good luck!

Subsections of Form Validation

Assignment Requirements

This page lists the example project requirements for Example 14 in CC 410. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

This example will cover updating an existing project to include server-side form validation.

General Requirements

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation and execution.
    • Java: Use Gradle with the application and jacoco plugins. The project should compile without errors.
    • Python: Use tox configured to use Python 3.10 and a requirements file to install libraries.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.10. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check.
      • There are instances where Mypy is unable to determine the type of lambda expressions used as commands with buttons. This error can be ignored.
  • Unit tests are not required.
  • Documentation comments are not required for this example, but they are recommended for your own use.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Style errors related to documentation comments (or lack thereof) will be ignored.
    • Java: Use Checkstyle 10.6.0+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
    • All HTML must conform to the HTML5 standard. Use the W3C Validator to check your rendered pages if desired.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

Assignment Requirements

Add server-side form validation using the features in Spring Boot (Java) or WTForms (Python) to add the following server-side validation options to the Edit Movie form:

  • Title must be not null and at least 1 character long
  • Genre must be not null and at least 1 character long
  • MPAA Rating must be one of “G”, “PG”, “PG-13”, “R”
  • IMDB Rating must be a floating point value between 0 and 10
  • Rotten Tomatoes Rating must be an integer value between 0 and 100

Time Requirements

Completing this project is estimated to require 1-2 hours.

Grading Rubric

This assignment will be graded based on the rubric below:

  • Title Validation: 20%
  • Genre Validation: 20%
  • MPAA Rating Validation: 20%
  • IMDB Rating Validation: 20%
  • Rotten Tomatoes Rating Validation: 20%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.

Java

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> java
  1. Install SDKMAN

Instructions

curl -s "https://get.sdkman.io" | bash
  1. Close and Reopen Terminal to load SDK Man

  2. Install Gradle

sdk install gradle 7.6
  1. Compile, Run & Test Existing Project
cd java
gradle run
gradle check
  1. Confirm that project runs and has no style errors.

  2. Follow along with the video to update the project.

  3. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.

Python

YouTube Video

Outline

Here is a basic outline of the steps to follow to complete this example.

  1. Clone Starter Code from GitHub
git clone <url> python
  1. Run Project
cd python
python3 -m src
  1. Install Tox
pip3 install tox
  1. Check & Test Existing Project
python3 -m tox
  1. Confirm that project runs and has no style errors.

  2. Follow along with the video to update the project.

  3. When complete, use Git to commit and push updated code.

git add .
git commit -m "Example Complete"
git push
  1. On GitHub, create a release tag and submit URL to Canvas for grading.