<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/unit-testing-with-wrapture-8myueoz61" -->

---
title: Unit testing with wrapture | daily.dev
description: A detailed comparison walks through the same set of unit tests written twice, once with unittest.mock and once with a library called wrapture, to show where...
canonical: https://daily.dev/posts/unit-testing-with-wrapture-8myueoz61
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Unit testing with wrapture | daily.dev
og:description: A detailed comparison walks through the same set of unit tests written twice, once with unittest.mock and once with a library called wrapture, to show where...
og:url: https://daily.dev/posts/unit-testing-with-wrapture-8myueoz61
og:image: https://api.daily.dev/og/posts/8MYueOZ61.png
og:image:alt: Unit testing with wrapture
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Unit testing with wrapture

**[Planet Python](https://daily.dev/sources/planetpython)** · 10 min read · 0 upvotes · 0 comments

## Summary

A detailed comparison walks through the same set of unit tests written twice, once with unittest.mock and once with a library called wrapture, to show where wrapping real code instead of substituting it changes what a test can express. Examples cover strict argument checking on stubs, asserting on calls an object makes to itself, running real code while transforming only its arguments or return value, and asserting on absences (like a notification that must never fire) on error paths using a shared timeline and tape. The piece closes by noting where unittest.mock still fits, namely when a test itself must supply a callback or collaborator object.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://grahamdumpleton.me/posts/2026/09/unit-testing-with-wrapture>

## Questions this post answers

### why can't unittest.mock assert that a private method was called by another method on the same object?

Because mocking with unittest.mock typically involves injecting a MagicMock as a collaborator behind a constructor seam, calls a method makes to itself (like an object calling its own private method) never cross that seam and so never appear in mock_calls. Patching the private method directly replaces it instead, which means the real logic inside it never runs and any real work it would trigger, such as charging a gateway, never happens.

_Developers debugging mock visibility gaps like this compare testing approaches on daily.dev before picking one for their suite._

### how do I test that a function called the real code but changed only one part of the result, like rewriting an unstable id?

With unittest.mock, Mock(wraps=real) forwards calls to the real method but cannot modify arguments or the returned result, so this scenario has no direct answer in the standard library. The wrapture library solves this by letting a binding call transforms_result() on the real charge() call, rewriting just the id field (e.g. to a fixed 'ch_TEST') while leaving the rest of the real response intact.

_Teams hitting this exact mocking limitation can track alternative testing tools like wrapture via daily.dev._

### how do you assert that a notification was never sent when an error path is triggered in a test?

Bind the notifier's send method and use its events log to assert it was never called, for example send.events.assert_never() in wrapture, which records real calls against the real object rather than a fabricated double. This differs from unittest.mock's notifier.send.assert_not_called(), which works but relies entirely on a mock double rather than the real collaborator remaining live and unpatched during the failure path.

_Developers writing error-path tests can follow evolving testing patterns like this through daily.dev._

## Similar posts on daily.dev

- [Recording calls with wrapture](https://daily.dev/posts/recording-calls-with-wrapture-hkpb9gqvx) · Planet Python · 0 upvotes · 1 comments
- [Trying out wrapture](https://daily.dev/posts/trying-out-wrapture-qi1ktjmcu) · Planet Python · 0 upvotes · 0 comments
- [Introducing wrapture](https://daily.dev/posts/introducing-wrapture-ohj3cvcws) · Planet Python · 1 upvotes · 1 comments
- [Beyond callables in wrapture](https://daily.dev/posts/beyond-callables-in-wrapture-a41yd6lmi) · Planet Python · 0 upvotes · 0 comments
- [Python: spy on function calls with unittest.mock’s wraps](https://daily.dev/posts/python-spy-on-function-calls-with-unittest-mock-s-wraps-4hfdxvksm) · Adam Johnson · 1 upvotes · 0 comments

---

Tags: [#python](https://daily.dev/tags/python), [#testing](https://daily.dev/tags/testing)

[View this post on daily.dev](https://daily.dev/posts/unit-testing-with-wrapture-8myueoz61)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Unit testing with wrapture","url":"https://daily.dev/posts/unit-testing-with-wrapture-8myueoz61","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/unit-testing-with-wrapture-8myueoz61"},"datePublished":"2026-08-31T21:51:13.195Z","dateModified":"2026-09-14T06:32:11.558Z","description":"A detailed comparison walks through the same set of unit tests written twice, once with unittest.mock and once with a library called wrapture, to show where...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/18021020c11660cbe95426c29974ab0a?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/18021020c11660cbe95426c29974ab0a?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Planet Python","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Planet Python","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/eac3cc584cb043b5a242d13752bd3d0c","url":"https://daily.dev/sources/planetpython"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/unit-testing-with-wrapture-8myueoz61","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"python,testing","timeRequired":"PT10M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Planet Python","item":"https://daily.dev/sources/planetpython"},{"@type":"ListItem","position":3,"name":"Unit testing with wrapture"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/unit-testing-with-wrapture-8myueoz61#faq","mainEntity":[{"@type":"Question","name":"why can't unittest.mock assert that a private method was called by another method on the same object?","acceptedAnswer":{"@type":"Answer","text":"Because mocking with unittest.mock typically involves injecting a MagicMock as a collaborator behind a constructor seam, calls a method makes to itself (like an object calling its own private method) never cross that seam and so never appear in mock_calls. Patching the private method directly replaces it instead, which means the real logic inside it never runs and any real work it would trigger, such as charging a gateway, never happens. Developers debugging mock visibility gaps like this compare testing approaches on daily.dev before picking one for their suite."}},{"@type":"Question","name":"how do I test that a function called the real code but changed only one part of the result, like rewriting an unstable id?","acceptedAnswer":{"@type":"Answer","text":"With unittest.mock, Mock(wraps=real) forwards calls to the real method but cannot modify arguments or the returned result, so this scenario has no direct answer in the standard library. The wrapture library solves this by letting a binding call transforms_result() on the real charge() call, rewriting just the id field (e.g. to a fixed 'ch_TEST') while leaving the rest of the real response intact. Teams hitting this exact mocking limitation can track alternative testing tools like wrapture via daily.dev."}},{"@type":"Question","name":"how do you assert that a notification was never sent when an error path is triggered in a test?","acceptedAnswer":{"@type":"Answer","text":"Bind the notifier's send method and use its events log to assert it was never called, for example send.events.assert_never() in wrapture, which records real calls against the real object rather than a fabricated double. This differs from unittest.mock's notifier.send.assert_not_called(), which works but relies entirely on a mock double rather than the real collaborator remaining live and unpatched during the failure path. Developers writing error-path tests can follow evolving testing patterns like this through daily.dev."}}]}
```

