5

So I have a file that I use module exports on and it has 4 fields among which an execute field that takes 2 args and is essentially a function. It doesn't return anything instead it uses discord.js and runs this message.channel.send('Pong');. I want to test this using jest How do I: 1 - Make sure that the message.channel.send was called with 'Pong' as args 2 - How do I mock it so it doesnt actually call it (i just want to make sure that the text inside of it, like the actual argument is 'Pong' since calling it won't work due to the lack of a proper message object)

I can access the actual command and execute it but I am unsure as to how to check the contents of message.channel.send. The message object cannot be reconstructed by me so that might also need mocking.

I'm using discord.js but that shouldn't really matter.

I will also have to test commands that feature functions that do have returns so how should I go about them?

1 Answer 1

6

You can try this:

const Discord = require('discord.js')

// replace this with whatever the execute command is
// e.g. const ping = require('./commands/ping').execute
const ping = async (message, args) => {
  message.channel.send('Pong')
}

// a counter so that all the ids are unique
let count = 0

class Guild extends Discord.Guild {
  constructor(client) {
    super(client, {
      // you don't need all of these but I just put them in to show you all the properties that Discord.js uses
      id: count++,
      name: '',
      icon: null,
      splash: null,
      owner_id: '',
      region: '',
      afk_channel_id: null,
      afk_timeout: 0,
      verification_level: 0,
      default_message_notifications: 0,
      explicit_content_filter: 0,
      roles: [],
      emojis: [],
      features: [],
      mfa_level: 0,
      application_id: null,
      system_channel_flags: 0,
      system_channel_id: null,
      widget_enabled: false,
      widget_channel_id: null
    })
    this.client.guilds.cache.set(this.id, this)
  }
}

class TextChannel extends Discord.TextChannel {
  constructor(guild) {
    super(guild, {
      id: count++,
      type: 0
    })
    this.client.channels.cache.set(this.id, this)
  }

  // you can modify this for other things like attachments and embeds if you need
  send(content) {
    return this.client.actions.MessageCreate.handle({
      id: count++,
      type: 0,
      channel_id: this.id,
      content,
      author: {
        id: 'bot id',
        username: 'bot username',
        discriminator: '1234',
        bot: true
      },
      pinned: false,
      tts: false,
      nonce: '',
      embeds: [],
      attachments: [],
      timestamp: Date.now(),
      edited_timestamp: null,
      mentions: [],
      mention_roles: [],
      mention_everyone: false
    })
  }
}

class Message extends Discord.Message {
  constructor(content, channel, author) {
    super(channel.client, {
      id: count++,
      type: 0,
      channel_id: channel.id,
      content,
      author,
      pinned: false,
      tts: false,
      nonce: '',
      embeds: [],
      attachments: [],
      timestamp: Date.now(),
      edited_timestamp: null,
      mentions: [],
      mention_roles: [],
      mention_everyone: false
    }, channel)
  }
}

const client = new Discord.Client()
const guild = new Guild(client)
const channel = new TextChannel(guild)

// the user that executes the commands
const user = {id: count++, username: 'username', discriminator: '1234'}

describe('ping', () => {
  it('sends Pong', async () => {
    await ping(new Message('ping', channel, user))
    expect(channel.lastMessage.content).toBe('Pong')
  })
})

You also need to put testEnvironment: 'node' in your jest configuration (see this issue).


Edit

You can also use Discord.SnowflakeUtil.generate() to generate an id if you need to obtain things like the timestamp.


2024 Update

There’s a library gauntlet that you can look into.

Sign up to request clarification or add additional context in comments.

7 Comments

Which fields need to be filled out to make this work?
@Leokins For this minimal example to work, you need at least id for Guild, TextChannel, and Message (and also content for Message if you're testing the content of the message). However, some properties may not work as expected (e.g. channel.type would be unknown).
You can also see what fields Discord.js is expecting to always be there by looking at the Discord documentation (guild structure, channel structure, message structure) (a question mark after the field name indicates that the field may not be returned by Discord).
Hi cherryblossom. Thanks for the reply. Can I contact you somehow? I have more questions about this haha.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.