JECRC University Students: Python Test Automation With Pytest From Scratch

JECRC University Students: Python Test Automation With Pytest From Scratch

Facebook
Twitter
LinkedIn
WhatsApp
Email

You can learn pytest from zero in 14 days if you work in stages: single assertions first, then exceptions, parametrisation, fixtures, and finally a small app tested end to end. The plan below gives you seven stages, concrete exercises for each, and a tracker to show your own progress. All code is illustrative, so type it out yourself and run it on your machine.

Why pytest is a sensible first automation tool for a Python student

pytest lets you write tests as plain Python functions. You do not need to inherit from a base class or memorise a dozen assertion methods. You write assert something == expected, and when it fails, pytest prints the values on both sides. That feedback loop is why it suits beginners, and the same habits carry over to the larger frameworks used in industry.

What you need before Day 1:

  • Python 3.9 or newer installed, and a terminal you are comfortable with.
  • A virtual environment (python -m venv .venv), activated before every session.
  • pytest installed inside it (pip install pytest), then pytest --version to check.
  • A folder named cart_project with two subfolders, app and tests.
  • A Git repository, so each stage ends in a commit you can show later.

The small shopping-cart app you will test throughout

Using one tiny app for all stages means you see the same code from different testing angles. Save the following as app/cart.py. It is deliberately simple and is an illustrative example, not production code.

class Cart:
    def __init__(self):
        self.items = {}

    def add(self, name, price, qty=1):
        if price < 0 or qty < 1:
            raise ValueError("invalid price or quantity")
        if name in self.items:
            self.items[name]["qty"] += qty
        else:
            self.items[name] = {"price": price, "qty": qty}

    def remove(self, name):
        del self.items[name]  # raises KeyError if missing

    def total(self):
        return sum(i["price"] * i["qty"] for i in self.items.values())

    def apply_discount(self, percent):
        if not 0 <= percent <= 100:
            raise ValueError("discount must be 0-100")
        return round(self.total() * (1 - percent / 100), 2)

Add an empty app/__init__.py, and a pytest.ini in the project root containing [pytest] and pythonpath = . so that from app.cart import Cart works from the tests folder.

Free 2-Week CareerSwami Internship

Online, 14 days, with a free offer letter and a free completion certificate once you finish the work. Check with your college that it fits your requirement before you enrol.

Apply for the free internship

The 14-day pytest roadmap at a glance

Stage Days Focus Output you should have
1 1-2 First assertions and test discovery 5 passing tests, 1 deliberately failing test you can explain
2 3-4 Exceptions and edge cases Tests using pytest.raises for every error path
3 5-6 Parametrisation Data-driven tests for totals and discounts
4 7-8 Fixtures and conftest.py Shared setup with at least two fixtures
5 9-10 Markers, tmp_path, monkeypatch Tagged test groups and one file-based test
6 11-12 Behaviour-level tests and coverage A readable suite with a coverage report
7 13-14 Bug hunt, README, portfolio write-up A repository another person can clone and run

Stage 1 (Days 1-2): write your first assertions

pytest finds files named test_*.py and functions named test_*. Create tests/test_cart_basic.py and start small.

  1. Empty cart total. Create a Cart, assert total() == 0.
  2. One item. Add a pen at 10, quantity 1, assert the total is 10.
  3. Quantity maths. Add a pen at 10, quantity 3, assert 30.
  4. Merging lines. Add the same item twice and assert that the quantity is merged, using cart.items["pen"]["qty"].
  5. Remove. Add then remove an item and assert the cart is empty.
  6. A deliberate failure. Write a wrong expectation on purpose and read the output line by line. Then fix the test, not the app.

Run pytest -v after every test. Learn three flags now: -v (verbose), -x (stop at first failure), and -k name (run tests whose name matches).

Stage 2 (Days 3-4): test the failure paths with pytest.raises

Good testers spend as much time on invalid input as on happy paths. pytest.raises checks that the right exception appears, and its match argument checks the message.

import pytest
from app.cart import Cart

def test_negative_price_rejected():
    cart = Cart()
    with pytest.raises(ValueError, match="invalid"):
        cart.add("pen", -5)

Exercises for this stage:

  • Zero quantity raises ValueError.
  • Removing an item that was never added raises KeyError.
  • A discount of 101 and a discount of -1 both raise ValueError.
  • Boundary values: 0 and 100 percent must not raise. Assert their results (full price and 0.0).
  • Write one test that you expect to reveal a gap. For example, does add accept a quantity of 2.5? Record what you find in a notes file instead of silently changing the app.

Stage 3 (Days 5-6): replace copy-pasted tests with parametrize

By now you probably have several nearly identical tests. @pytest.mark.parametrize turns them into one test with a table of inputs, and each row is reported separately.

import pytest
from app.cart import Cart

@pytest.mark.parametrize("percent, expected", [
    (0, 100.0),
    (10, 90.0),
    (50, 50.0),
    (100, 0.0),
])
def test_discount_on_100_rupee_cart(percent, expected):
    cart = Cart()
    cart.add("book", 100)
    assert cart.apply_discount(percent) == expected

