How to Mock SQLite executemany When Batch Inserting in Python

Batch insert many rows into SQLite with executemany and mock the cursor for isolated tests.

Medium Python 3.9+ Aug 9, 2026 Database scaling & optimization 15 views 0 copies

Python code

41 lines
Python 3.9+
import sqlite3
from unittest.mock import Mock, patch

def insert_users(conn, users):
    """Insert multiple user records using executemany."""
    cursor = conn.cursor()
    cursor.executemany(
        "INSERT INTO users (name, age) VALUES (?, ?)",
        users
    )
    conn.commit()
    return cursor.rowcount

if __name__ == "__main__":
    # Create a real in-memory database for demonstration
    conn = sqlite3.connect(":memory:")
    conn.execute("CREATE TABLE users (name TEXT, age INTEGER)")
    
    users = [
        ("Alice", 30),
        ("Bob", 25),
        ("Charlie", 35)
    ]
    
    count = insert_users(conn, users)
    print(f"Inserted {count} rows")
    
    # Verify the data
    rows = conn.execute("SELECT * FROM users ORDER BY name").fetchall()
    print("Database contents:", rows)
    
    # Now demonstrate mocking for testing
    mock_conn = Mock()
    mock_conn.cursor.return_value.executemany.return_value = Mock(rowcount=0)
    
    with patch("__main__.insert_users", wraps=insert_users) as mocked_insert:
        # Test with mock connection
        result = mocked_insert(mock_conn, [("Test", 1)])
        print(f"Mock result: {result}")
    
    conn.close()

Output

stdout
Inserted 3 rows
Database contents: [('Alice', 30), ('Bob', 25), ('Charlie', 35)]
Mock result: 0

How it works

The insert_users function calls executemany on the connection's cursor, passing the SQL template and the list of tuples. The commit ensures all rows are saved. When testing, Mock replaces the connection and its cursor so no real database is needed. The patch context manager wraps the function to allow checking calls while using the mock. This isolates logic and avoids side effects.

Common mistakes

  • Forgetting that `executemany` takes a list of tuples, not a tuple of tuples.
  • Assuming `rowcount` is returned automatically without mocking the return value.
  • Not calling `commit()` which leads to no rows in the actual database.

Variations

  1. Use `patch('module.insert_users')` to replace the entire function with a Mock instead of wrapping.
  2. Use a context manager `with mock_conn.cursor() as cursor` to avoid leaking cursors.

Real-world use cases

  • Bulk-loading CSV data into a database for ETL pipelines.
  • Seeding test databases with sample data quickly.
  • Testing database functions in unit tests without a live connection.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Database scaling & optimization

Related tutorials and quizzes for this topic.