wechaty / puppet-mock

Puppet Mocker for Wechaty (& A Puppet Template Starter)
https://paka.dev/npm/wechaty-puppet-mock
Apache License 2.0
45 stars 14 forks source link

How to write test case use mock for bot reply contact messge? #37

Closed chs97 closed 4 years ago

chs97 commented 4 years ago

contact send a message to bot. how to get the bot reply message?

import test from 'tstest'
import sinon from 'sinon'
import { Wechaty } from 'wechaty'
import { PuppetMock, Mocker } from 'wechaty-puppet-mock'

test('reply', async (t) => {
  const sandbox = sinon.createSandbox()
  const mocker = new Mocker()
  const puppet = new PuppetMock({ mocker })
  const wechaty = new Wechaty({ puppet })
  const [user, contact] = mocker.createContacts(3)
  await wechaty.start()
  await mocker.login(user)

  // contact say something to user
  contact.say('Hello').to(user)
  // then
  wechaty.on('message', async (message) => {
    await message.say('world')
  })
  // how to write test case ? I want to get the reply message.
  sandbox.restore()
  await wechaty.stop()
})
chs97 commented 4 years ago

@huan please publish the last commit, wechaty-puppet-mock, I see the mocker initial twice.

huan commented 4 years ago

It's great to see that you are trying to use the mocker to unit testing your bot!

The Problem

How to receive the message from the bot with our mocker system?

Solution

It's easy!

test('reply', async (t) => {
  const mocker = new Mocker()
  const puppet = new PuppetMock({ mocker })
  const wechaty = new Wechaty({ puppet })
  await wechaty.start()

  const [user, contact] = mocker.createContacts(3)
  await mocker.login(user)

  // ATTENTION: you need to register your listener before you say anything
  wechaty.on('message', async (message) => {
    await message.say('world')
  })

  // how to write test case ? I want to get the reply message.
  // SOLUTION:
  let reply
  contact.on('message', message => {
    reply = message.text()
  })

  // contact say something to user
  contact.say('Hello').to(user)

  await wechaty.stop()

  // FINALLY!
  assert(reply === 'world')
})

I hope this example can explain how to use the mocker system for you.

A Complex Mocker Example

This weekend, I'm working on another complex unit test with the mocker and a text game: Math Master, which is one of our Wechaty Vorpal Extensions.

The Math Master game is an interactive text game between the bot and users, the rule is very sample: the bot will ask math questions, and the user needs to answer it in a period of time. In order to test this game, we need the power of the mocker system, with more testing codes.

You can expect to read the unit tests for it this week, I'll notify you in this issue thread when I get it done.

New Version Publish

About the fix: please wait for the v0.22.38 , I have just fixed the pack testing which blocked the module publish.

chs97 commented 4 years ago

@huan ok thanks. the mocker object initial twice, so the Contact pool init with empty. the bot can't reply successfully

huan commented 4 years ago

As I mentioned in my last post: please use the v0.22.38 to get a fixed version of the puppet mock.

chs97 commented 4 years ago

@huan This case can't work. I have some questions.

  1. why emit to talker ? code
  2. wechaty listened to the message, this message object, not mockMessage, code look like will created a new Message object, not mock message.
huan commented 4 years ago
  1. If you send a message to a friend, you and your friend will both received the message. That's why Wechaty will emit the message to talkers too because, on WeChat App, you will also get the message that you sent to others.
  2. Wechaty will always work with the Message, on the other side, the Mocker will always work with the MockMessage.
chs97 commented 4 years ago

You can run this case, I don't know why Message Object Created unlimited


function sleep(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms))
}
test('reply', async (t) => {
  const mocker = new Mocker()
  const puppet = new PuppetMock({ mocker })
  const wechaty = new Wechaty({ puppet })
  await wechaty.start()

  const [user, contact] = mocker.createContacts(3)
  await mocker.login(user)

  // ATTENTION: you need to register your listener before you say anything
  wechaty.on('message', async (message) => {
    await message.say('world')
  })

  // how to write test case ? I want to get the reply message.
  // SOLUTION:
  let reply
  contact.on('message', message => {
    reply = message.text()
  })

  // contact say something to user
  contact.say('Hello').to(user)

  await sleep(1000)
  await wechaty.stop()
  // FINALLY!
  t.isEqual(reply, 'world')
})
chs97 commented 4 years ago

Oh, I know,just because emit a message to himself? wechaty say ('world') talker also receives the event?

huan commented 4 years ago
  1. Yes, and No. Whether the message emitted by a bot will be received by itself will be dependent on the puppet implementation behavior.
  2. My code is just a pseudo-code, it's just for an explanation purpose.
  3. I can not understand what is Message Object Created unlimited, could you please explain it in details?
chs97 commented 4 years ago

wechaty receives a message, and replies a message, then this message emits to (talker, room, listener) so wechaty receive this message again? then reply a message, once and once, is right? @huan

huan commented 4 years ago

We have a createFixture helper function now.

See: https://github.com/wechaty/wechaty/issues/2022

huan commented 4 years ago

wechaty receives a message and replies a message, then this message emits to (talker, room, listener) so wechaty receive this message again? then reply a message, once and once, is right? @huan

No, that will not and should not happen.

You should never reply what said by yourself, that's why we need to do the follow code logic:

wechaty.on('message', message => {
  if (message.self()) { return }
  // to reply the message after the above code
})
chs97 commented 4 years ago

ok, it works. thanks.

huan commented 4 years ago

You are welcome.

huan commented 4 years ago

As I promised, here's the unit test for Wechaty Vorpal Text Game: The Math Master, powered by the Wechaty Mocker:

https://github.com/wechaty/wechaty-vorpal-contrib/blob/master/src/contrib/math_master/math_master.spec.ts