Wolfram Language Paclet Repository

Community-contributed installable additions to the Wolfram Language

Primary Navigation

    • Cloud & Deployment
    • Core Language & Structure
    • Data Manipulation & Analysis
    • Engineering Data & Computation
    • External Interfaces & Connections
    • Financial Data & Computation
    • Geographic Data & Computation
    • Geometry
    • Graphs & Networks
    • Higher Mathematical Computation
    • Images
    • Knowledge Representation & Natural Language
    • Machine Learning
    • Notebook Documents & Presentation
    • Scientific and Medical Data & Computation
    • Social, Cultural & Linguistic Data
    • Sound & Video
    • Strings & Text
    • Symbolic & Numeric Computation
    • System Operation & Setup
    • Time-Related Computation
    • User Interface Construction
    • Visualization & Graphics
    • Random Paclet
    • Alphabetical List
  • Using Paclets
    • Get Started
    • Download Definition Notebook

Mockingbird

Tutorials

  • WritingMockTestsWithMockingbird

Guides

  • Mockingbird

Symbols

  • MockDefinitionGroup
  • MockDefinition
  • MockEvaluate
  • MockEvaluateReap
  • MockPropertyValuePatternTest
  • MockPropertyValueQ
  • MockSow
Writing Mock Tests with Mockingbird
Fundamentals of Mock Evaluation
Validating Complex Behavior with
MockSow
and
MockEvaluateReap
An Example Package-Under-Test: DeckOfCardsLink
Validating
HTTPRequest
Expressions
Basic Mocking of an HTTP API Request
Further Notes
Testing Failure Behavior
​
The Mockingbird package provides tools for locally overriding the definitions of Wolfram Language functions and symbols. This general functionality is useful for multiple purposes, including interactive development and debugging, but one particularly strong application is the writing of software tests involving a "mocking" or "stubbing" pattern.
Mocking and stubbing
– sometimes referred to collectively as
"test double"
patterns – originate from the object-oriented programming (OOP) world, and both involve substituting special versions of objects that mimic the behavior of real objects for testing. This is valuable both conceptually, in allowing the scope of unit tests to be restricted to only the functionality they're intended to test, and practically, in enabling unit and integration tests that avoid calling the real implementation of methods or APIs whose execution may be expensive – in time, computational power, or even in money. Additionally, mocking/stubbing makes it easy to simulate failure modes that may be difficult to elicit on demand from a real application.
The original OOP sense of "mock/stub objects" does not readily lend itself towards use in the Wolfram Language, as the language does not offer a strict object-oriented paradigm. However, the Wolfram Language does offer powerful tools for locally modifying the behavior of function and symbol definitions. Mockingbird provides a wrapper around this functionality that can be used in a similar spirit, and to more or less the same end, when writing software tests in the Wolfram Language.
The distinction between mocking and stubbing is fairly subtle when viewed without the OOP lens. This rest of this document, and that of Mockingbird's documentation, will generally use the term "mock", although Mockingbird can also be used to implement Wolfram Language versions of stubs and other "test double"-like patterns.
Fundamentals of Mock Evaluation
This tutorial will focus mostly on the application of Mockingbird to the specific use case of software testing, but it is useful to first give a brief overview of the basic concepts and operation of the package.
Load the Mockingbird package.
In[1]:=
Needs["Wolfram`Mockingbird`"]
MockDefinition
[lhs:=rhs]
represents a delayed definition for use during mock evaluation
MockEvaluate
[defs,expr]
evaluate
expr
with mock definition(s)
defs
in place
The basic mocking constructs.
MockDefinition
is an inert container representing a definition for later use in mock evaluation.
In[2]:=
def=
MockDefinition
[f[x_]:=Expand[(x/2)^3]]
Out[2]=
MockDefinitionf[x_]:=Expand
3
x
2

MockEvaluate
evaluates an expression after locally installing one or more mock definitions.
In[3]:=
MockEvaluate
[def,f[a+b]]
Out[3]=
3
a
8
+
3
2
a
b
8
+
3a
2
b
8
+
3
b
8
MockSow
[expr]
sow expr to be caught by
MockEvaluateReap
MockEvaluateReap
[defs,expr]
evaluate
expr
while reaping expressions sown with
MockSow
The "agricultural" mocking functions.
MockEvaluateReap
returns a list like
{result,reaped}
, where
result
is the result of evaluating
expr
, and
reaped
is a list (or association) of expressions sown using
MockSow
.
In[4]:=
MockEvaluateReap
​​
MockDefinition
f[x_]:=Expand
MockSow
[(x/2)^3],​​f[a+b]​​
Out[4]=

3
a
8
+
3
2
a
b
8
+
3a
2
b
8
+
3
b
8
,
1
8
3
(a+b)

