test

testtest

1、单元测试

针对的是代码中的每个类,一个类认为是一个代码组件,对每个代码组件都编写一个单元测试类。这个单元测试类中会有多个方法,通常至少对要测试的类中的每个方法都编写一个对应的测试方法。

单元测试,面向的是代码组件的级别,它是最小最细粒度的测试单元

通常而言,通过单元测试检查出来的bug是最多的,所以它是位于测试金字塔模型的最底端

单元测试,需要每个RD对自己编写的代码自己去写单元测试,一般是使用JUnit框架,如果你使用了一些其他的框架,Spring,Spring MVC,都有对应的跟JUnit整合起来进行单元测试的一些框架,逻辑判断的Hamcrest框架,mock对象的Mockito框架。
初步的一些规范:

  1. 单元测试类,必须以test来结尾,以要测试的类来打头
  2. 针对每个类,一般都要写一个单元测试类来进行测试
  3. 单元测试中的每个方法,针对的是被测试类中的每个方法,方法以test打头,跟上要测试的方法名称.

下面给出单元测试的一个基本的例子:

public class HelloWorld {
    
    public String sayHello(String name) {
        return "hello, " + name;
    }
}
public class HelloWorldTest {
    
    private HelloWorld helloWorld = new HelloWorld();
    
    @Test
    public void testSayHello() {
        String name = "leo";
        String result = helloWorld.sayHello(name);
        assertEquals("hello, leo", result);
    }
}

下面是使用测试替身的一个例子

public class HelloWorld {
    
    private GreetingMessageService greetingMessageService;
    
    public String sayHello(String name) {
        greetingMessageSerivce.save(name);
        return "hello, " + name;
    }
    
    public void setGreetingMessageService(GreetingMessageService greetingMessageService) {
        this.greetingMessageService = greetingMessageService;
    }
    
    public GreetingMessageService getGreetingMessageService() {
        return greetingMessageService;
    }
}

public class GreetingMessageServiceImpl implements GreetingMessageService {
    
    private GreetingMessageDAO greetingMessageDAO;
    
    public void save(String name) {
        greetingMessageDAO.save(name);
    }
    
}

重要的原则:单元测试中,你测试一个类,就只能针对这一个类中的代码来进行测试。如果这个类依赖了其他的类,必须用测试替身将依赖的类和要测试的类隔离开来。为什么呢?记住,单元测试是什么,单元测试其实是最小的测试单位,一个单元测试类就负责测试一个代码组件(一个类),如果这个类依赖了其他的类。那么你就自己模拟一些测试替身,注入到要测试的类中去,将要测试的类和依赖的类隔离开来。

避免说,要测试的类和依赖的类耦合在一起测试,互相如果有bug会互相影响

public class GreetingMessageServiceStub implements GreetingMessageService {
    
    public void save(String name) {
        System.out.println("received a message: " + name);
    }
    
}

 
public class HelloWorldTest {
    
    private HelloWorld helloWorld;
    
    @Before
    public void setup() {
        this.helloWorld = new HelloWorld();
        GreetingMessageService greetingMessageService = new GreetingMessageServiceStub();
        this.helloWorld.setGreetingMessageService(greetingMessageService);
    }
    
    @Test
    public void testSayHello() {
        String name = "leo";
        String result = helloWorld.sayHello(name);
        assertEquals("hello, leo", result);
    }
    
    @After
    public void teardown() {
        this.helloWorld = null;
    }
    
}

fake对象的示例

public class UserDAOFake implements UserDAO {
    
    private Map<Long, User> mockDB = new HashMap<Long, User>();
    
    public void save(User user) {
        mockDB.put(user.getUserId(), user);
    }
    
    public void update(User user) {
        mockDB.put(user.getUserId(), user);
    }
    
    public void remove(Long userId) {
        mockDB.remove(userId);
    }
    
    public User getUserById(Long userId) {
        return mockDB.get(userId);
    }
    
}

spy对象的示例

public class GreetingMessageServiceSpy implements GreetingMessageService {
    
