zoukankan      html  css  js  c++  java
  • 对Singleton类解依赖

    上周六在杭州的《互联网软件测试大会》上做演讲的时候提到可测试性的话题,顺手举了一个例子:“B类存在对A类的依赖,A类是一个Singleton类,B类使用到了A类中的一个需要被mock的函数,这种情况下如何处理?” 本来,这是《修改代码的艺术》上曾经提到的一个典型模式,该书中有非常详细的描述。但今天收到某位听众的邮件,问这个问题应该怎么回答。

    正好好久没有写过blog了,于是顺手写了一段简单的代码来说明这个问题。

    问题:

    类Singleton是一个单件类,该类实现了一个名为getUserName()的函数,getUserName函数需要连接到数据库并从数据库中取出用户名称。类DependsOnSingleton类是被测类,该类中的sayHello函数使用到了Singleton类的getUserName函数,在对DependsOnSingleton类进行单元测试的时候,显然我们并不想设置一个真正的数据库,所以最理想的情况是使用mock方式得到一个可以被控制的Singleton类的getUserName函数的实现。如果Singleton类不是一个单件类,这件事情非常简单,使用接口提取的方法从Singleton类提取接口,然后就可以使用mock技术了。但由于Singleton是一个单件类,这件事情就比较棘手了:Singleton.getInstance会返回一个由getInstance自身逻辑决定的Singleton对象,这样就没法直接塞入一个mock对象了。

    代码:

    代码
     1 public class Singleton {
     2     static private Singleton _instance;
     3     public final String userName = "Dennis";
     4     
     5     private Singleton() {
     6     }
     7     
     8     static Singleton getInstance() {
     9         if(null == _instance) {
    10             _instance = new Singleton();
    11         }
    12         return _instance;
    13     }
    14     
    15     String getUserName() throws Exception{
    16         // Connect to DB and return data from DB
    17         // Throw Exception instead
    18         throw new Exception("There's no DB exist");
    19     }
    20 }
    1 public class DependsOnSingleton {
    2     
    3     ...
    4     public String sayHello() throws Exception {
    5         return "Hi, " + Singleton.getInstance().getUserName();
    6     }
    7 }

    解决:

    其实解决这个问题的思路,按照《修改代码的艺术》一书的说法,主要是解决“分离”的问题。在Singleton情况下,由于getInstance函数不受控导致了这样的情况,所以最简单的做法是为Singleton类增加一个setTestingInstance函数,使用这个函数注入一个受控的实例。

    修改后的Singleton:

    代码
     1 public class Singleton {
     2     static private Singleton _instance;
     3     public final String userName = "Dennis";
     4     
     5     private Singleton() {
     6     }
     7     
     8     public static Singleton getInstance() {
     9         if(null == _instance) {
    10             _instance = new Singleton();
    11         }
    12         return _instance;
    13     }
    14     
    15     public static void setTestingInstance(Singleton instance) {
    16         _instance = instance;
    17     }
    18     
    19     public String getUserName() throws Exception{
    20         // Connect to DB and return data from DB
    21         // Throw Exception instead
    22         throw new Exception("There's no DB exist");
    23     }
    24 }

    测试代码:

    代码
     1 import junit.framework.Assert;
     2 import junit.framework.TestCase;
     3 
     4 import org.easymock.EasyMock;
     5 
     6 public class TestDependsOnSingleton extends TestCase {
     7     public void testSayHello() throws Exception{
     8         Singleton depend = EasyMock.createMock(Singleton.class);
     9         Singleton.setTestingInstance(depend);
    10         
    11         EasyMock.expect(depend.getUserName()).andReturn("Dennis");
    12         EasyMock.replay(depend);
    13         
    14         DependsOnSingleton itu = new DependsOnSingleton();
    15         Assert.assertEquals("Hi, Dennis", itu.sayHello());
    16         EasyMock.verify(depend);
    17     }
    18 }
  • 相关阅读:
    后缀名文件说明
    转行小白成长路-java篇
    转行小白成长路-java篇
    转行小白成长路-java篇
    转行小白成长路-java篇
    转行小白成长路-java篇
    转行小白成长路-java篇
    转行小白成长路-java篇
    转行小白成长路-java篇
    转行小白成长路-java篇
  • 原文地址:https://www.cnblogs.com/guanhe/p/1878034.html
Copyright © 2011-2022 走看看