An Example Package-Under-Test: DeckOfCardsLink
The target of our mockery in this tutorial will be a simple toy package called DeckOfCardsLink. This package provides three functions for interacting with the Deck of Cards API (
https://deckofcardsapi.com/)
, an example HTTP API following RESTful design principles. The code for this package is below, followed by examples of the functions it defines.
In[5]:=
BeginPackage["DeckOfCardsLink`"];​​​​ShuffleNewDeck[count_Integer : 1] := Enclose​​ Replace​​ Confirm@URLExecute​​ "https://deckofcardsapi.com/api/deck/new/shuffle/",​​ {"deck_count" -> count},​​ "RawJSON"​​ ,​​ ​​ data : KeyValuePattern["success" -> True] :> data["deck_id"],​​ data : KeyValuePattern["success" -> False] :> Failure["APIError", data["error"]]​​ ​​ ,​​ "Expression"​​​​​​DrawCardsFromDeck[deck_String, count_Integer : 1] := Replace​​ URLExecute​​ "https://deckofcardsapi.com/api/deck/" <> deck <> "/draw/",​​ {"count" -> count},​​ "RawJSON"​​ ,​​ ​​ data : KeyValuePattern["success" -> True] :> Map​​ <|"Value" -> #value, "Suit" -> #suit, "Image" -> Import[#image]|> &,​​ data["cards"]​​ ,​​ data : KeyValuePattern["success" -> False] :> Failure["APIError", data["error"]]​​ ​​​​​​ReturnCardsToDeck[deck_String, cards : {__String}] := Replace​​ ImportByteArray​​ URLRead​​ HTTPRequest"https://deckofcardsapi.com/api/deck/" <> deck <> "/return/", <|​​ "Query" -> {"cards" -> StringRiffle[cards, ","]}​​ |>​​ ["BodyByteArray"],​​ "RawJSON"​​ ,​​ ​​ data : KeyValuePattern["success" -> True] :> Success["ReturnedCards", <||>],​​ data : KeyValuePattern["success" -> False] :> Failure["APIError", data["error"]]​​ ​​​​​​EndPackage[]
ShuffleNewDeck
requests a new shuffled deck from the API server and returns the deck ID.
In[10]:=
deckID=ShuffleNewDeck[]
Out[10]=
eib4sjpovsk7
A
Failure
object is returned if an error is encountered, such as upon requesting too many decks.
In[11]:=
ShuffleNewDeck[400]
Out[11]=
Failure

Message:
The max number of Decks is 20.
Tag:
APIError

DrawCardsFromDeck
draws the specified number of cards from a deck and imports each card's image.
In[12]:=
DrawCardsFromDeck[deckID,2]
Out[12]=
Value9,SuitDIAMONDS,Image
,Value8,SuitSPADES,Image

ReturnCardsToDeck
returns one or more drawn cards to a deck, with cards specified by abbreviated strings.
In[13]:=
(*3C=3ofclubs;JD=jackofdiamonds*)​​ReturnCardsToDeck[deckID,{"3C","JD"}]
Out[13]=
Success
✓
Tag: ReturnedCards

Basic Mocking of an HTTP API Request
This tutorial will focus on mocking the behavior of an external service (
deckofcardsapi.com
) accessible via an HTTP API. Bear in mind, though, that Mockingbird is not limited to such applications – mocking I/O operations such as database queries or filesystem access is also feasible. Indeed, the behavior of most any built-in or custom Wolfram Language function can be mocked.
The mocked version of the API call is far faster than sending a real HTTP request.

Validating Request Parameters

Confirmation functions.
Mock the /deck/new/shuffle API endpoint, asserting that the request includes the expected deck count as an HTTP query parameter.
Testing Failure Behavior
Testing that code behaves as expected during failure cases is an important yet oft-neglected element of software testing. Mockingbird makes it easy to simulate failure modes in practically any part of an application or an external service that it depends on.
Constructs for validating properties of HTTPRequest and other "object"-like expressions.
Further Notes
This tutorial has covered an example application of Mockingbird to testing a Wolfram Language client for an HTTP API. As noted in the introduction, Mockingbird is a general tool for creating and reusing local definitions, and is not limited to the particular use patterns demonstrated in this document.
This section will describe some specific features, patterns, and potential use cases beyond those demonstrated previously.

Recursion

Contrary to the standard behavior of Wolfram Language definitions, Mockingbird mock definitions normally do not recurse – i.e. a given definition can only appear once on the evaluation stack. This behavior is useful for "spying" patterns that involve inspecting the arguments passed to a function while still ultimately calling the original or built-in implementation of the function.

Catching Fall-Through Evaluations

Mock definitions precede, but do not supplant, existing definitions for a given symbol. If an invocation of a mocked function does not match any mock definition patterns, the function's original definition will be called.
The mock definition intended for sendRequest does not catch URLExecute called with two arguments, as done in sendRequestWithParameter.

Saving and Reusing Mock Definitions

Maintaining State in Mock Definitions

It is permissible for mock definitions to access and modify global state, or state otherwise outside of their local scope.
It is probably advisable to exercise discretion in applying this pattern so as to avoid writing tests that are excessive in imitating application behavior irrelevant to the part of the system under test. In particular, mock definitions should use global state with extreme care.

© 2023 Wolfram. All rights reserved.

  • Legal & Privacy Policy
  • Contact Us
  • WolframAlpha.com
  • WolframCloud.com