0
0
PyTesttesting~15 mins

Comparing values (equality, inequality) in PyTest - Deep Dive

Choose your learning style9 modes available
Overview - Comparing values (equality, inequality)
What is it?
Comparing values means checking if two things are the same or different. In testing, we often want to see if the output of our code matches what we expect. Equality means the values are the same, and inequality means they are not the same. Using pytest, we write tests that compare values to find bugs or confirm correct behavior.
Why it matters
Without comparing values, we cannot know if our code works correctly. Imagine baking a cake without tasting it; you wouldn't know if it’s good. Similarly, tests that compare values help catch mistakes early, saving time and effort. Without these checks, software would be unreliable and frustrating to use.
Where it fits
Before learning this, you should understand basic Python syntax and how to write simple functions. After this, you can learn about more complex assertions, test fixtures, and parameterized tests to write better automated tests.
Mental Model
Core Idea
Comparing values in tests is like asking a yes/no question: 'Is this what I expected?'
Think of it like...
It's like checking if two puzzle pieces fit perfectly together. If they do, the picture is correct; if not, something is wrong.
┌───────────────┐
│  Expected     │
│   Value       │
└──────┬────────┘
       │ Compare
┌──────▼────────┐
│  Actual       │
│  Value        │
└──────┬────────┘
       │ Result
   ┌───▼─────┐
   │ Pass/Fail│
   └─────────┘
