0

Disclaimer: This is my first question here, so apologies if it's framed poorly, please ask for clarifications if required.

This is the function that I want to test:

def driver():
    n = int(input("Enter number of rows: "))
    m = int(input("Enter number of columns: "))
    if n == 0 or m == 0:
        raise ValueError("Invalid grid size.")
    grid = []
    for row in range(n):
        row_i = input("Enter the space-separated cells of row " + str(row + 1) + ": ")
        row_i = list(map(lambda x: int(x), row_i.split()))
        if len(row_i) != m:
            raise IndexError("Invalid input for the given number of columns.")
        if any([cell not in [0, 1] for cell in row_i]):
            raise ValueError("Invalid value of cell, a cell can only have 0 or 1 as a value.")
        grid.append(row_i)
    print("Initial grid: ")
    print_grid(grid)

The tests I have written are as follows:

class TestGameOfLife(unittest.TestCase):
    def setUp(self):
        self.driver = game_of_life.driver

    @mock.patch('game_of_life.input', create=True)
    def test_driver_invalid_num_rows(self, mocked_input):
        mocked_input.side_effect = ["0", "5"]
        self.assertRaisesRegex(ValueError, "Invalid grid size.", self.driver)

The issue with this is that this test itself and the lines of codes it's trying to test are not included in the coverage. So I reckon it's not supposed to be done this way. Can anyone help me with how I should be testing it instead?

I took inspiration from this post to write my unit tests, but given that neither the code nor the tests are included in coverage, this is probably not the appropriate way to do this in my case.

4
  • This seems weird. Are you sure this test is actually runned ? What are you using to get your coverage ? Commented Jun 23, 2023 at 13:07
  • 2
    This may be a good question for Code Review: your driver function could be rewritten to be more testable. You should separate "get the input from the user" from "create the grid", and then for testing you can provide synthetic input to the "create the grid" logic. Commented Jun 23, 2023 at 13:09
  • "I reckon it's not supposed to be done this way" correct! You have just discovered one of the primary benefits of unit testing code: it tells you when your design is wrong. In this case as larsks said you are struggling because your function does too many things. Have two functions: one that gets input and dispatches to a second that does the logic. The I/O function doesn't need to be tested at all, the second one with the logic should be easy to test. Commented Jun 23, 2023 at 13:20
  • @Axnyff I'm using python3 -m coverage html Commented Jun 23, 2023 at 15:12

1 Answer 1

0

I left some thoughts in a comment, but to answer your specific question it is possible to provide synthetic stdin and stdout to a function so that you can provide it with test input and read the resulting output.

Assuming that we have a simplified version of your driver code in driver.py:

def driver():
    n = int(input("Enter number of rows: "))
    m = int(input("Enter number of columns: "))
    if n == 0 or m == 0:
        raise ValueError("Invalid grid size.")

    print(f"grid: {n}x{m}")

We can test it like this:

import io
import sys
import pytest

import driver


def test_driver():
    stdin = io.StringIO("10\n10\n")
    stdout = io.StringIO()
    sys.stdin = stdin
    sys.stdout = stdout
    driver.driver()
    assert "grid: 10x10" in stdout.getvalue()


def test_driver_invalid_input():
    stdin = io.StringIO("x\ny\n")
    sys.stdin = stdin
    with pytest.raises(ValueError):
        driver.driver()
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! However, I've modified my code and there are still some coverage issues. Please find below: ``` def driver(): rows, cols = get_grid_size() grid = create_grid(rows, cols) ``` ``` def get_grid_size(): rows = int(input("Enter number of rows: ")) cols = int(input("Enter number of columns: ")) if not rows or not cols: raise ValueError("Invalid grid size.") return rows, cols ``` The test code is the same as yours except it uses unittest. But still get_grid_size() is missing from the coverage.
On another note, there is some code that I haven't tested, but the tool says otherwise, so is it possible that the tool is inaccurate or is that because I'm probably testing it implicitly in some other test?
You can't usefully post code in a comment. You're welcome to edit your answer to modify it or add new content. W/r/t your second question, that generally means the code is called implicitly via one of your existing tests.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.