Exercises:

  1. Parametrise the total test across at least six (price, quantity, expected) rows.
  2. Parametrise invalid discounts, expecting ValueError.
  3. Add an ids= list so failures read like sentences, not test_x[3-2-7].
  4. Add a rounding case such as price 33.33 with a 10 percent discount, and decide what the correct expected value is before you run it.

Stage 4 (Days 7-8): fixtures for shared setup

A fixture is a function that prepares something a test needs. Tests ask for it by naming it as a parameter. Put shared fixtures in tests/conftest.py and pytest finds them automatically.

import pytest
from app.cart import Cart

@pytest.fixture
def cart():
    return Cart()

@pytest.fixture
def filled_cart(cart):
    cart.add("pen", 10, 3)
    cart.add("notebook", 50, 2)
    return cart

With that in place, a test becomes short:

def test_total_of_filled_cart(filled_cart):
    assert filled_cart.total() == 130

Exercises:

  • Refactor all earlier tests to use the cart fixture instead of creating Cart() by hand.
  • Build filled_cart on top of cart, as above, and write three tests against it.
  • Write a yield fixture that prints “setup” before and “teardown” after, then run pytest -s to see the order.
  • Try fixture scope: change one fixture to scope="module". Then show with a test why sharing a mutable cart between tests can cause order-dependent failures. This is the most important lesson of the stage.

Stage 5 (Days 9-10): markers, tmp_path and monkeypatch

Add a small feature first: a method export_csv(self, path) that writes one line per item. Write it yourself; it is part of the exercise.

  • tmp_path: pytest gives each test a fresh temporary folder. Call cart.export_csv(tmp_path / "cart.csv"), read the file back, and assert on its lines. Your real disk stays clean.
  • Markers: tag slow or experimental tests with @pytest.mark.slow. Register the marker under markers = in pytest.ini, then run pytest -m "not slow".
  • skip and xfail: mark a known, unfixed bug with @pytest.mark.xfail(reason="...") so the suite stays honest about it.
  • monkeypatch: if you add a discount read from an environment variable such as CART_DISCOUNT, use monkeypatch.setenv to test it without touching your real environment.

A worked illustrative example: from a failing assertion to a fixed test

This example is illustrative; the numbers are worked by hand rather than taken from a real run. Suppose you write the following in Stage 1:

def test_discount_pen_pack():
    cart = Cart()
    cart.add("pen", 10, 3)
    assert cart.apply_discount(10) == 28

The cart total is 10 x 3 = 30. A 10 percent discount leaves 30 x 0.9 = 27.0. The assertion expects 28, so pytest would fail it and show the left side (27.0) against the right side (28). Now ask the right question: is the app wrong or is the test wrong? Check the requirement. If “10 percent off” means 3 rupees off 30, then 27.0 is correct and the test must change to == 27.0.

The habit to build is to decide the expected value from the requirement before you look at what the code returns. If you copy the actual output into the expectation, the test can never fail and proves nothing.

Stage 6 (Days 11-12): turn the suite into readable behaviour checks

Now step back from single methods and test user-visible behaviour. Name tests as sentences, such as test_removing_last_item_leaves_zero_total.

  1. Write a flow test: add three items, remove one, apply a discount, check the final amount.
  2. Group related tests into classes (class TestDiscounts:) and see how pytest reports them.
  3. Install pytest-cov and run pytest --cov=app --cov-report=term-missing. Find the lines that no test reaches and write tests for them.
  4. Delete a line of the app on purpose (for example, the price < 0 check) and see whether any test fails. If none does, your suite has a blind spot. This simple form of mutation testing teaches more than a coverage percentage does.

Stage 7 (Days 13-14): bug hunt and a portfolio-ready repository

Spend Day 13 hunting bugs in your own app. Try a 100 percent discount on an empty cart, a very large quantity, a price with many decimals, and removing then re-adding an item. Record each finding in a short table: input, expected, actual, verdict. Spend Day 14 packaging.

  • A README with setup steps, the command to run the tests, and a one-paragraph summary of what is covered.
  • A requirements.txt listing pytest and pytest-cov.
  • Your bug table as FINDINGS.md.
  • Clean commit messages, one per stage.

If you want a structured place to practise alongside this plan, CareerSwami’s free two-week online internship offers 26 tracks, a free offer letter and a free completion certificate after successful completion. Pick a testing-related track if one fits, and use your pytest repository as your own working evidence.

Your pytest progress tracker

Copy this table into a notes file and update it daily. Mark an item done only when you could explain it to a classmate without looking at your code.

Skill Day target Evidence in your repo Status (Not started / In progress / Done)
Run tests with -v, -x, -k 2 Notes with one example of each
Read a failing assertion and fix the right side 2 Commit message describing the fix
Use pytest.raises with match 4 At least 4 error-path tests
Write a parametrised test with ids 6 2 parametrised tests, 6+ rows each
Create fixtures in conftest.py 8 2 fixtures, one depending on the other
Explain fixture scope and shared-state risk 8 A test or note that demonstrates it
Use tmp_path 10 CSV export test
Register and use a custom marker 10 pytest.ini entry and a filtered run
Check coverage and close a gap 12 Coverage output before and after
Deliberately break the app and catch it 12 Note on which test failed
Publish README and findings table 14 Repository another person can clone and run