Build-Up - 7 Steps
1
FoundationBasic equality assertion in pytest
🤔
Concept: Learn how to check if two values are equal using pytest's assert statement.
In pytest, you use the simple assert keyword to check if two values are equal. For example: def test_sum(): result = 2 + 3 assert result == 5 This test passes if result equals 5, otherwise it fails.
Result
Test passes if the values are equal; fails if not.
Understanding that assert checks a condition directly is the foundation of writing tests.
2
FoundationUsing inequality assertions
🤔
Concept: Learn how to check if two values are not equal using pytest.
You can check that two values are different using != in an assert: def test_not_equal(): value = 'apple' assert value != 'orange' This test passes if value is not 'orange'.
Result
Test passes if values differ; fails if they are the same.
Knowing how to assert inequality helps test that unwanted or unexpected values do not appear.
3
IntermediateComparing complex data structures
🤔Before reading on: do you think assert checks nested lists element-by-element or just references? Commit to your answer.
Concept: Learn how pytest compares lists, dictionaries, and other complex types for equality.
Pytest's assert compares complex data by checking each element or key-value pair. For example: def test_list_equality(): a = [1, 2, 3] b = [1, 2, 3] assert a == b This passes because all elements match in order. Similarly, dictionaries are compared by keys and values: def test_dict_equality(): d1 = {'x': 1, 'y': 2} d2 = {'y': 2, 'x': 1} assert d1 == d2 Order does not matter for dict equality.
Result
Tests pass if all elements or key-value pairs match; fail otherwise.
Understanding element-wise comparison prevents confusion when tests fail on complex data.
4
IntermediateUsing pytest's detailed assertion introspection
🤔Before reading on: do you think pytest shows detailed info on assert failures automatically? Commit to yes or no.
Concept: Pytest shows exactly which parts differ when an assert fails, helping debugging.
When an assert fails, pytest prints the values and highlights differences. For example: def test_fail(): assert [1, 2, 3] == [1, 4, 3] Pytest output shows the mismatch at index 1: E assert [1, 2, 3] == [1, 4, 3] E At index 1 diff: 2 != 4 This helps quickly find the problem.
Result
Test fails with clear, helpful error messages.
Knowing pytest's introspection saves time by making test failures easier to understand.
5
IntermediateChecking floating point equality carefully
🤔Before reading on: do you think direct equality works well for floating point numbers? Commit to yes or no.
Concept: Floating point numbers can be tricky; use approximate comparisons to avoid false failures.
Due to tiny rounding errors, floats may not be exactly equal. Instead of assert a == b, use: import math def test_float(): a = 0.1 + 0.2 b = 0.3 assert math.isclose(a, b, rel_tol=1e-9) This test passes if a and b are close enough within a small tolerance.
Result
Tests pass for nearly equal floats, avoiding flaky failures.
Understanding floating point precision prevents confusing test failures on decimal calculations.
6
AdvancedCustom equality with __eq__ in objects
🤔Before reading on: do you think Python compares objects by value or by memory address by default? Commit to your answer.
Concept: Objects can define their own equality logic by implementing __eq__, affecting test comparisons.
By default, Python compares objects by their memory location, so two objects with same data may not be equal. class Point: def __init__(self, x, y): self.x = x self.y = y p1 = Point(1, 2) p2 = Point(1, 2) assert p1 != p2 # This passes because they are different objects To compare by value, define __eq__: class Point: def __init__(self, x, y): self.x = x self.y = y def __eq__(self, other): return isinstance(other, Point) and self.x == other.x and self.y == other.y p1 = Point(1, 2) p2 = Point(1, 2) assert p1 == p2 # Now passes because values match
Result
Tests can compare objects meaningfully by their content, not just identity.
Knowing how to customize equality lets you write meaningful tests for your own data types.
7
ExpertPitfalls of chained comparisons in asserts
🤔Before reading on: do you think assert a == b == c checks if all three are equal or just pairs? Commit to your answer.
Concept: Chained comparisons in asserts can behave unexpectedly and cause subtle bugs.
In Python, assert a == b == c means (a == b) and (b == c). This can be confusing if you expect all three to be equal directly. Example: a = 5 b = 5 c = 6 assert a == b == c # Fails because c != b But if you write: assert (a == b) and (b == c) It is the same, but sometimes parentheses or complex expressions cause confusion. Also, using assert a == b != c means (a == b) and (b != c), which can be tricky to read. Better to write separate asserts for clarity: assert a == b assert b == c
Result
Avoids subtle bugs and improves test readability.
Understanding Python's comparison chaining prevents hard-to-find test errors and improves clarity.
Under the Hood
Pytest uses Python's built-in assert statement, but enhances it by rewriting assert expressions at runtime. It parses the assert condition to extract the values and operators, then generates detailed error messages showing the exact values that failed. This is done by bytecode inspection and AST (abstract syntax tree) rewriting before the test runs.
Why designed this way?
The design allows tests to be written simply with assert, without special functions, while still providing rich failure information. This avoids extra boilerplate and makes tests easy to read and write. Alternatives like custom assert functions exist but are less natural and more verbose.
┌───────────────┐
│  Test Code    │
│  with assert  │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Pytest parses  │
│ assert AST     │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Executes test  │
│ and captures  │
│ values        │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ On failure,   │
│ generate      │
│ detailed info │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does assert a == b check if a and b are the same object or just equal in value? Commit to your answer.
Common Belief:Assert a == b checks if a and b are the exact same object in memory.
Tap to reveal reality
Reality:Assert a == b checks if a and b have equal values, not if they are the same object. To check identity, use 'is'.
Why it matters:Confusing equality with identity can cause tests to pass or fail unexpectedly, especially with mutable objects.
Quick: Do you think assert a != b is the opposite of assert a == b in all cases? Commit to yes or no.
Common Belief:Assert a != b is always the exact opposite of assert a == b.
Tap to reveal reality
Reality:In some cases, like with floating point NaN values, a == b and a != b can both be False, so they are not exact opposites.
Why it matters:Assuming strict opposites can lead to missing edge cases and false test results.
Quick: Does pytest automatically handle floating point rounding errors in assert a == b? Commit to yes or no.
Common Belief:Pytest assert a == b works perfectly for floating point numbers without extra care.
Tap to reveal reality
Reality:Pytest does not handle floating point precision issues automatically; you must use math.isclose or similar methods.
Why it matters:Ignoring floating point quirks causes flaky tests that fail randomly.
Quick: Does assert a == b == c check if all three values are equal simultaneously? Commit to your answer.
Common Belief:Assert a == b == c checks if a, b, and c are all equal at once.
Tap to reveal reality
Reality:It checks if a == b and b == c separately, which can behave differently than expected in some cases.
Why it matters:Misunderstanding chained comparisons can cause subtle bugs and confusing test failures.
Expert Zone
1
Pytest's assertion rewriting only works on assert statements, not on assert functions or custom helpers, so understanding this helps write better tests.
2
Comparing objects with __eq__ can cause infinite recursion if not implemented carefully, especially with inheritance and mixed types.
3
Floating point comparisons require domain knowledge to choose appropriate tolerances; too loose hides bugs, too tight causes flaky tests.
When NOT to use
Avoid using direct equality for floating point numbers; use approximate comparisons instead. For complex objects, if __eq__ is not defined or unreliable, compare attributes explicitly or use specialized libraries like dataclasses or attrs for equality. When testing side effects or state changes, consider other assertion types beyond equality.
Production Patterns
In real projects, tests often use pytest's assert for simple value checks, but also combine it with helper functions for complex comparisons. Custom __eq__ methods are common in domain models to enable meaningful equality tests. Floating point tests use math.isclose or pytest.approx for robust checks. Clear, separate asserts improve test readability and maintainability.
Connections
Mathematics: Equality and Inequality Relations
Builds-on
Understanding mathematical equality and inequality helps grasp how programming comparisons work and why some edge cases exist.
Computer Science: Object-Oriented Programming (OOP)
Builds-on
Knowing how objects define equality via __eq__ connects testing comparisons to core OOP principles.
Philosophy: Identity and Difference
Cross-domain analogy
Philosophical ideas about what makes things identical or different deepen understanding of equality vs identity in programming.
Common Pitfalls
#1Using assert a == b for floating point numbers directly.
Wrong approach:def test_float_fail(): a = 0.1 + 0.2 b = 0.3 assert a == b # This may fail due to precision
Correct approach:import math def test_float_pass(): a = 0.1 + 0.2 b = 0.3 assert math.isclose(a, b, rel_tol=1e-9)
Root cause:Misunderstanding floating point precision and expecting exact equality.
#2Comparing objects without defining __eq__.
Wrong approach:class Point: def __init__(self, x, y): self.x = x self.y = y def test_points(): p1 = Point(1, 2) p2 = Point(1, 2) assert p1 == p2 # Fails because default compares identity
Correct approach:class Point: def __init__(self, x, y): self.x = x self.y = y def __eq__(self, other): return isinstance(other, Point) and self.x == other.x and self.y == other.y def test_points(): p1 = Point(1, 2) p2 = Point(1, 2) assert p1 == p2 # Passes with __eq__
Root cause:Not realizing Python compares object identity by default.
#3Using chained comparisons in assert without clarity.
Wrong approach:def test_chain(): a = 5 b = 5 c = 6 assert a == b == c # Fails, but confusing why
Correct approach:def test_chain_clear(): a = 5 b = 5 c = 6 assert a == b assert b == c
Root cause:Misunderstanding how Python evaluates chained comparisons.
Key Takeaways
Comparing values in tests is essential to verify code correctness by checking expected versus actual results.
Pytest uses Python's assert statement enhanced with detailed failure messages to make debugging easier.
Equality checks work for simple and complex data types, but floating point numbers need special approximate comparisons.
Custom objects require defining __eq__ to enable meaningful equality tests beyond memory identity.
Understanding Python's comparison chaining and floating point quirks prevents subtle bugs and flaky tests.