- Installation
- Requirements
- Starting the Server(s)
- Command-line Switches
- Endpoint Configuration
- The Admin Portal
- The Stubs Portal
- Programmatic API
- Running Tests
- See Also
- TODO
- Wishful Thinkings
- NOTES
As a command-line tool via Chocolatey
cinst stubby
This will install stubby
as a command in your PATH
.
As a project dependency via NuGet
PM> Install-Package stubby
- .NET Framework 4+
Testing with Mono has not been explicitly performed but adherence to Mono-compatible APIs was attempted.
- YamlDotNet for loading yaml files.
- CommandLineParser for handling cli arguments.
Some systems require you to sudo
before running services on port certain ports (like 80)
[sudo] stubby
stubby [-a <port>] [-s <port>] [-t <port>]
[-l <hostname>] [-d <file>] [-v] [-w] [-m]
-a, --admin <port> Port for admin portal. Defaults to 8889.
-s, --stubs <port> Port for stubs portal. Defaults to 8882.
-t, --tls <port> Port for https stubs portal. Defaults to 7443.
-l, --location <hostname> Hostname at which to bind stubby.
-d, --data <file> Data file to pre-load endoints. YAML or JSON format.
-w, --watch Auto-reload data file when edits are made.
-m, --mute Prevent stubby from printing to the console.
-v, --version Prints stubby's version number.
--help This help text.
This section explains the usage, intent and behavior of each property on the request
and response
objects.
Here is a fully-populated, unrealistic endpoint:
- request:
url: ^/your/awesome/endpoint$
method: POST
query:
exclamation: post requests can have query strings!
headers:
content-type: application/xml
post: >
<!xml blah="blah blah blah">
<envelope>
<unaryTag/>
</envelope>
file: tryMyFirst.xml
response:
status: 200
latency: 5000
headers:
content-type: application/xml
server: stubbedServer/4.2
body: >
<!xml blah="blah blah blah">
<responseXML>
<content></content>
</responseXML>
file: responseData.xml
This object is used to match an incoming request to stubby against the available endpoints that have been configured.
- is a full-fledged regular expression
- This is the only required property of an endpoint.
- signify the url after the base host and port (i.e. after
localhost:8882
). - must begin with
/
. - any query paramters are stripped (so don't include them, that's what
query
is for)./url?some=value&another=value
becomes/url
- no checking is done for URI-encoding compliance.
- If it's invalid, it won't ever trigger a match.
This is the simplest you can get:
- request:
url: /
A demonstration using regular expressions:
- request:
url: ^/has/to/begin/with/this/
- request:
url: /has/to/end/with/this/$
- request:
url: ^/must/be/this/exactly/with/optional/trailing/slash/?$
- defaults to
GET
. - case-insensitive.
- can be any of the following:
- HEAD
- GET
- POST
- PUT
- POST
- DELETE
- etc.
- request:
url: /anything
method: GET
- it can also be an array of values.
- request:
url: /anything
method: [GET, HEAD]
- request:
url: ^/yonder
method:
- GET
- HEAD
- POST
-
if ommitted, stubby ignores query parameters for the given url.
-
a yaml hashmap of variable/value pairs.
-
allows the query parameters to appear in any order in a uri
-
The following will match either of these:
/with/parameters?search=search+terms&filter=month
/with/parameters?filter=month&search=search+terms
- request:
url: ^/with/parameters$
query:
search: search terms
filter: month
- if ommitted, any post data is ignored.
- the body contents of the server request, such as form data.
- request:
url: ^/post/form/data$
post: name=John&email=john@example.com
- if supplied, replaces
post
with the contents of the locally given file.- paths are relative from where stubby was executed.
- if the file is not found when the request is made, falls back to
post
for matching. - allows you to split up stubby data across multiple files
- request:
url: ^/match/against/file$
file: postedData.json
post: '{"fallback":"data"}'
postedData.json
{"fileContents":"match against this if the file is here"}
- if
postedData.json
doesn't exist on the filesystem when/match/against/file
is requested, stubby will match post contents against{"fallback":"data"}
(frompost
) instead.
- if ommitted, stubby ignores headers for the given url.
- case-insensitive matching of header names.
- a hashmap of header/value pairs similar to
query
.
The following endpoint only accepts requests with application/json
post values:
- request:
url: /post/json
method: post
headers:
content-type: application/json
Assuming a match has been made against the given request
object, data from response
is used to build the stubbed response back to the client.
- the HTTP status code of the response.
- integer or integer-like string.
- defaults to
200
.
- request:
url: ^/im/a/teapot$
method: POST
response:
status: 420
- contents of the response body
- defaults to an empty content body
- request:
url: ^/give/me/a/smile$
response:
body: ':)'
- similar to
request.file
, but the contents of the file are used as thebody
.
- request:
url: /
response:
file: extremelyLongJsonFile.json
- similar to
request.headers
except that these are sent back to the client.
- request:
url: ^/give/me/some/json$
response:
headers:
content-type: application/json
body: >
[{
"name":"John",
"email":"john@example.com"
},{
"name":"Jane",
"email":"jane@example.com"
}]
- time to wait, in milliseconds, before sending back the response
- good for testing timeouts, or slow connections
- request:
url: ^/hello/to/jupiter$
response:
latency: 800000
body: Hello, World!
The admin portal is a RESTful(ish) endpoint running on localhost:8889
. Or wherever you described through stubby's options.
Submit POST
requests to localhost:8889
or load a data-file (-d) with the following structure for each endpoint:
request
: describes the client's call to the servermethod
: GET/POST/PUT/DELETE/etc.url
: the URI regex string. GET parameters should also be included inline herequery
: a key/value map of query string parameters included with the requestheaders
: a key/value map of headers the server should respond topost
: a string matching the textual body of the response.file
: if specified, returns the contents of the given file as the request post. If the file cannot be found at request time, post is used instead
response
: describes the server's response to the clientheaders
: a key/value map of headers the server should use in it's responselatency
: the time in milliseconds the server should wait before responding. Useful for testing timeouts and latencyfile
: if specified, returns the contents of the given file as the response body. If the file cannot be found at request time, body is used insteadbody
: the textual body of the server's response to the clientstatus
: the numerical HTTP status code (200 for OK, 404 for NOT FOUND, etc.)
- request:
url: ^/path/to/something$
method: POST
headers:
authorization: "Basic usernamez:passwordinBase64"
post: this is some post data in textual format
response:
headers:
Content-Type: application/json
latency: 1000
status: 200
body: You're request was successfully processed!
- request:
url: ^/path/to/anotherThing
query:
a: anything
b: more
method: GET
headers:
Content-Type: application/json
post:
response:
headers:
Content-Type: application/json
Access-Control-Allow-Origin: "*"
status: 204
file: path/to/page.html
- request:
url: ^/path/to/thing$
method: POST
headers:
Content-Type: application/json
post: this is some post data in textual format
response:
headers:
Content-Type: application/json
status: 304
[
{
"request": {
"url": "^/path/to/something$",
"post": "this is some post data in textual format",
"headers": {
"authorization": "Basic usernamez:passwordinBase64"
},
"method": "POST"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"latency": 1000,
"body": "You're request was successfully processed!"
}
},
{
"request": {
"url": "^/path/to/anotherThing",
"query": {
"a": "anything",
"b": "more"
},
"headers": {
"Content-Type": "application/json"
},
"post": null,
"method": "GET"
},
"response": {
"status": 204,
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
},
"file": "path/to/page.html"
}
},
{
"request": {
"url": "^/path/to/thing$",
"headers": {
"Content-Type": "application/json"
},
"post": "this is some post data in textual format",
"method": "POST"
},
"response": {
"status": 304,
"headers": {
"Content-Type": "application/json"
}
}
}
]
If you want to load more than one endpoint via file, use either a JSON array or YAML list (-) syntax. On success, the response will contain Location
in the header with the newly created resources' location
Performing a GET
request on localhost:8889
will return a JSON array of all currently saved responses. It will reply with 204 : No Content
if there are none saved.
Performing a GET
request on localhost:8889/<id>
will return the JSON object representing the response with the supplied id.
You can also view the currently configured endpoints by going to localhost:8889/status
Perform PUT
requests in the same format as using POST
, only this time supply the id in the path. For instance, to update the response with id 4 you would PUT
to localhost:8889/4
.
Send a DELETE
request to localhost:8889/<id>
Requests sent to any url at localhost:8882
(or wherever you told stubby to run) will search through the available endpoints and, if a match is found, respond with that endpoint's response
data
For a given endpoint, stubby only cares about matching the properties of the request that have been defined in the YAML. The exception to this rule is method
; if it is omitted it is defaulted to GET
.
For instance, the following will match any POST
request to the root url:
- request:
url: /
method: POST
response: {}
The request could have any headers and any post body it wants. It will match the above.
Pseudocode:
for each <endpoint> of stored endpoints {
for each <property> of <endpoint> {
if <endpoint>.<property> != <incoming request>.<property>
next endpoint
}
return <endpoint>
}
Add stubby
as a reference within your project:
PM> Install-Package stubby
The Arguments
class is a container for options used by the Stubby
class during construction that correlate to the command-line options.
Admin
- Port for admin portal. Defaults to8889
.Stubs
- Port for stubs portal. Defaults to8882
.Tls
- Port for stubs https portal. Defaults to7443
.Location
- Hostname at which to bind stubby. Defaults tolocalhost
.Data
- Data file location to pre-load endpoints. YAML format.Mute
- Prevent stubby from logging to the console. Defaults totrue
.Watch
- Monitor supplied data file for changes and reload endpoints if necessary. Defaults tofalse
.
Here is the constructor and default values of each (public) property.
public Arguments() {
Admin = 8889;
Stubs = 8882;
Tls = 7443;
Location = "localhost";
Data = null;
Mute = true;
Watch = false;
}
There is a single constructor that takes an instance of Arguments
as it's parameter.
// Constructor
public Stubby(IArguments arguments);
//Start stubby's services
public void Start();
// Stop stubby's services
public void Stop();
// Get a listing of all of stubby's configured endpoints
public IList<Endpoint> GetAll();
// Get an endpoint back by id
public Endpoint Get(uint id);
// Find an endpoint by it's matching Request
public Endpoint Find(Request request);
// Swap out the configuration of one of the endpoints.
// True if successful.
public bool Replace(uint id, Endpoint endpoint);
// Remove an endpoint by id
// True if the operation succeeded
public bool Delete(uint id);
// Remove all configured endpoints from stubby
public void DeleteAll();
// Add a new endpoint configuration
// out: the generated id
// True if successful
public bool Add(Endpoint endpoint, out uint id);
// Add many new endpoint configurations
// out: the generated ids
// True if successful
public bool Add(IEnumerable<Endpoint> endpoints, out IList<uint> ids);
[TestFixture]
public class MyIntegrationTest {
private readonly Stubby _stubby = new Stubby(new Arguments {
Admin = 9999,
Stubs = 9992,
Mute = true,
Data = "../../YAML/endpoints.yaml"
});
[TestFixtureSetUp]
public void Before() {
_stubby.Start();
}
[TestFixtureTearDown]
public void After() {
_stubby.Stop();
}
[Test]...
}
- stubby4node: A node.js implementation of stubby
- stubby4j: A java implementation of stubby
post
parameter as a hashmap underrequest
for easy form-submission value matching
- Copyright 2013 Eric Mrak, Alexander Zagniotov
- License Apache v2.0