Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need a TestBotContext #84

Closed
GeekTrainer opened this issue Feb 17, 2018 · 2 comments
Closed

Need a TestBotContext #84

GeekTrainer opened this issue Feb 17, 2018 · 2 comments
Assignees

Comments

@GeekTrainer
Copy link
Member

After spending a week creating middleware, and doing it with TDD, the thing I settled on above all is the need for a TestBotContext. When calling any of the middleware functions (events), what's really happening is the state of the context is being modified in many (most?) cases.

For example, in the case of human handoff, where messages need to be sent back on the context passed in, and not via the bot, all that's really happening is the responses array is having another Activity pushed on it. Something like this (with logic assumed):

onReceive(context: Partial<BotContext>, next: () => Promise<void>) {
  if(userWantsAgent) context.reply("Connecting you to an agent."); // modifies context.responses
}

It's much simpler to test if I can pass a context in, and then inspect it afterwards, to ensure there's a particular reply waiting in the wings. As best I can tell, there isn't an easy way to just create an empty context, so I wound up implementing one on my own. The moment I did that, my tests became easier to write and more elegant. My implementation isn't complete, but it was what I needed for my tests.

class TestContext implements BotContext {
    constructor(conversationReference: ConversationReference, request: Activity | string) {
        if(typeof request !== "object") {
            request = { type: "message", text: request };
        }
        this.conversationReference = conversationReference;
        this.request = request;
        this.bot = new Bot(new TestAdapter);
        this.bot.createContext =
            (reference: ConversationReference, onReady: (context: BotContext) => void) => {
                onReady(this);
                return new Promise(() => {});
            };

        this.responses = [];
    }
    public request: Activity;
    public responses: Activity[];
    public bot: Bot;
    public responded: boolean = false;
    public conversationReference: ConversationReference;
    public state: BotState;
    public templateManager: TemplateManager;

    public delay(duration: number) { return this; }
    public dispose() {}
    public endOfConversation(code?: string) { return this; }
    public replyWith(id: string, data: any) { return this; }
    public flushResponses() { 
        return new Promise<ConversationResourceResponse[]>(
            (value) => {}
        );
    }
    public showTyping() {
        return this;
    }

    public reply(textOrActivity : string | Activity) {
        if(typeof textOrActivity !== "object") {
            textOrActivity = { type: "message", text: textOrActivity };
        }
        this.responses.unshift(textOrActivity);
        return this;
    }
}

// sample test:
    it("Agent can list queue", (done) => {
        const context = new TestContext(agentReference, "#list");
        const provider = getProvider(HandoffUserState.queued);

        new HandoffMiddleware(provider).receiveActivity(context, next);

        assert(provider.getQueue.called, "getQueue not called");
        assert(next.notCalled, "next called"); // (a sinon spy)
        assert(context.responses.length === 1, "Wrong number of responses");
        assert(context.responses[0].text.indexOf(userReference.user.name) > -1, "Name not listed")
        done();
    });
@Stevenic
Copy link
Contributor

So one of the changes I'm planning to make is to turn the context into an actual class so you'll be able to just create it very short. I think it's definitely a good suggestion to be able to test middleware in isolation so I'll ensure that's possible. In fact I'll try to shift to testing our internal middleware using the same technique.

@Stevenic
Copy link
Contributor

You can now very easily spin up a new TurnContext that you can pass directly to your middleware. Closing and please comment if the current code doesn't satisfy this need.

gasper-az pushed a commit that referenced this issue Sep 24, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants