Create tests for your functions

1Set up Slack CLI

Use the new Slack CLI to create and deploy Slack apps. With the Slack CLI, developers can create an app and generate some boilerplate code in seconds.

This section walks through setting up your environment for next gen platform development.

  • Install the Slack CLI

    We've built a command line interface to make the process of creating new apps, running them, and deploying them easier. Binaries are available for macOS and Windows.

    Deno is currently a pre-requisite for using the CLI — if you do not have Deno installed, please do so first.

    Additionally, the experience of writing an app will be greatly enhanced if your IDE includes support for Deno. Here's a plugin to add Deno capabilities to VS Code.

    To install the Slack CLI:

    1. Download the latest binary for your platform
    2. Decompress the downloaded file. On macOS, copy the slack binary to a destination in your path (we recommend /usr/local/bin). For Windows, copy the .exe file into any location accessible in your path.
    3. Test the app is properly installed by running slack version and verifying the version matches the one you downloaded.

    Now you can now use the Slack CLI to authenticate and create a new app.

  • Authenticate the Slack CLI

    Before you can create an app, you must authenticate.

    1. Run slack login — this will display some instruction text and a string to copy and paste in any channel in the test workspace.

          /hermesauthticket ABC123DEF...XYZ
      
    2. Copy the entire string, including the leading /, and paste it into any channel in your Slack workspace. (You are essentially calling a special slash command that will authenticate your app with Slack).

    3. Run slack auth info for details on your active workspace authorization.

    Now that you've authenticated, you can create a new app.

  • Create a new app

    Now you can create an app! cd to a directory where you have write access and want your app to live, most likely in your home. Then, run

    $ slack create [optional-project-name]
    

    This will create a new project called with a random name like priceless-lemur-123.

    If you want to name your project something specific, include an additional parameter

    $ slack create my-new-project
    

    Once the app has finished running, you'll have a new directory that contains a skeleton app.

    $ cd my-new-project
    

    The structure of these next-generation apps aims to favor convention over configuration. Directories are named logically and the files within those directories map to specific functionality with an app. For example, the functions directory contains a list of an app's functions, with a single file for each function an app defines.

2Understand how the app works and setup your app for testing

This section walks you through how your app is structured and how the different pieces tie together.

