The aim of fakediscord
is to replicate the behaviour of the Discord HTTP and Websocket APIs, based on the documentation and observed behaviour, in order to enable the integration testing of Discord bots without calling the real Discord API.
Analogous to LocalStack, fakediscord
should be run locally using Docker when running your bot's tests.
While written in Go, fakediscord
can be used to test bots in any language, provided they adhere to Discord's specifications.
flowchart LR
t["Your tests"] --> b["Your Bot"] --> d["Discord"]
b --> f["fakediscord"]
t --> f["fakediscord"]
fakediscord
fakes the HTTP and WebSocket endpoints of the Discord API, triggering corresponding events via the WebSocket connection. fakediscord
pairs well with (and is based on the hard work of) bwmarrin/discordgo.
Of course, you should also test your bot manually before releasing to the public: there are many features currently not present, such as authorization, -- any action is currently allowed.
fakediscord
should work with any Discord client in any language, and is intended to be run via Docker:
docker run -p 8080:8080 ghcr.io/elliotwms/fakediscord:{version}
It is possible to provide a config.yml
file to bootstrap users and guilds (todo: document config):
services:
fakediscord:
image: ghcr.io/elliotwms/fakediscord:{version}
ports:
- 8080:8080
volumes:
- ${PWD}/fakediscord.yaml:/config.yml:ro
fakediscord
provides a Go client as a convenience wrapper for internal endpoints, as well as a shim for discordgo to allow you to override the endpoints, which can be found in pkg/fakediscord
.
Override the Discord Base URL to fakediscord
's, then proceed to use your client as normal:
package main
import "github.com/elliotwms/fakediscord/pkg/fakediscord"
func main() {
// override discordgo URLs
fakediscord.Configure("http://localhost:8080")
// Client for internal endpoints (e.g. interactions)
c := fakediscord.NewClient()
}
- Any token value will pass authentication (
Bot {token}
) - If the token matches one specified in the config then the relevant user will be authenticated
- Otherwise, a user will be generated with the token value as the username
- For testing purposes, all users are assumed to be in all guilds
fakediscord
provides an endpoint for triggering interactions, which would normally only be possible via a user initiating via the UI. A POST
of an InteractionCreate
event to /api/:version/interactions
will create an interaction.
A suggested pattern for testing interactions within a webhook application would be as follows:
- Build the expected interaction within your test suite
- Create the initial interaction in
fakediscord
. This will provide you with IDs, tokens etc - Send the interaction to your application's endpoint
- Your application will likely call the interaction's callback url to acknowledge the interaction
sequenceDiagram
participant t as Tests
participant a as App
participant d as fakediscord
t->>t: Set up interaction
t->>d: POST /interactions
d->>t: 201 Created: Interaction
t->>a: Interaction
activate a
a->>d: POST /interactions/:id/:token/callback
d->>a: 204 No Content
a->>a: Process interaction
a->>d: POST/webhooks/:appID/:token/@original
d->>a: 200 OK
a->>t: 202 Accepted
deactivate a
t->>d: GET /webhooks/:appID/:token/@original
d->>t: 200 OK: Message
t->>t: Assert on message
fakediscord
currently supports the following API operations, and emits the corresponding events:
- Get Gateway
- Connect
HELLO
READY
GUILD_CREATE
- Create Message
MESSAGE_CREATE
- Supports basic, embeds and multipart
- Get Message
- Delete Message
- Get Message Reactions
- Create Reaction
- Delete Reactions
Check out how the following projects use fakediscord
for inspiration:
- Docker Compose contains Pinbot config, including the bot user in fakediscord.yaml
- TestMain calls
fakediscord.Configure
to set base URLs etc, sets up the client, creates a test guild for the run and opens a general session for the test suite - Individual tests then create channels in the test guild to execute their tests within (example)