Habits that separate useful tests from decorative ones

  • One reason to fail. A test with five assertions hides which behaviour broke. Prefer several small tests.
  • Independent tests. Run pytest -p no:randomly or shuffle the order and confirm nothing depends on another test’s leftovers.
  • Specific expectations. Assert exact values and messages where they matter, and avoid assert result when you know what the result should be.
  • Tests as documentation. A teammate should understand the rule from the test name alone.
  • Fix the cause. If a test is flaky, find out why; adding sleeps or reruns only hides the problem.

JECRC University students: fitting this pytest practice around your academic structure

Hands-on testing practice sits alongside, not inside, your formal degree requirements, so it helps to know what the university’s own documents do and do not say. JECRC’s regulations on academic evaluation under the CGPA scheme say the final-year internship is assessed by a committee of internal and external examiners set up by the Board of Studies chairman. The same document gives credits to practical training, seminars and projects. However, it states no internship duration, credit count, semester, required documents or online-internship rule. A 14-day self-paced pytest project is therefore best treated as skill-building and portfolio evidence, not as something that automatically satisfies a university requirement.

Timing matters too. The regulations describe an odd semester starting in July/August, an even semester in January, and an optional Summer Semester during the May to July vacation. The official Academic Calendar page lists separate 2026-27 calendar downloads for each school, so your own school’s PDF is the one to use when choosing a 14-day window away from exams.

For B.Tech CSE students, an official CSE scheme PDF for the 2025-2029 batch shows a course table with codes, L-T-P, credits and course type, and lists Data Structures and Algorithms as a 4-credit core course. That is a useful companion: writing tests for your own data-structure code is a strong Stage 6 exercise. Scheme documents are batch-specific, so check that you are reading the version for your admission year.

Steps to take before you rely on any of this:

  1. Download your school’s 2026-27 calendar from the Academic Calendar page and note the exam and break dates before choosing your 14 days.
  2. Find the scheme or syllabus PDF for your programme and batch, and look for any software testing, project or training course that matches this skill.
  3. Email your department or the Board of Studies contact in writing, asking whether an online internship of any length is accepted for your programme, and keep the reply.
  4. Ask your final-year coordinator which documents the evaluation committee expects (for example, a report or a supervisor letter), so you collect the right evidence early.
  5. Ask the training and placement office, in writing, how they treat self-directed technical projects and certificates when advising on internships.

Where to go after the 14 days

With the roadmap complete, extend the same repository rather than starting a new one. Add a second small module (a login validator or a marks calculator), reuse your fixtures, and then try testing a tiny Flask or FastAPI endpoint with its test client. Later, learn how pytest runs in a CI pipeline such as GitHub Actions, because a visible green build on your repository is a convincing signal. Keep your tracker; the habit of recording evidence is the same one professional testers use.

Frequently Asked Questions

Do I need to know advanced Python before starting pytest?

No. If you understand functions, classes, dictionaries and exceptions, you can start. Stage 1 uses only plain assertions, and the rest of the plan introduces features one at a time.

What is the difference between a pytest fixture and a setup method?

A fixture is requested by name, can be shared across files through conftest.py, and can depend on other fixtures. Setup methods apply to everything in a class whether a test needs them or not, so fixtures are usually clearer and more flexible.

How many tests are enough for a small app like the cart?

There is no magic number. Aim to cover each public method, each error path and the boundary values (0, 1, maximum, just outside the limit). Then use coverage and deliberate code changes to find the gaps, rather than counting tests.

Why does pytest not find my test file?

Usually the filename does not start with test_, the function name does not start with test, or the import path is wrong. Run pytest --collect-only to see what it discovers, and check that pythonpath = . is set in pytest.ini.

Should I test private helper functions?

Mostly no. Test behaviour through the public interface. If a private helper feels too complex to leave untested, that is often a sign it should become its own small, public function.

Can I use this pytest project as evidence when applying for testing roles?

Yes, as a portfolio piece. A repository with a clear README, parametrised tests, fixtures, a coverage report and a bug-findings table shows practical ability. Describe it accurately as a self-directed learning project.

Is a 14-day pytest plan realistic alongside college classes?

It is if you plan for about 60 to 90 focused minutes a day. If your exams fall inside the window, stretch the plan or pick a lighter period using your school’s calendar. Finishing fewer stages with real understanding beats rushing all seven.

Which CareerSwami track suits someone learning pytest?

The free two-week online internship has 26 tracks, so look through them for a software testing or Python-related option and match its tasks to the stages above. Check the track description on the CareerSwami site before enrolling.

Related Software Testing guides

Ready to put this into practice?

Join the free two-week CareerSwami online internship in Software Testing. The internship, the offer letter and the completion certificate are free.

Start your free 2-week internship

Web Stories

Scroll to Top