正确理解和使用测试--Junit的正确使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/bbbbln/article/details/80629272

好处

体现设计

  • 测试驱动迫使我们从调用者的角度去观察和思考问题,迫使我们把代码设计成可测试的,松耦合的。

验证行为

  • 保证代码的正确性

  • 回归测试:即使到项目后期,我们仍然有勇气去增加新功能,修改程序结构,而不用担心破坏重要功能
    给重构带来保证

作为文档

  • 单元测试是一种无价的文档,精确的描述了代码的行为,是如何使用函数和类的最佳文档。

原则

测试代码和被测代码是同等重要的,需要被同时维护

  • 测试代码不是附属品!

  • 不但要重构代码,也要重构单元测试!

单元测试一定是隔离的

  • 一个测试用例的运行结果不能影响其他测试用例

  • 测试用不能互相依赖,应该能够以任何次序执行

单元测试一定是可以重复执行的

  • 不能依赖环境的变化

保持单元测试的简单性和可读性

尽量对接口进行测试

  • 尽量公共方法,私有方法就不用

单元测试应该可以迅速执行

  • 给程序员提供及时的反馈

  • 使用Mock对象对数据库,网络的依赖进行解耦

自动化单元测试

  • 集成到build过程中去

常规测试用例:

public class CalculatorTest{

    @BeforeClass
    public void beforeClass(){
    }

    @AfterClass
    public void afterClass(){
    }
    @Before
    public void setUp(){
    }

    @After
    public void tearDown(){
    }

    @Test
    public void testCase1(){
    }

    @Test
    public void testCase2(){
    }
}

以上方法的执行顺序是:

beforeClass();

setUp();
testCase1();
tearDown();

setUp();
testCase2();
tearDown();

afterClass();

保证真正的测试用例有前置条件,并做好后置工作。

测试suite:

AllTests组织V1,V2,V3测试,V1再组织其它测试用例,依此类推。。。

  • AllTests.java
@RunWith(Suite.class)
@SuiteClasses({
    V1AllTests.class,
    V2AllTests.class,
    V3AllTests.class,
    ...
})
public class AllTests{

//...
}
  • V1Tests.java
@RunWith(Suite.class)
@SuiteClasses({
    ATest.class,
    BTest.class,
    CTest.class,
    ...
})
public class V1Tests{

//...
}

高级使用:

使用Mock对象

  • 真实的对象不易构造

    例如httpservlet 必须在servlet容器中才能创建出来

  • 真实的对象非常复杂

    如jdbc中的Connection, ResultSet

  • 真实的对象的行为具有不确定性,难于控制他们的输出或者返回结果

  • 真实的对象的有些行为难于触发, 例如硬盘已满,网络连接断开

  • 真实的对象可能还不存在,例如依赖的另外一个模块还没开发完毕

  • 使用Mock 对象“替代”或者“冒充” 真实模块和被测试对象进行交互

    开发人员可以精确的定制期待的行为

  • 对TDD提供有力的支持

    帮助你发现对象的角色和职责

    对接口编程,而不是对实现编程

Mock Object例子:

public class URLParser{
    public void parse(HttpServletRequest request){
        String startRow = request.getParameter(“startRow”);
        String endRow = request.getParameter(“endRow”);
        … do some business logic…
   }
}

使用:

//step 1: 创建mock 对象
MockControl control = MockControl.createControl(HttpServletRequest.class);
HttpServletRequest request = (HttpServletRequest) control.getMock();

//step2: 设置并记录mock对象的行为
request.getParameter("startRow");
control.setReturnValue("10");
request.getParameter("endRow");
control.setReturnValue("20"); 

// step3: 转换为回放模式
control.replay();

// step 4: 测试代码
URLParser parser = new URLParser(request);
parser.parse();
Assert xxx

其它技巧

重构遗留代码

  • 遗留代码不可避免

虽然TDD是很有效的编程方法,但是我们的工作很少从第一行代码开始。

  • 遗留代码不是坏代码

它是可以工作的软件/组件,但是在设计和开发式没有考虑“可测试性”

  • 遗留代码难于测试

长久失修,导致业务逻辑难于理解

依赖的资源太多,导致测试无从下手

不敢修改,害怕牵一发而动全身

  • 重构代码,提高可测试性

  • 使用Mock Object解除依赖

  • 测试分解

先写粗粒度的测试代码,然后编写细粒度的代码

Package -> Class -> method

处理遗留代码的步骤

  1. 确定要测试的类和函数

  2. 解除依赖

  3. 编写测试用例

  4. 重构代码

好的单元测试

  • 简单

防止过度的Setup,否则不知道是测试用例的错误,还是业务逻辑的错误

  • 隔离

  • 可重复

    防止在一台机器上可以运行,在另外一台机器上失败

    防止今天成功,明天失败

  • 运行快

    防止长时间的运行

  • 代码覆盖面广

    防止测试通过,但是没测到什么代码

猜你喜欢

转载自blog.csdn.net/bbbbln/article/details/80629272
今日推荐