    private String _name;
    
    public void save(String name) {
        this._name = name;
        System.out.println("received a message: " + name);
    }
    
    public boolean received(String name) {
        return _name.equals(name);
    }
    
}

 
public class HelloWorldTest {
    
    private HelloWorld helloWorld;
    
    @Before
    public void setup() {
        this.helloWorld = new HelloWorld();
        GreetingMessageService greetingMessageService = new GreetingMessageServiceSpy();
        this.helloWorld.setGreetingMessageService(greetingMessageService);
    }
    
    @Test
    public void testSayHello() {
        String name = "leo";
        String result = helloWorld.sayHello(name);
        assertEquals("hello, leo", result);
        
        GreetingMessageServiceSpy greetingMessageService = 
            (GreetingMessageServiceSpy)helloWorld.getGreetingMessageService();
        assertTrue(greetingMessageService.received(name));
    }
    
    @After
    public void teardown() {
        this.helloWorld = null;
    }
    
}

mock对象的伪代码示例

public class HelloWorldTest {
    
    private HelloWorld helloWorld;
    
    @Before
    public void setup() {
        this.helloWorld = new HelloWorld();
        
        GreetingMessageService greetingMessageSerivce = mock(GreetingMessageSerivce.class);
        when(greetingMessageSerivce.save("leo")).thenReturn("success");
        
        this.helloWorld.setGreetingMessageService(greetingMessageService);
    }
    
    @Test
    public void testSayHello() {
        String name = "leo";
        String result = helloWorld.sayHello(name);
        assertEquals("hello, leo", result);
    }
    
    @After
    public void teardown() {
        this.helloWorld = null;
    }
    
}

重要原则:单元测试绝对不能依赖任何的外部基础设施,比如说mysql、redis、rabbitmq,绝对不能依赖这些东西来写单元测试。如果要依赖的话,直接注入测试替身,用模拟的行为来替代掉。

重要规范:单元测试覆盖率,要保证你写的单元测试覆盖了足够多的代码,保证覆盖的代码至少达到70%,对核心模块的代码要覆盖100%

一个好的单元测试要符合的一个规范和规则

  1. 什么是单元测试?
  2. 直接裸奔不写单元测试的弊端
    如果裸奔不写单元测试的话,就直接意味着,这个RD根本就没有对自己编写的代码负责;
    最基本的单元测试都没有,凭什么让人相信你的代码是经过测试的呢?
    写更多的单元测试对代码质量提高的稳定曲线.
    写更多的单元测试对代码设计质量提高的稳定曲线
  3. 优秀的单元测试有哪些特质?
    可读性
    良好的测试代码结构
    3.精准的测试名称
    测试代码的可重复性
  4. 自动化单元测试的3大工具
    测试框架:JUnit来测试,加上其他一些辅助性质的测试框架
    运行单元测试的自动化构建:基于maven的插件,mvn test,自动把所有的单元测试都自动化跑一遍
    测试替身:主要使用的是Mockito框架,模拟出来各种各样实现某个接口的类,每个方法的模拟的行为是什么
  5. 测试替身
    测试替身的几大作用:
    隔离要测试的代码组件:将测试替身对象传入要测试的组件
    加快测试执行速度:替换复杂耗时的代码执行
    有的时候会有这样的一种情况,比如你要测试的是类A,类A依赖了类B,类B中的代码非常复杂,执行了大量的数据库读写操作, 导致类B的那个方法跑起来是很慢的
    就是用测试替身代替类B,将类B中的方法实现为非常简单的一些打印日志的模拟的实现就可以了,因为我们重点要测试的是类A,不是类B
    测试替身的一个效果,就是可以加速类A中的方法的执行速度,也就可以加快我们的单元测试运行的速度
    让代码行为变得足够稳定:将随机行为改为固定行为
    1.类A依赖了类B,类B中会随机生成一个随机数,根据随机数来执行一些行为, 这就会导致类A每次调用类B,执行的结果是随机的,不够稳定
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值