瀏覽代碼

:art: 优化企业微信消息发送接口代码,引入moco模拟测试组件,方便测试代码

Binary Wang 4 年之前
父節點
當前提交
c01347cac6

+ 7 - 1
pom.xml

@@ -182,7 +182,7 @@
       <dependency>
         <groupId>com.google.guava</groupId>
         <artifactId>guava</artifactId>
-        <version>29.0-android</version>
+        <version>29.0-jre</version>
       </dependency>
       <dependency>
         <groupId>com.google.code.gson</groupId>
@@ -239,6 +239,12 @@
         <version>3.0.0</version>
         <scope>test</scope>
       </dependency>
+      <dependency>
+        <groupId>com.github.dreamhead</groupId>
+        <artifactId>moco-runner</artifactId>
+        <version>1.1.0</version>
+        <scope>test</scope>
+      </dependency>
 
       <dependency>
         <groupId>redis.clients</groupId>

+ 2 - 2
spring-boot-starters/wx-java-mp-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/mp/properties/WxMpProperties.java

@@ -7,8 +7,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
 
 import java.io.Serializable;
 
-import static com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties.PREFIX;
 import static com.binarywang.spring.starter.wxjava.mp.enums.StorageType.Memory;
+import static com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties.PREFIX;
 
 
 /**
@@ -68,7 +68,7 @@ public class WxMpProperties {
     /**
      * http客户端类型.
      */
-    private HttpClientType httpClientType = HttpClientType.httpclient;
+    private HttpClientType httpClientType = HttpClientType.HttpClient;
 
     /**
      * http代理主机.

+ 5 - 0
weixin-java-cp/pom.xml

@@ -83,6 +83,11 @@
       <artifactId>assertj-guava</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.github.dreamhead</groupId>
+      <artifactId>moco-runner</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>

+ 25 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageService.java

@@ -0,0 +1,25 @@
+package me.chanjar.weixin.cp.api;
+
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.bean.WxCpMessage;
+import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
+
+/**
+ * 消息推送接口.
+ *
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ * @date 2020 -08-30
+ */
+public interface WxCpMessageService {
+  /**
+   * <pre>
+   * 发送消息
+   * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
+   * </pre>
+   *
+   * @param message 要发送的消息对象
+   * @return the wx cp message send result
+   * @throws WxErrorException the wx error exception
+   */
+  WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException;
+}

+ 113 - 25
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java