We will then make a sample function that takes in a string input and returns a capitalized version.

  • Understand how the app is structured

    Your new project has a directory structure that should resemble the following:

    .slack/
      - apps.json
      - cli.json
    assets/
      - icon.png
    functions/
     - myfunction.ts
    tables/
     - mytable.ts
    triggers/
     - mytrigger.ts
    workflows/
     - myworkflow.ts
    import_map.json
    project.ts
    README.md
    

    Functions: Modular, reusable, atomic blocks of functionality that can be grouped together as workflows. A function defines inputs, performs some useful bit of work using those inputs, and responds with pre-defined outputs. You'll be able to quickly write custom functions, deploy them via the CLI, and building workflows using these functions. Check out our functions documentation to learn more.

    Workflows: A workflow is a group of functions, including built-in functions provided by Slack. Check out our workflows documentation to learn more.

    Triggers: Define how workflows are called, such as a message action, shortcut, or event, where it is able to be executed, and who can execute it. Check out our triggers documentation to learn more.

    Tables: Define tables and store data to power your application. Check out our tables documentation to learn more.

    Project.ts: Contains all of the metadata about a project, such as the name of project, description, and requested OAuth scopes. It also contains a list of defined functions, tables, triggers and workflows.

  • Adding a new function to your app

    A function is defined via the DefineFunction method, which is part of the SDK that gets included with every newly created project. The function is exported so that it can be used elsewhere in your app (such as workflows). DefineFunction accepts a name (as a string) and object that defines the input and output parameters as well as an anonymous, asynchronous function that performs that actual work of the function (i.e. taking the input, doing something like calling an API, and then providing an output via a promise).

    Let's create a yelling function that will take as input a string and return a capitalized version:

    1. Create a yell.ts file under the functions/ directory of your project.
    2. Copy and paste the following code into it:
    import { DefineFunction, Schema } from "slack-cloud-sdk/mod.ts";
    export const YellFunction = DefineFunction(
        "yell",
        {
            title: "Yell",
            description: "Capitalizes any input text",
            input_parameters: {
                text: {
                    type: Schema.types.string,
                    description: "The text to capitalize",
                },
            },
            output_parameters: {
                scream: {
                    type: Schema.types.string,
                    description: "The capitalized text",
                },
            },
        },
        async ({ inputs, client, env }) => {
            console.log(`capitalizing ${inputs.text}!`);
            return await {
                completed: true,
                outputs: { scream: inputs.text.toUpperCase() },
            };
        },
    );
    
    1. Now we need to import and reference the function we just created in the project metadata file, located at project.ts in the project root:
      1. Import your new function at the top of your project.ts file:

        import { YellFunction } from "./functions/yell.ts";

      2. Make sure YellFunction is referenced in the functions array of your Project definition, so the relevant section of your project.ts file looks like the following:

        functions: [YellFunction],

  • Adding a new workflow to your app

    Now that we have defined our YellFunction, we need to connect it to Workflow.

    Let's create a yelling workflow that leverages our YellFunction, take its output and uses a built-in Slack function to post it to a channel:

    1. Create a yell.ts file under the workflows/ directory of your project.
    2. Copy and paste the following code into it:
    import { DefineWorkflow, Schema } from "slack-cloud-sdk/mod.ts";
    import { YellFunction } from "../functions/yell.ts";
    
    export const YellWorkflow = DefineWorkflow("yell_workflow", {
        title: "YellWorkflow",
        description: "Express yourself loudly",
        input_parameters: {
            channel: {
                type: Schema.slack.types.channel_id,
                description: "Channel to yell in",
            },
            text: {
                type: Schema.types.string,
                description: "Text to capitalize",
            },
        },
    });
    
    const step1 = YellWorkflow.addStep(YellFunction, {
        text: YellWorkflow.inputs.text,
    });
    
    YellWorkflow.addStep(Schema.slack.functions.SendMessage, {
        channel_id: YellWorkflow.inputs.channel,
        message: `${step1.outputs.scream}`,
    });
    
    1. Now we need to import and reference the workflow we just created in the project metadata file, located at project.ts in the project root:
      1. Import your new workflow at the top of your project.ts file:

        import { YellWorkflow } from "./workflows/yell.ts";

      2. Make sure YellWorkflow is referenced in the workflows array of your Project definition, so the relevant section of your project.ts file looks like the following:

        workflows: [YellWorkflow],

  • Adding a new trigger to your app

    Now that we have defined our YellWorkflow, we need to connect it to Trigger. There are many different kinds of triggers, but for our purposes we will use a Shortcut trigger. This Shortcut will be how users in your Slack workspace will interact with this app.

    1. Create a yell.ts file under the triggers/ directory of your project.
    2. Copy and paste the following code into it:
    import { DefineTrigger, TriggerTypes } from "slack-cloud-sdk/mod.ts";
    import { YellWorkflow } from "../workflows/yell.ts";
    
    export const YellShortcut = DefineTrigger(
        "yell_shortcut",
        {
            type: TriggerTypes.Shortcut,
            name: "/yell",
            description: "Echoes some text IN ALL CAPITAL LETTERS",
        },
    )
    .runs(YellWorkflow)
    .withInputs((ctx) => ({
        channel: ctx.data.channel_id,
        text: ctx.data.command.text,
    }));
    
    1. Now we need to import and reference the trigger we just created in the project metadata file, located at project.ts in the project root:
      1. Import your new trigger at the top of your project.ts file:

        import { YellShortcut } from "./triggers/yell.ts";

      2. Make sure YellShortcut is referenced in the triggers array of your Project definition, so the relevant section of your project.ts file looks like the following:

        triggers: [YellShortcut],

  • Review project.ts

    Let's take a quick minute to review our project.ts file to confirm everything looks right:

        import { Project } from "slack-cloud-sdk/mod.ts";
        import { YellFunction } from "./functions/yell.ts";
        import { YellWorkflow } from "./workflows/yell.ts";
        import { YellShortcut } from "./triggers/yell.ts";
    
        Project({
            name: "Yell It",
            description: "An app that you use when you want to yell your message",
            icon: "assets/icon.png",
            runtime: "deno1.x",
            botScopes: [
                "commands",
                "chat:write",
                "chat:write.public",
                "groups:write",
            ],
            functions: [YellFunction],
            workflows: [YellWorkflow],
            triggers: [YellShortcut],
            tables: [],
            outgoingDomains: [],
        });
    

    You should now be able to run the app locally with slack run or deploy it to Slack's hosting infrastructure via slack deploy.

    This concludes the setting up the app! We are now ready to write some tests!

3Write tests for your function

In this section, we will cover writing a test for the yelling function we implemented in the last section.

  • Writing your first test

    Functions created for hosted Slack applications have a defined structure: they accept input parameters, execute some logic and then return some output parameters. This structure provides a great foundation for writing unit tests for each Function making up your application.

    The example below uses the assertions module from the Deno standard library. We will be using assertsEqual in the example below to do a string comparison, but there are many other useful assertion types you may wish to use in your testing journey.

    Let's create a test for the YellFunction we created in an earlier step to exercise its logic and make sure it is working properly.

    1. Create a yell_test.ts file under the functions/ directory of your project.
    2. Copy and paste the following code into it:
    import { assertEquals } from "https://deno.land/std@0.99.0/testing/asserts.ts";
    import { SlackAPIClient } from "slack-cloud-sdk/mod.ts";
    import { YellFunction } from "./yell.ts";
    
    const client = new SlackAPIClient("");
    
    Deno.test("Yell function test", async () => {
        const inputs = { text: "This is Unacceptable!!!" };
        const { outputs } = await YellFunction.run({ inputs, client, env: {} });
        assertEquals(outputs?.scream, "THIS IS UNACCEPTABLE!!!");
    });
    
  • Running your tests

    Last but not least, let's run our the test we just wrote using the slack CLI to make sure everything is working as expected:

    slack deno test
    

    You should see the following in your terminal:

    [test --import-map import_map.json --unstable]
    running 1 test from file:///Users/fmaj/src/yell-app/functions/yell_test.ts
    test Yell function test ...capitalizing This is Unacceptable!!!!
     ok (8ms)
    
    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (60ms)
    

    Every file suffixed with _test.ts will be automatically executed whenever you run slack deno test.

Was this page helpful?