Ahoy, fellow developers! 🌊 Ever tried to sail the uncharted waters of someone else's codebase? Well, grab your compasses and let me regale you with my latest adventure in setting up Continuous Integration (CI) and writing tests for a project that wasn't mine. Spoiler alert: it involved a lot of mocking—both the technical kind and, well, the other kind too!
Setting Sail with GitHub Actions CI Workflow
First things first, I needed to set up a CI workflow for my own repository. I wanted to automate the testing process so that every push would run my tests and ensure everything was shipshape.
Here's what my .github/workflows/ci.yml
looked like:
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
run: |
python -m unittest discover
Pretty standard stuff, right? This workflow checks out the code, sets up Python, installs dependencies, and runs the tests. Simple, yet effective—like a trusty compass.
Boarding the Ship: Writing Tests for a Stranger's Repo
Now, here's where the real adventure began. I was tasked with writing tests for HTSagara's readme_genie
repository. Imagine being handed a treasure map in a language you only partially understand. Challenge accepted!
The Quest for Meaningful Tests
I decided to focus on the read_file_content
function, which, as the name suggests, reads file content. I thought, "What could possibly go wrong with reading files?" Turns out, plenty—if you're testing!
I wrote tests to handle various scenarios:
- Empty Files
- Files with Only Comments
- Large Files
Here's a peek at one of the tests:
@patch("builtins.open", new_callable=mock_open, read_data="")
def test_read_file_content_empty_file(self, mock_file):
"""Test read_file_content with an empty file."""
file_content = read_file_content(["empty_file.py"])
self.assertEqual(file_content, "\n\n")
Mocking Files Like a Pro
Since I didn't want to actually create files, I used unittest.mock
to simulate file operations. This was my first time heavily relying on mocking, and it felt like I was crafting illusions—like a code wizard!
Writing tests for someone else's codebase was like deciphering a coded message. I had to understand the function's intent, its edge cases, and how it handled unexpected input. It was both challenging and exhilarating.
Comparing Ships: My Repo vs. My Partner's
In my own repo, I had a cozy setup with straightforward tests. In contrast, HTSagara's repo was a grand vessel with intricate details. Their testing setup was minimal, so I had the freedom (and responsibility) to expand it.
The main differences were:
- Test Coverage: My repo had tests covering most functions, while theirs had room for more.
- Complexity: Their functions handled more edge cases, requiring more thoughtful tests.
- CI Integration: Both of us used GitHub Actions, but the workflows had slight variations.
The Treasure of Continuous Integration
After setting up CI, I must say—I'm hooked! Having automated tests run on every push is like having a vigilant lookout who never sleeps. It catches issues early, ensures code quality, and gives peace of mind.
CI has transformed from a mysterious acronym to an indispensable tool in my development arsenal. It's like upgrading from a rowboat to a schooner.
Optional Challenges: Tackling the Behemoth
One of the optional challenges was to test the function's performance with a large file. I simulated a file with 1000 lines to see how the function held up:
@patch("builtins.open", new_callable=mock_open, read_data="line\n" * 10**6)
def test_read_file_content_large_file(self, mock_file):
"""Test read_file_content with a large file."""
start_time = time.time()
file_content = read_file_content(["large_file.py"])
end_time = time.time()
elapsed_time = end_time - start_time
expected_content = ("line\n" * 10**6) + "\n\n"
self.assertEqual(file_content, expected_content)
self.assertLess(elapsed_time, 2, "Processing large files took too long!")
This test not only checked if the function could handle large files but also if it did so efficiently. I'm happy to report that it passed with flying colors!
Lessons Learned and Final Thoughts
- Writing Tests for Others: It's a fantastic way to deepen your understanding of testing and code structure.
-
Mocking is Powerful:
unittest.mock
is now a close friend. It opens up possibilities for testing without relying on actual I/O operations. - CI is Essential: Automating tests saves time and reduces errors. It's like having a trusty first mate.
Overall, this lab was a thrilling voyage through the seas of CI and testing. I encourage everyone to venture beyond their comfort zones—who knows what treasures you'll find?
Fair winds and following seas! 🌬️🚢
Feel free to share your own adventures or ask questions in the comments below. Happy coding!
Top comments (0)