In this post I will show how to solve the problems from topic 4 - Unit Tests of CS50’s - Introduction to Python course.

Table of Contents

Testing my twttr

In this problem we will reimplement Setting up my twttr from Problem Set 2, restructuring our code per the below, wherein shorten expects a str as input and returns that same str but with all vowels (A, E, I, O, and U) omitted, whether inputted in uppercase or lowercase.

def main():
    ...

def shorten(word):
    ...

if __name__ == "__main__":
    main()

I reimplemented Setting up my twttr as follows:

def main():
    text = input("Input: ")
    print(shorten(text))

def shorten(word):
    str = ""
    for char in word:
        if (char.lower() not in "aeiou"):
            str += char

    return str

if __name__ == "__main__":
    main()

Now we will implement the test file. It will test the our implementation of the shorten function. For this I creted some functions inside test_twttr file each of whose names begins with test_ so that I could execute my tests with:

pytest test_twttr.py

To use pytest we must intall it:

pip install -U pytest

The program to test the shorten function will be as follows:

The assert is used to compare result between the value result from function and expected value.

import twttr

def main():
    test_twttr()
    test_number()
    test_punctuation()

def test_twttr():
    assert twttr.shorten("twitter") == "twttr"
    assert twttr.shorten("TEXT") == "TXT"
    assert twttr.shorten("LaTeX") == "LTX"

def test_number():
    assert twttr.shorten("123") == "123"

def test_punctuation():
    assert twttr.shorten(",.?") == ",.?"

if __name__ == "__main__":
    main()

GitHub repository

Back to the Bank

In this problem we will reimplement Home Federal Savings Bank restructuring this code per the below, wherein value expects a str as input and returns an int, namely 0 if that str starts with “hello”, 20 if that str starts with an “h” (but not “hello”), or 100 otherwise, treating the str case-insensitively. We can assume that the string passed to the value function will not contain any leading spaces. Only main should call print.

def main():
    ...

def value(greeting):
    ...

if __name__ == "__main__":
    main()

In this case I reimplemented Home Federal Savings Bank as per the bellow:

def main():
    greetings = input("Greeting: ")
    print(f"${value(greetings)}")

def value(greeting):
    greeting = greeting.strip()
    greeting = str.lower(greeting)

    if greeting.startswith("hello"):
        return 0
    elif greeting.startswith("h"):
        return 20
    else:
        return 100

if __name__ == "__main__":
    main()

Now we create a test file to test the value function:

To use pytest we must intall it:

pip install -U pytest
  • test_hello: Testes the word “hello” to be case-insensitive.
  • test_h: testes words that starts with “h”.
  • test_others: tests words that starts with other character than “h”.

The assert is used to compare result between the value result from function and expected value.

import bank

def main():
    test_hello()
    test_h()
    test_others()


def test_hello():
    assert bank.value("hello") == 0
    assert bank.value("Hello") == 0
    assert bank.value("HELLO") == 0

def test_h():
    assert bank.value("hi") == 20
    assert bank.value("How are you") == 20
    assert bank.value("H") == 20
    assert bank.value("h") == 20

def test_others():
    assert bank.value("Whats your name?") == 100
    assert bank.value("Just saying hi") == 100


if __name__ == "__main__":
    main()

GitHub repository

Re-requesting a Vanity Plate

This problem asks to reimplement Vanity Plates restructuring our code per the below, wherein is_valid still expects a str as input and returns True if that str meets all requirements and False if it does not, but main is only called if the value of __name__ is "__main__":

def main():
    ...

def is_valid(s):
    ...

if __name__ == "__main__":
    main()

My reimplementation of Vanity Plates is as follows:

def main():
    plate = input("Plate: ")
    if is_valid(plate):
        print("Valid")
    else:
        print("Invalid")


def is_valid(s):
    size = len(s)

    if size < 2 or size > 6:
        return False

    for i in range(2):
        if (s[i].isalpha() != True):
            return False

    for i in range(size):
        if i < size - 1:
            j = i + 1

        if s[i].isalnum() != True:
            return False

        # Verify if after the number has a letter
        if s[i].isnumeric() and s[j].isalpha():
            return False

    # Verify if the first number is zero
    count = 1
    while s[count].isnumeric() != True and count < size - 1:
        if s[count + 1] == '0':
            return False
        count += 1

    return True

if __name__ == "__main__":
    main()

The program that tests the function is_valid will then be:

The assert is used to compare result between the value result from function and expected value.

import plates

def test_valid():
    assert plates.is_valid("CS50") == True

def test_starts_with_0():
    assert plates.is_valid("CS05") == False

def test_number_in_middle():
    assert plates.is_valid("CS50P") == False

def test_puctuation():
    assert plates.is_valid("PI3.14") == False

def test_one_letter():
    assert plates.is_valid("H") == False

def test_more_than_six_digits():
    assert plates.is_valid("OUTATIME") == False

def test_two_first_not_letter():
    assert plates.is_valid("1CS5") == False
    assert plates.is_valid("C123") == False
    assert plates.is_valid("1234") == False

GitHub repository

Refueling

In this project we must reimplement Fuel Gauge from Problem Set 3, restructuring our code per the below, wherein:

  • convert expects a str in X/Y format as input, wherein each of X and Y is an integer, and returns that fraction as a percentage rounded to the nearest int between 0 and 100, inclusive. If X and/or Y is not an integer, or if X is greater than Y, then convert should raise a ValueError. If Y is 0, then convert should raise a ZeroDivisionError.
  • gauge expects an int and returns a str that is:
    • “E” if that int is less than or equal to 1,
    • “F” if that int is greater than or equal to 99,
    • and “Z%” otherwise, wherein Z is that same int.
def main():
    ...

def convert(fraction):
    ...

def gauge(percentage):
    ...

if __name__ == "__main__":
    main()

My reimplementation for this is:

def main():
    while True:
        try:
            n = input("Fraction: ")
            fuel = convert(n)
        except (ValueError, ZeroDivisionError):
            pass
        else:
            break

    print(gauge(fuel))

def convert(fraction):
    numerator, denominator = fraction.split('/')
    numerator = int(numerator)
    denominator = int(denominator)

    if denominator == 0:
        raise ZeroDivisionError

    percentage = round(numerator / denominator * 100)

    if percentage < 0 or percentage > 100:
        raise ValueError
    else:
        return percentage

def gauge(percentage):
    if percentage <= 1:
        return "E"
    elif percentage >= 99:
        return "F"
    else:
        return f"{percentage}%"

if __name__ == "__main__":
    main()

The program that tests the function convert and gauge will then be:

We import pytest to use the method raises to raise the errors.

import fuel
import pytest

def test_convert():
    assert fuel.convert("3/4") == 75
    assert fuel.convert("1/4") == 25
    assert fuel.convert("4/4") == 100
    assert fuel.convert("0/4") == 0

def test_errors():
    with pytest.raises(ZeroDivisionError):
        fuel.convert("4/0")
    with pytest.raises(ValueError):
        fuel.convert("three/four")
    with pytest.raises(ValueError):
        fuel.convert("1.5/3")

def test_gauge():
    assert fuel.gauge(75) == "75%"
    assert fuel.gauge(33) == "33%"
    assert fuel.gauge(0) == "E"
    assert fuel.gauge(1) == "E"
    assert fuel.gauge(99) == "F"
    assert fuel.gauge(100) == "F"

The assert is used to compare result between the value result from function and expected value.

GitHub repository

This is all for week five - Unit Tests, set of problems of CS50’s Introduction to Programming with Python course.