def generate_sine_wave(frequency, amplitude, duration, sample_rate=1000): t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False) y = amplitude * np.sin(2* np.pi * frequency * t)return t, yt, y = generate_sine_wave(1, 2, -1) # duration = -1assertlen(t) ==0assertlen(y) ==0
Edge Cases Matter
---------------------------------------------------------------------------ValueError Traceback (most recent call last)
Cell In[19], line 6 3 y = amplitude * np.sin(2* np.pi * frequency * t)
4return t, y
----> 6 t, y =generate_sine_wave(1,2,-1)# duration = -1 7assertlen(t) ==0 8assertlen(y) ==0
Cell In[19], line 2, in generate_sine_wave(frequency, amplitude, duration, sample_rate) 1defgenerate_sine_wave(frequency, amplitude, duration, sample_rate=1000):
----> 2 t =np.linspace(0,duration,int(sample_rate*duration),endpoint=False) 3 y = amplitude * np.sin(2* np.pi * frequency * t)
4return t, y
File ~/miniforge3/envs/test-doc-pack_env/lib/python3.10/site-packages/numpy/_core/function_base.py:130, in linspace(start, stop, num, endpoint, retstep, dtype, axis, device) 128 num = operator.index(num)
129if num <0:
--> 130raiseValueError(
131"Number of samples, %s, must be non-negative."% num
132 )
133 div = (num -1) if endpoint else num
135 conv = _array_converter(start, stop)
ValueError: Number of samples, -1000, must be non-negative.
Fixing Edge Cases
Let’s make the function handle negative durations.
def generate_sine_wave(frequency, amplitude, duration, sample_rate=1000):if duration <0:return np.array([]), np.array([]) t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False) y = amplitude * np.sin(2* np.pi * frequency * t)return t, yt, y = generate_sine_wave(1, 2, -1) # duration = -1assertlen(t) ==0assertlen(y) ==0
✅ Now all tests pass.
Another Edge Case
What if amplitude is 0?
t, y = generate_sine_wave(5, 0, 1)assert np.allclose(y, 0)
✅ Works fine — sine wave is just flat zero.
Test-Driven Development (TDD)
TDD mindset:
Write the test first.
Run it (it fails).
Write the code to make it pass.
Refactor if needed.
What you just did in the previous couple of slides was TDD without realizing it! 🎉
Why TDD Helps
Forces you to define expected behavior first
Encourages modular design
Reduces debugging time
Builds confidence in your code
Simple checklist for writing tests
Call the function (that you want to test) with known inputs.
Use assert statements to check outputs against expected results.
Cover normal cases and edge cases.
Keep tests independent of each other, i.e. call the function afresh in each test.
Try using descriptive names for test functions.
Using pytest
First, let’s make a function that contains all our tests.
def test_generate_sine_wave(): t, y = generate_sine_wave(1, 2, 1)assertlen(t) ==1000assert y[0] ==0 t, y = generate_sine_wave(5, 3, 1)assert np.isclose(max(y), 3, atol=1e-6) t, y = generate_sine_wave(1, 2, -1)assertlen(t) ==0andlen(y) ==0 t, y = generate_sine_wave(5, 0, 1)assert np.allclose(y, 0)
Run It Manually
Call the test function in your Python file:
test_generate_sine_wave()
And run the Python file. If nothing happens, all tests passed.
✅ All tests pass.
But what if we have dozens of functions? (maybe spread across multiple files?) We need automation.
👉 Enter pytest.
Installing pytest
In your terminal:
pip install pytest
Running pytest
Add your test_generate_sine_wave function to a file named test_my_function.py.
Then run:
pytest test_my_function.py
pytest Output Example
============================= test session starts ==============================collected 1 itemtest_my_function.py . [100%]============================== 1 passed in 0.05s ===============================
. = test passed ✅
F = test failed ❌
Seeing a Failure
Let’s add a broken test to the same file see what happens. The time-step is wrong on purpose.
def test_we_want_to_see_a_fail(): t, y = generate_sine_wave(1, 2, 1)assertlen(t) ==999
Seeing a Failure
Run again with pytest. You’ll see an F and details about the failure.