@@ -29,13 +29,16 @@ public interface WxCpService {
    * @param timestamp    时间戳
    * @param nonce        随机数
    * @param data         微信传输过来的数据,有可能是echoStr,有可能是xml消息
+   * @return the boolean
    */
   boolean checkSignature(String msgSignature, String timestamp, String nonce, String data);
 
   /**
    * 获取access_token, 不强制刷新access_token
    *
-   * @see #getAccessToken(boolean)
+   * @return the access token
+   * @throws WxErrorException the wx error exception
+   * @see #getAccessToken(boolean) #getAccessToken(boolean)
    */
   String getAccessToken() throws WxErrorException;
 
@@ -49,13 +52,17 @@ public interface WxCpService {
    * </pre>
    *
    * @param forceRefresh 强制刷新
+   * @return the access token
+   * @throws WxErrorException the wx error exception
    */
   String getAccessToken(boolean forceRefresh) throws WxErrorException;
 
   /**
    * 获得jsapi_ticket,不强制刷新jsapi_ticket
    *
-   * @see #getJsapiTicket(boolean)
+   * @return the jsapi ticket
+   * @throws WxErrorException the wx error exception
+   * @see #getJsapiTicket(boolean) #getJsapiTicket(boolean)
    */
   String getJsapiTicket() throws WxErrorException;
 
@@ -68,6 +75,8 @@ public interface WxCpService {
    * </pre>
    *
    * @param forceRefresh 强制刷新
+   * @return the jsapi ticket
+   * @throws WxErrorException the wx error exception
    */
   String getJsapiTicket(boolean forceRefresh) throws WxErrorException;
 
@@ -78,7 +87,9 @@ public interface WxCpService {
    * 签名的jsapi_ticket必须使用以下接口获取。且必须用wx.agentConfig中的agentid对应的应用secret去获取access_token。
    * 签名用的noncestr和timestamp必须与wx.agentConfig中的nonceStr和timestamp相同。
    *
-   * @see #getJsapiTicket(boolean)
+   * @return the agent jsapi ticket
+   * @throws WxErrorException the wx error exception
+   * @see #getJsapiTicket(boolean) #getJsapiTicket(boolean)
    */
   String getAgentJsapiTicket() throws WxErrorException;
 
@@ -96,6 +107,8 @@ public interface WxCpService {
    * </pre>
    *
    * @param forceRefresh 强制刷新
+   * @return the agent jsapi ticket
+   * @throws WxErrorException the wx error exception
    */
   String getAgentJsapiTicket(boolean forceRefresh) throws WxErrorException;
 
@@ -107,23 +120,18 @@ public interface WxCpService {
    * </pre>
    *
    * @param url url
+   * @return the wx jsapi signature
+   * @throws WxErrorException the wx error exception
    */
   WxJsapiSignature createJsapiSignature(String url) throws WxErrorException;
 
-  /**
-   * <pre>
-   * 发送消息
-   * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
-   * </pre>
-   *
-   * @param message 要发送的消息对象
-   */
-  WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException;
 
   /**
    * 小程序登录凭证校验
    *
    * @param jsCode 登录时获取的 code
+   * @return the wx cp ma js code 2 session result
+   * @throws WxErrorException the wx error exception
    */
   WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException;
 
@@ -134,6 +142,7 @@ public interface WxCpService {
    * </pre>
    *
    * @return { "ip_list": ["101.226.103.*", "101.226.62.*"] }
+   * @throws WxErrorException the wx error exception
    */
   String[] getCallbackIp() throws WxErrorException;
 
@@ -147,12 +156,7 @@ public interface WxCpService {
    *
    * @param corpId         服务商的corpid
    * @param providerSecret 服务商的secret,在服务商管理后台可见
-   * @return {
-   * "errcode":0 ,
-   * "errmsg":"ok" ,
-   * "provider_access_token":"enLSZ5xxxxxxJRL",
-   * "expires_in":7200
-   * }
+   * @return { "errcode":0 , "errmsg":"ok" , "provider_access_token":"enLSZ5xxxxxxJRL", "expires_in":7200 }
    * @throws WxErrorException .
    */
   WxCpProviderToken getProviderToken(String corpId, String providerSecret) throws WxErrorException;
@@ -162,6 +166,8 @@ public interface WxCpService {
    *
    * @param url        接口地址
    * @param queryParam 请求参数
+   * @return the string
+   * @throws WxErrorException the wx error exception
    */
   String get(String url, String queryParam) throws WxErrorException;
 
@@ -170,6 +176,8 @@ public interface WxCpService {
    *
    * @param url      接口地址
    * @param postData 请求body字符串
+   * @return the string
+   * @throws WxErrorException the wx error exception
    */
   String post(String url, String postData) throws WxErrorException;
 
@@ -178,6 +186,8 @@ public interface WxCpService {
    *
    * @param url      接口地址
    * @param postData 请求body字符串
+   * @return the string
+   * @throws WxErrorException the wx error exception
    */
   String postWithoutToken(String url, String postData) throws WxErrorException;
 
@@ -188,11 +198,13 @@ public interface WxCpService {
    * 可以参考,{@link MediaUploadRequestExecutor}的实现方法
    * </pre>
    *
+   * @param <T>      请求值类型
+   * @param <E>      返回值类型
    * @param executor 执行器
    * @param uri      请求地址
    * @param data     参数
-   * @param <T>      请求值类型
-   * @param <E>      返回值类型
+   * @return the t
+   * @throws WxErrorException the wx error exception
    */
   <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException;
 
@@ -220,6 +232,7 @@ public interface WxCpService {
    * 获取某个sessionId对应的session,如果sessionId没有对应的session,则新建一个并返回。
    *
    * @param id id可以为任意字符串,建议使用FromUserName作为id
+   * @return the session
    */
   WxSession getSession(String id);
 
@@ -228,13 +241,14 @@ public interface WxCpService {
    *
    * @param id     id可以为任意字符串,建议使用FromUserName作为id
    * @param create 是否新建
+   * @return the session
    */
   WxSession getSession(String id, boolean create);
 
   /**
    * 获取WxSessionManager 对象
    *
-   * @return WxSessionManager
+   * @return WxSessionManager session manager
    */
   WxSessionManager getSessionManager();
 
@@ -252,6 +266,8 @@ public interface WxCpService {
    * 上传部门列表覆盖企业号上的部门信息
    *
    * @param mediaId 媒体id
+   * @return the string
+   * @throws WxErrorException the wx error exception
    */
   String replaceParty(String mediaId) throws WxErrorException;
 
@@ -259,11 +275,17 @@ public interface WxCpService {
    * 上传用户列表覆盖企业号上的用户信息
    *
    * @param mediaId 媒体id
+   * @return the string
+   * @throws WxErrorException the wx error exception
    */
   String replaceUser(String mediaId) throws WxErrorException;
 
   /**
    * 获取异步任务结果
+   *
+   * @param joinId the join id
+   * @return the task result
+   * @throws WxErrorException the wx error exception
    */
   String getTaskResult(String joinId) throws WxErrorException;
 
@@ -275,7 +297,7 @@ public interface WxCpService {
   /**
    * 获取WxMpConfigStorage 对象
    *
-   * @return WxMpConfigStorage
+   * @return WxMpConfigStorage wx cp config storage
    */
   WxCpConfigStorage getWxCpConfigStorage();
 
@@ -288,75 +310,141 @@ public interface WxCpService {
 
   /**
    * 获取部门相关接口的服务类对象
+   *
+   * @return the department service
    */
   WxCpDepartmentService getDepartmentService();
 
   /**
    * 获取媒体相关接口的服务类对象
+   *
+   * @return the media service
    */
   WxCpMediaService getMediaService();
 
   /**
    * 获取菜单相关接口的服务类对象
+   *
+   * @return the menu service
    */
   WxCpMenuService getMenuService();
 
   /**
    * 获取Oauth2相关接口的服务类对象
+   *
+   * @return the oauth 2 service
    */
   WxCpOAuth2Service getOauth2Service();
 
   /**
    * 获取标签相关接口的服务类对象
+   *
+   * @return the tag service
    */
   WxCpTagService getTagService();
 
   /**
    * 获取用户相关接口的服务类对象
+   *
+   * @return the user service
    */
   WxCpUserService getUserService();
 
+  /**
+   * Gets external contact service.
+   *
+   * @return the external contact service
+   */
   WxCpExternalContactService getExternalContactService();
 
   /**
    * 获取群聊服务
    *
-   * @return 群聊服务
+   * @return 群聊服务 chat service
    */
   WxCpChatService getChatService();
 
   /**
    * 获取任务卡片服务
    *
-   * @return 任务卡片服务
+   * @return 任务卡片服务 task card service
    */
   WxCpTaskCardService getTaskCardService();
 
+  /**
+   * Gets agent service.
+   *
+   * @return the agent service
+   */
   WxCpAgentService getAgentService();
 
+  /**
+   * Gets message service.
+   *
+   * @return the message service
+   */
+  WxCpMessageService getMessageService();
+
+  /**
+   * Gets oa service.
+   *
+   * @return the oa service
+   */
   WxCpOaService getOAService();
 
   /**
    * 获取群机器人消息推送服务
    *
-   * @return 群机器人消息推送服务
+   * @return 群机器人消息推送服务 group robot service
    */
   WxCpGroupRobotService getGroupRobotService();
 
   /**
    * http请求对象
+   *
+   * @return the request http
    */
   RequestHttp<?, ?> getRequestHttp();
 
+  /**
+   * Sets user service.
+   *
+   * @param userService the user service
+   */
   void setUserService(WxCpUserService userService);
 
+  /**
+   * Sets department service.
+   *
+   * @param departmentService the department service
+   */
   void setDepartmentService(WxCpDepartmentService departmentService);
 
+  /**
+   * Sets media service.
+   *
+   * @param mediaService the media service
+   */
   void setMediaService(WxCpMediaService mediaService);
 
+  /**
+   * Sets menu service.
+   *
+   * @param menuService the menu service
+   */
   void setMenuService(WxCpMenuService menuService);
 
+  /**
+   * Sets oauth 2 service.
+   *
+   * @param oauth2Service the oauth 2 service
+   */
   void setOauth2Service(WxCpOAuth2Service oauth2Service);
 
+  /**
+   * Sets tag service.
+   *
+   * @param tagService the tag service
+   */
   void setTagService(WxCpTagService tagService);
 }

+ 6 - 12
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/BaseWxCpServiceImpl.java

@@ -22,8 +22,6 @@ import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
 import me.chanjar.weixin.common.util.json.GsonParser;
 import me.chanjar.weixin.cp.api.*;
 import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult;
-import me.chanjar.weixin.cp.bean.WxCpMessage;
-import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
 import me.chanjar.weixin.cp.bean.WxCpProviderToken;
 import me.chanjar.weixin.cp.config.WxCpConfigStorage;
 
@@ -53,6 +51,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
   private WxCpTaskCardService taskCardService = new WxCpTaskCardServiceImpl(this);
   private WxCpExternalContactService externalContactService = new WxCpExternalContactServiceImpl(this);
   private WxCpGroupRobotService groupRobotService = new WxCpGroupRobotServiceImpl(this);
+  private WxCpMessageService messageService = new WxCpMessageServiceImpl(this);
 
   /**
    * 全局的是否正在刷新access token的锁.
@@ -170,16 +169,6 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
   }
 
   @Override
-  public WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException {
-    Integer agentId = message.getAgentId();
-    if (null == agentId) {
-      message.setAgentId(this.getWxCpConfigStorage().getAgentId());
-    }
-
-    return WxCpMessageSendResult.fromJson(this.post(this.configStorage.getApiUrl(MESSAGE_SEND), message.toJson()));
-  }
-
-  @Override
   public WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException {
     Map<String, String> params = new HashMap<>(2);
     params.put("js_code", jsCode);
@@ -486,6 +475,11 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH
     return agentService;
   }
 
+  @Override
+  public WxCpMessageService getMessageService() {
+    return this.messageService;
+  }
+
   public void setAgentService(WxCpAgentService agentService) {
     this.agentService = agentService;
   }

+ 31 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMessageServiceImpl.java

@@ -0,0 +1,31 @@
+package me.chanjar.weixin.cp.api.impl;
+
+import lombok.RequiredArgsConstructor;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.api.WxCpMessageService;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.WxCpMessage;
+import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
+import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
+
+/**
+ * 消息推送接口实现类.
+ *
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ * @date 2020-08-30
+ */
+@RequiredArgsConstructor
+public class WxCpMessageServiceImpl implements WxCpMessageService {
+  private final WxCpService cpService;
+
+  @Override
+  public WxCpMessageSendResult messageSend(WxCpMessage message) throws WxErrorException {
+    Integer agentId = message.getAgentId();
+    if (null == agentId) {
+      message.setAgentId(this.cpService.getWxCpConfigStorage().getAgentId());
+    }
+
+    return WxCpMessageSendResult.fromJson(this.cpService.post(this.cpService.getWxCpConfigStorage()
+      .getApiUrl(WxCpApiPathConsts.Message.MESSAGE_SEND), message.toJson()));
+  }
+}

+ 16 - 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java

@@ -14,7 +14,6 @@ public final class WxCpApiPathConsts {
 
   public static final String GET_JSAPI_TICKET = "/cgi-bin/get_jsapi_ticket";
   public static final String GET_AGENT_CONFIG_TICKET = "/cgi-bin/ticket/get?&type=agent_config";
-  public static final String MESSAGE_SEND = "/cgi-bin/message/send";
   public static final String GET_CALLBACK_IP = "/cgi-bin/getcallbackip";
   public static final String BATCH_REPLACE_PARTY = "/cgi-bin/batch/replaceparty";
   public static final String BATCH_REPLACE_USER = "/cgi-bin/batch/replaceuser";
@@ -23,6 +22,22 @@ public final class WxCpApiPathConsts {
   public static final String GET_TOKEN = "/cgi-bin/gettoken?corpid=%s&corpsecret=%s";
   public static final String WEBHOOK_SEND = "/cgi-bin/webhook/send?key=";
 
+  /**
+   * 消息推送相关接口
+   * https://work.weixin.qq.com/api/doc/90000/90135/90235
+   */
+  public static class Message {
+    /**
+     * 发送应用消息
+     */
+    public static final String MESSAGE_SEND = "/cgi-bin/message/send";
+
+    /**
+     * 互联企业发送应用消息
+     */
+    public static final String LINKEDCORP_MESSAGE_SEND = "/cgi-bin/linkedcorp/message/send";
+  }
+
   public static class Agent {
     public static final String AGENT_GET = "/cgi-bin/agent/get?agentid=%d";
     public static final String AGENT_SET = "/cgi-bin/agent/set";

+ 13 - 55
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModule.java

@@ -1,22 +1,23 @@
 package me.chanjar.weixin.cp.api;
 
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import com.google.inject.Binder;
 import com.google.inject.Module;
 import com.thoughtworks.xstream.XStream;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.util.xml.XStreamInitializer;
 import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
 import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
 
+import java.io.IOException;
+import java.io.InputStream;
+
+@Slf4j
 public class ApiTestModule implements Module {
-  private final Logger log = LoggerFactory.getLogger(this.getClass());
   private static final String TEST_CONFIG_XML = "test-config.xml";
+  protected WxXmlCpInMemoryConfigStorage config;
 
   private static <T> T fromXml(Class<T> clazz, InputStream is) {
     XStream xstream = XStreamInitializer.getInstance();
@@ -32,70 +33,27 @@ public class ApiTestModule implements Module {
         throw new RuntimeException("测试配置文件【" + TEST_CONFIG_XML + "】未找到,请参照test-config-sample.xml文件生成");
       }
 
-      WxXmlCpInMemoryConfigStorage config = fromXml(WxXmlCpInMemoryConfigStorage.class, inputStream);
+      config = fromXml(WxXmlCpInMemoryConfigStorage.class, inputStream);
       WxCpService wxService = new WxCpServiceImpl();
       wxService.setWxCpConfigStorage(config);
 
       binder.bind(WxCpService.class).toInstance(wxService);
       binder.bind(WxXmlCpInMemoryConfigStorage.class).toInstance(config);
     } catch (IOException e) {
-      this.log.error(e.getMessage(), e);
+      log.error(e.getMessage(), e);
     }
   }
 
+  @Data
+  @EqualsAndHashCode(callSuper = true)
   @XStreamAlias("xml")
   public static class WxXmlCpInMemoryConfigStorage extends WxCpDefaultConfigImpl {
+    private static final long serialVersionUID = -4521839921547374822L;
 
     protected String userId;
-
     protected String departmentId;
-
     protected String tagId;
-
     protected String externalUserId;
-
-    public String getUserId() {
-      return this.userId;
-    }
-
-    public void setUserId(String userId) {
-      this.userId = userId;
-    }
-
-    public String getDepartmentId() {
-      return this.departmentId;
-    }
-
-    public void setDepartmentId(String departmentId) {
-      this.departmentId = departmentId;
-    }
-
-    public String getTagId() {
-      return this.tagId;
-    }
-
-    public void setTagId(String tagId) {
-      this.tagId = tagId;
-    }
-
-    public String getExternalUserId() {
-      return externalUserId;
-    }
-
-    public void setExternalUserId(String externalUserId) {
-      this.externalUserId = externalUserId;
-    }
-
-    @Override
-    public String toString() {
-      return super.toString() + " > WxXmlCpConfigStorage{" +
-        "userId='" + this.userId + '\'' +
-        ", departmentId='" + this.departmentId + '\'' +
-        ", tagId='" + this.tagId + '\'' +
-        ", externalUserId='" + this.externalUserId + '\'' +
-
-        '}';
-    }
   }
 
 }

+ 19 - 0
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/ApiTestModuleWithMockServer.java

@@ -0,0 +1,19 @@
+package me.chanjar.weixin.cp.api;
+
+import com.google.inject.Binder;
+
+/**
+ * 带mock server 的test module.
+ *
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ * @date 2020-08-30
+ */
+public class ApiTestModuleWithMockServer extends ApiTestModule {
+  public static final int mockServerPort = 8080;
+
+  @Override
+  public void configure(Binder binder) {
+    super.configure(binder);
+    super.config.setBaseApiUrl("http://localhost:" + mockServerPort);
+  }
+}

+ 43 - 23
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMessageAPITest.java

@@ -1,33 +1,53 @@
-package me.chanjar.weixin.cp.api;
+package me.chanjar.weixin.cp.api.impl;
 
+import com.github.dreamhead.moco.HttpServer;
+import com.github.dreamhead.moco.Runner;
 import com.google.common.collect.ImmutableMap;
-import org.testng.annotations.*;
-
 import com.google.inject.Inject;
 import me.chanjar.weixin.common.api.WxConsts;
 import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.api.ApiTestModule;
+import me.chanjar.weixin.cp.api.ApiTestModuleWithMockServer;
+import me.chanjar.weixin.cp.api.WxCpService;
 import me.chanjar.weixin.cp.bean.WxCpMessage;
 import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
-
-import static org.testng.Assert.*;
-
-/***
- * 测试发送消息
- * @author Daniel Qian
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import static com.github.dreamhead.moco.Moco.file;
+import static com.github.dreamhead.moco.MocoJsonRunner.jsonHttpServer;
+import static me.chanjar.weixin.cp.api.ApiTestModuleWithMockServer.mockServerPort;
+import static org.testng.Assert.assertNotNull;
+
+/**
+ * 测试类.
  *
+ * @author <a href="https://github.com/binarywang">Binary Wang</a>
+ * @date 2020-08-30
  */
 @Test
-@Guice(modules = ApiTestModule.class)
-public class WxCpMessageAPITest {
-
+@Guice(modules = ApiTestModuleWithMockServer.class)
+//@Guice(modules = ApiTestModule.class)
+public class WxCpMessageServiceImplTest {
   @Inject
   protected WxCpService wxService;
 
+  private Runner mockRunner;
   private ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage;
 
   @BeforeTest
   public void setup() {
-    configStorage = (ApiTestModule.WxXmlCpInMemoryConfigStorage) this.wxService.getWxCpConfigStorage();
+    HttpServer mockServer = jsonHttpServer(mockServerPort, file("src/test/resources/moco/message.json"));
+    this.mockRunner = Runner.runner(mockServer);
+    this.mockRunner.start();
+    this.configStorage = (ApiTestModule.WxXmlCpInMemoryConfigStorage) this.wxService.getWxCpConfigStorage();
+  }
+
+  @AfterTest
+  public void stopMockServer() {
+    this.mockRunner.stop();
   }
 
   public void testSendMessage() throws WxErrorException {
@@ -37,7 +57,7 @@ public class WxCpMessageAPITest {
     message.setToUser(configStorage.getUserId());
     message.setContent("欢迎欢迎,热烈欢迎\n换行测试\n超链接:<a href=\"http://www.baidu.com\">Hello World</a>");
 
-    WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message);
+    WxCpMessageSendResult messageSendResult = this.wxService.getMessageService().messageSend(message);
     assertNotNull(messageSendResult);
     System.out.println(messageSendResult);
     System.out.println(messageSendResult.getInvalidPartyList());
@@ -54,7 +74,7 @@ public class WxCpMessageAPITest {
       .content("欢迎欢迎,热烈欢迎\n换行测试\n超链接:<a href=\"http://www.baidu.com\">Hello World</a>")
       .build();
 
-    WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message);
+    WxCpMessageSendResult messageSendResult = this.wxService.getMessageService().messageSend(message);
     assertNotNull(messageSendResult);
     System.out.println(messageSendResult);
     System.out.println(messageSendResult.getInvalidPartyList());
@@ -82,7 +102,7 @@ public class WxCpMessageAPITest {
         "                >如需修改会议信息,请点击:[修改会议信息](https://work.weixin.qq.com)")
       .build();
 
-    WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message);
+    WxCpMessageSendResult messageSendResult = this.wxService.getMessageService().messageSend(message);
     assertNotNull(messageSendResult);
     System.out.println(messageSendResult);
     System.out.println(messageSendResult.getInvalidPartyList());
@@ -96,12 +116,12 @@ public class WxCpMessageAPITest {
       .TEXTCARD()
       .toUser(configStorage.getUserId())
       .btnTxt("更多")
-      .description( "<div class=\"gray\">2016年9月26日</div> <div class=\"normal\">恭喜你抽中iPhone 7一台,领奖码:xxxx</div><div class=\"highlight\">请于2016年10月10日前联系行政同事领取</div>")
+      .description("<div class=\"gray\">2016年9月26日</div> <div class=\"normal\">恭喜你抽中iPhone 7一台,领奖码:xxxx</div><div class=\"highlight\">请于2016年10月10日前联系行政同事领取</div>")
       .url("URL")
       .title("领奖通知")
       .build();
 
-    WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message);
+    WxCpMessageSendResult messageSendResult = this.wxService.getMessageService().messageSend(message);
     assertNotNull(messageSendResult);
     System.out.println(messageSendResult);
     System.out.println(messageSendResult.getInvalidPartyList());
@@ -110,7 +130,7 @@ public class WxCpMessageAPITest {
   }
 
   @Test
-  public void testSendMessage_miniprogram_notice() throws WxErrorException {
+  public void testSendMessage_miniProgram_notice() throws WxErrorException {
     WxCpMessage message = WxCpMessage
       .newMiniProgramNoticeBuilder()
       .toUser(configStorage.getUserId())
@@ -119,12 +139,12 @@ public class WxCpMessageAPITest {
       .title("会议室预订成功通知")
       .description("4月27日 16:16")
       .emphasisFirstItem(true)
-      .contentItems(ImmutableMap.of("会议室","402",
-        "会议地点","广州TIT-402会议室",
-        "会议时间","2018年8月1日 09:00-09:30"))
+      .contentItems(ImmutableMap.of("会议室", "402",
+        "会议地点", "广州TIT-402会议室",
+        "会议时间", "2018年8月1日 09:00-09:30"))
       .build();
 
-    WxCpMessageSendResult messageSendResult = this.wxService.messageSend(message);
+    WxCpMessageSendResult messageSendResult = this.wxService.getMessageService().messageSend(message);
     assertNotNull(messageSendResult);
     System.out.println(messageSendResult);
     System.out.println(messageSendResult.getInvalidPartyList());

+ 1 - 1
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImplTest.java

@@ -49,7 +49,7 @@ public class WxCpTaskCardServiceImplTest {
       .buttons(Arrays.asList(btn1, btn2))
       .build();
 
-    WxCpMessageSendResult messageSendResult = this.wxCpService.messageSend(message);
+    WxCpMessageSendResult messageSendResult = this.wxCpService.getMessageService().messageSend(message);
     assertNotNull(messageSendResult);
     System.out.println(messageSendResult);
     System.out.println(messageSendResult.getInvalidPartyList());

+ 18 - 0
weixin-java-cp/src/test/resources/moco/message.json

@@ -0,0 +1,18 @@
+[
+  {
+    "request": {
+      "uri": "/cgi-bin/gettoken"
+    },
+    "response": {
+      "text": "{\"errcode\":0,\"errmsg\":\"ok\",\"access_token\":\"oG1MrhLSzGBl4YxM1W2EHJlL_5vAotNwQ6KBp98sP2fO8XGPPRUlWS9w98CKjxSgPx4YnTy0DU_DvmNXAwt3mSDJ1Uhg_WCFrxX8GWbbCRlzrj2csK-1Y3tzI6dBCMa2YmblBo2sX7qkkzc9pnjP38GzO7Yuo_Bbpyi4doilNWZme0z9ovwiBCkAtV7DXYuh14EsnNrODG454kstOxsqWA\",\"expires_in\":7200}"
+    }
+  },
+  {
+    "request": {
+      "uri": "/cgi-bin/message/send"
+    },
+    "response": {
+      "text": "{\"errcode\":0,\"errmsg\":\"ok\",\"invaliduser\":\"\"}"
+    }
+  }
+]