敏捷测试价值观、方法和实践读书笔记(5)

简介: 本章节介绍了敏捷功能测试的原则与实践,包括单元测试的概念及其编写步骤,测试驱动开发(TDD)的流程,以及如何通过模拟对象进行测试。详细讲解了单元测试的编写方法,如初始化对象、执行操作及验证结果,并探讨了 TDD 的五个步骤。通过具体案例展示了如何逐步完善储蓄账户的功能测试,包括存款、取款及异常处理。此外,还讨论了代码覆盖率的重要性及其局限性,强调了测试充分性比单纯追求代码覆盖率更为关键。

第6章 敏捷功能测试原则

6.1 测试驱动开发(TDD)什么是单元测试

  • 面向过程的编程:整个模块(Module),但更常见的是一个单独的函数 (Function)或过程 (Procedure)
  • 面向对象的编程:一个完整的接口(Interface),上至一个类(Class),下至一个方法(Method),都可以是一个单元

编写单元测试时都遵循以下 3 步。

  1. 初始化对象
  2. 执行操作
  3. 验证结果

代码语言:javascript

复制

public void test check response is 200(){
    //初始化对象
    APIHelper apiHelper = new APIHelper 0;
    //执行 get 方法并获得对应代码的API结果
    HttpResponse response = apiHelper.get("http://www.baidu.com/");
    //验证结果
    assert(response getStatus()).is(200);
}

好的单元测试代码要具备以下 3 点。

  1. 测试代码的方法名能够体现出测试用例的内容。
  2. 初始化对象、执行操作和验证结果这3段之间有明显的分隔,一般使用空行进行分割
  3. 每个测试用例的代码行数均不多,每个测试用例只测试一个方法,测试目的是保证软件的可测试性。

什么是 TDD

测试驱动开发(Test Driven Development,TDD)

TDD 5步骤。

  1. 编写描述程序某方面功能的单个单元测试
  2. 运行单元测试,该测试会因为没有实现测试内容而失败
  3. 编写刚好够用的代码(最简单的方法) 使测试通过
  4. 重构代码,直到其符合简单性这一标准
  5. 随着时间的推移,重复累积单元测试

步骤

  1. 编写或重写自动化测试。
  2. 运行单元测试,查看测试是否失败,若成功,则返回第1步。
  3. 编写刚好能够通过测试的代码,让测试通过
  4. 如果测试通过,则检查全部测试是否都成功。
  5. 如果成功,则重构代码;如果失败,则更新或修复测试代码
  6. 除非有一个测试失败,否则不要写任何代码
  7. 定期重构,避免重复,保持代码设计的一致性和定义的唯一性。

除非存在没有通过的测试,否则不写代码

好处

  • 代码更简洁,设计更好
  • 代码更简单,维护成本更低
  • 从一开始就较少的 Bug
  • 一套全面的回归测试

案例

作为一名银行储户

我想要拥有一个储蓄账户

以便我可以存钱、取钱,并且显示当前余额

代码语言:javascript

复制

package com.Account.TDD;
public class Account {
}

代码语言:javascript

复制

package com.Account.TDD;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class AccountTest{
   @Test
   public void testCreateAccountTheBalanceIsZero() {
      //创建一个账户
      Account account = new Account();
      //期望getBalance 获得为0元的余额    
     assertEquals(0.0,account.getBalance());
   }
}

缺少getBalance()方法

代码语言:javascript

复制

package com.example;
public class Account {
   public double getBalance() {
     return 0;
   }
}

代码语言:javascript

复制

@Test
public void testDeposit(){
    // 创建一个账户对象
    Account account= new Account();
    // 给账户对象存入 500元
    account.deposit(500.00); 
     // 期望 getBalance方法返回500元余额
    assertEquals(500.0,account.getBalance()); 
 }

要创建 deposit 方法

代码语言:javascript

复制

package com.Account.TDD; 
public class Account {
    private double balance = 0.0;
    public double getBalance() {
        return this.balance;
    }
    public void deposit(double value) {
        this.balance += value;
    }
}

如果存入负数如何?

在调用 deposit 方法时,如果是负值,就抛出IllegalDepositException (非法存款值)异常

代码语言:javascript

复制

@Test
public void testDepositIllegalShouldThrowException(){
    Account account = new Account();
    //期待在调用deposit 方法为负值的时候抛出IlegalDepositException 异常
    assertThrows(IllegalDepositException.class,0->account.deposit(-500));    
    assertEquals(0.0,account.getBalance());// 抛出异常也不能让余额出现问题
}

除了要抛出异常,我们还需要保持余额正确

代码语言:javascript

复制

package com.Account.TDD;
 
public class Account {
private double balance = 0.0;
public double getBalance() {
    return this.balance;
}
public void deposit(double value) throws IllegalDepositException{
    if(value < 0.0) throw new IllegalDepositException();
    else this.balance += value;
    }
}

代码语言:javascript

复制

package com.Account.TDD;
public class IllegalDepositException extends Exception {
private static final long serialVersionUID = 1L;
IllegalDepositException() { super(); }
IllegalDepositException(String msg) { super(msg); }
}

加上处理异常

代码语言:javascript

复制

package com.Account.TDD;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class AccountTest{
    @Test
    public void testCreateAccountTheBalanceIsZero() {
       Account account = new Account();
       assertEquals(0.0,account.getBalance());
    }
    @Test
    public void testDeposit() throws IllegalDepositException {
        Account account = new Account();
        account.deposit(500);
        assertEquals(500.0,account.getBalance());
    }
 
    @Test
    public void testDepositIllegalShouldThrowException() {
        Account account = new Account();
        assertThrows(IllegalDepositException.class,()->account.deposit(-500));
        assertEquals(0.0,account.getBalance());    
    }
}

在取款时,除了负值要抛出异常,我们还要判断余额不足时如何处理

  1. 拒绝:抛出IlegalWithdrawException。
  2. 透支:直接减去,保留负值。
  3. 取出可用部分,清零 balance 值。

选择第1个方案

代码语言:javascript

复制

@Test
public void testWithdrawIfBalanceIsNegativeShouldThrowException() throws IllegalWithdrawException{
    Account account = new Account();
    assertThrows(IllegalWithdrawException.class,()->account.withdraw(500));
    assertEquals(0.0,account.getBalance());
}

代码语言:javascript

复制

public class IllegalWithdrawException  extends Exception{
    private static final long serialVersionUID = 1L;
    IllegalWithdrawException() { super(); }    
    IllegalWithdrawException(String msg) { super(msg); }
}

书写withdraw方法

代码语言:javascript

复制

public void withdraw(double v) throws IllegalWithdrawException{
    this.balance = 0.0;
    throw new IllegalWithdrawException();
 }

withdraw 方法的参数也不能是负值。此时如果用同样的异常IllegalWithdrawException处理“负值”和“余额不足”2种情况,这时可以采取以下2种设计。

  1. 修改 IllegalWithdrawException0的实现,使用不同的 message 信息进行区分。也就是说,虽然同样是 llegalWithdrawExceptionO,但具体内容不同。
  2. 新建一个异常,命名为IegalBalanceException 异常,用于处理余额不足的

使用2透支:直接减去,保留负值。

代码语言:javascript

复制

@Test
public void testWithdrawIfBalanceIsNegativeShouldThrowException() {
    Account account= new Account();
    assertThrows(llegalBalanceException.class, ()->account.withdraw(500));    
    assertEquals(0.0,account,getBalance());
}

产品代码中加入:

代码语言:javascript

复制

package com.Account.TDD;
public class IllegalWithdrawException  extends Exception{
private static final long serialVersionUID = 1L;
IllegalWithdrawException() { super(); }
IllegalWithdrawException(String msg) { super(msg); }
}

代码语言:javascript

复制

public void withdraw(double v) throws IllegalWithdrawException, lllegalBalanceException{
    if(v < 0.0) throw new IllegalWithdrawException();
    if (this.balance - v < 0) throw new lllegalBalanceException();
    else this.balance -= v;
}

修改测试并补充对取款为负值时进行测试的代码。

代码语言:javascript

复制

@Test
public void testDepositThenWithdraw()throws IllegalWithdrawException, lllegalBalanceException,IllegalDepositException {
Account account = new Account();
account.deposit(500);
account.withdraw(300);    
assertEquals(200.0,account.getBalance());
}
@Test
public void testWithdrawIfBalanceIsNegativeShouldThrowException() throws IllegalWithdrawException{
Account account = new Account();
        assertThrows(IllegalWithdrawException.class,()->account.withdraw(-500));
        assertEquals(0.0,account.getBalance());
    }

重构代码

问题

  1. 虽然能精确定义什么是非法的取钱和存钱,但非法的定义并不清晰。
  2. 同样地,非法余额的定义也不明确。
  • 当取值为负的时候,应该抛出 NegativeValueException。
  • 当余额为负的时候,应该抛出 NegativeBalanceException。

代码语言:javascript

复制

package com.Account.TDD;    
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class AccountTest{
    //测试创建帐户余额为零
    @Test
    public void testCreateAccountTheBalanceIsZero() {
       Account account = new Account();
       assertEquals(0.0,account.getBalance());
    }
  
    //测试存款
    @Test
    public void testDeposit() throws NegativeValueException {
        Account account = new Account();    
        account.deposit(500.00);
        assertEquals(500.0,account.getBalance());
    }
  
    //测试存款负值应抛出异常
    @Test
    public void testDepositNegativeValueShouldThrowException(){
        Account account= new Account();
        assertThrows(NegativeValueException.class, ()->account.deposit(-500));
        assertEquals(0.0,account.getBalance());
    }
 
    //测试提取负余额应抛出异常
    @Test
    public void testWithdrawNegativeBalanceShouldThrowException() {            
        Account account= new Account();
        assertThrows(NegativeBalanceException.class, ()->account.withdraw(500));
        assertEquals(0.0,account.getBalance());
    }
 
    //测试先存后取
    @Test
    public void testDepositThenWithdraw()throws NegativeValueException, NegativeBalanceException {
       Account account = new Account();
       account.deposit(500);
       account.withdraw(300);
       assertEquals(200.0,account.getBalance());
    }
 
     //测试取款负值应引发异常
    @Test
    public void testWithdrawNegativeValueShouldThrowException () {
        Account account = new Account();
        assertThrows(NegativeValueException.class, ()->account.withdraw(-500));
        assertEquals(0.0,account.getBalance());
    }
}

代码语言:javascript

复制

package com.Account.TDD;
 
public class Account {
    private double balance = 0.0;
    public double getBalance() {
        return this.balance;
    }
 
 
    public void deposit(double value) throws NegativeValueException {
        checkInputValue(value);
        this.balance += value;
    }
 
    public void withdraw(double value) throws NegativeValueException, NegativeBalanceException{
        checkInputValue(value);
        if (this.balance - value < 0) throw new NegativeBalanceException();
        else this.balance -= value;
    }
 
    private static void checkInputValue(double value) throws NegativeValueException {    
        if (value < 0.0) throw new NegativeValueException();
    }
}

实际Double -> BigDecimal

模拟对象

如Mock、Stub、Fake、Spy、虚拟服务等。

“三段论”

  1. 创建一个模拟对象或监视 (Spy) 一个已创建的对象
  2. 在执行真实方法前绑定方法运行结果。
  3. 验证结果或方法是否被执行。

Mock 对象不能替代集成测试。

创建账户的时候生成一个 ID。

代码语言:javascript

复制

@Test
public void verifyLoadAccountById () throws NegativeValueException {
    Account account =new Account(accountRepository);
    account.deposit(anyDouble);
    when(accountRepository.loadAccountByld(account.getld()).thenReturn(account);
    Account accountLoaded= accountRepository.loadAccountByld(account.getld());
    assertEquals(account.getId(),accountLoaded.getld);
    assertEquals(account.getBalance(),accountLoaded.getBalance());    
}
  1. 在创建账户的时候,需要保存数据库。
  2. 在存钱的时候,需要保存数据库。
  3. 在取钱的时候,需要保存数据库。
  4. 在抛出异常的时候,不保存数据库。

把数据库的操作对象“注入”进去,最好是使用构造函数的方式

代码语言:javascript

复制

@Test
public void verifyCreateAccountWillSaveToRepository () {
    Account account = new Account(new AccountRepository());
}
  1. 在测试之前,我们要先去实现 AccountRepository 类。
  2. 我们希望它是一个接口,而接口是不能直接 new 的。

可通过构造一个实现 AccountRepository 接口的对象进行“模拟”?

代码语言:javascript

复制

@Test
public void verifyCreateAccountWillSaveToRepository(){
    AccountRepository accountRepository= mock(AccountRepository.class);
    Account account = new Account(accountRepository);
    verify(accountRepository).save(account);
}

构造一个名为 AccountRepository 的接口

代码语言:javascript

复制

package com.example.account;    
public interface AccountRepository
    Boolean save(Account account);
}

建立构造函数

代码语言:javascript

复制

public Account(AccountRepository accountRepository) {
    accountRepository.save(this);
}

修改测试代码

代码语言:javascript

复制

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
 
class AccountTest{
    AccountRepository accountRepository= Mockito.mock(AccountRepository.class);
 
    //测试创建帐户余额为零
    @Test
    public void testCreateAccountTheBalanceIsZero() {
       Account account = new Account(accountRepository);
       assertEquals(0.0,account.getBalance());
    }    
 
    //测试存款
    @Test
    public void testDeposit() throws NegativeValueException {
        Account account = new Account(accountRepository);
        account.deposit(500.00);
        assertEquals(500.0,account.getBalance());
    }
 
    //测试存款负值应抛出异常
    @Test
    public void testDepositNegativeValueShouldThrowException(){
        Account account= new Account(accountRepository);
        assertThrows(NegativeValueException.class, ()->account.deposit(-500));
        assertEquals(0.0,account.getBalance());
    }
 
    //测试提取负余额应抛出异常
    @Test
    public void testWithdrawNegativeBalanceShouldThrowException() {        
        Account account= new Account(accountRepository);
        assertThrows(NegativeBalanceException.class, ()->account.withdraw(500));    
        assertEquals(0.0,account.getBalance());
    }
 
    //测试先存后取
    @Test
    public void testDepositThenWithdraw()throws NegativeValueException, NegativeBalanceException {
        Account account = new Account(accountRepository);
        account.deposit(500);
        account.withdraw(300);
        assertEquals(200.0,account.getBalance());
    }
 
    //测试取款负值应引发异常
    @Test
    public void testWithdrawNegativeValueShouldThrowException () {
        Account account = new Account(accountRepository);
        assertThrows(NegativeValueException.class, ()->account.withdraw(-500));
        assertEquals(0.0,account.getBalance());
    }
     
    @Test
    public void verifyCreateAccountWillSaveToRepository(){
    //AccountRepository accountRepository= Mockito.mock(AccountRepository.class);
       Account account = new Account(accountRepository);
       verify(accountRepository, times(1)).save(isA(account.getClass()));
   }
}

代码语言:javascript

复制

package com.Account.TDD;
public class Account {
    private final AccountRepository accountRepository;
    private double balance = 0.0;
    public Account(AccountRepository accountRepository) {
      this.accountRepository= accountRepository;
      this.accountRepository.save(this);    
    }
 
    public double getBalance(){
       return this.balance;
    }
 
    public void deposit(double value) throws NegativeValueException{
       checkInputValue(value);
       this .balance += value;
       this.accountRepository.save(this);
    }
 
    public void withdraw(double value) throws NegativeValueException, NegativeBalanceException{
        checkInputValue(value);
        if (this.balance -value < 0) throw new NegativeBalanceException();
        else this.balance -= value;
        this.accountRepository.save(this);
    }
 
    private static void checkInputValue(double value) throws NegativeValueException{    
        if (value <0.0) throw new NegativeValueException();
    }
}

重构

代码语言:javascript

复制

private void changeBalance(double value){
    this.balance += value;
    this.accountRepository.save(this);
}
 
public void deposit(double value) throws NegativeValueException{
   checkInputValue(value);
   changeBalance(value);
}
 
 
public void withdraw(double value) throws NegativeValueException, NegativeBalanceException{
    checkInputValue(value);
    if (this.balance -value < 0) throw new NegativeBalanceException();
    else changeBalance(-value);
}    

作为一名银行储户

我想要通过账户 I 查询我的储蓄账户

以便我能够继续在我的储蓄账户上存取款

首先,我们列举出不同的场景。

  1. 新建空账户,显示账户 ID。
  2. 在存钱后根据账户 ID 读取账户,余额应该为最后一次操作后的余额
  3. 在取钱后根据账户ID 读取账户,余额应该为最后一次操作后的余额。

对于1新建空账户,显示账户 ID。

代码语言:javascript

复制

@Test
public void verifyCreateAccountWillSaveToRepository (){
    Account account = new Account(accountRepository);
    verify(accountRepository).save(account);
    assertNotNull(account.getId());
}

还没有想清楚怎么实现 ID 对象之前,可以先使用 String 类型

代码语言:javascript

复制

public String getId() {
return "";
}

建立测试用例,运行失败

代码语言:javascript

复制

@Test    
public void verifyCreateTwoAccountsIdMustNotSame () {
    Account accountOne = new Account(accountRepository);
    Account accountTwo = new Account(accountRepository);
    assertNotEquals(accountOne.getId(), accountTwo.getId());
}

修改代码的实现。

代码语言:javascript

复制

public String getId(){
    return UUID.randomUUID().toString();
}

(2)在存钱后根据账户 ID 读取账户,余额应该为最后一次操作后的余额

代码语言:javascript

复制

@Test
public void verifyLoadAccount () throws NegativeValueException {    
    Account account = new Account(accountRepository);
    account.deposit(500.00);
    Account accountLoaded = accountRepository.loadAccountById(account.getId());
    assertEquals(account.getBalance(), accountLoaded.getBalance());
}

运行测试会抛出 NulPointerException,提示 account loaded 是空的对象,因此要构造一个对象。使用 any()让模拟对象的方法返回指定类型的任意对象。因为accoutRepository目前只是一个接口,没有任何实现,所以无法返回对象。不过,我们可以使用when()方创建一个对象。

代码语言:javascript

复制

@Test
public void verifyLoadAccountById () throws NegativeValueException {
    Account account= new Account(accountRepository);
    account.deposit(anyDouble());
    when(accountRepository.loadAccountById(account.getld())).thenReturn(account);
    Account account loaded = accountRepository.loadAccountByld(account.getId());
}

修改产品代码

代码语言:javascript

复制

public Account(AccountRepository accountRepository){
this.id=UUID.randomUUID().toString();
this.accountRepository= accountRepository;
this.accountRepository.save(this);
}
public String getId() {
return this.id;
}

采用自动化构建工具管理自动化测试任务

  1. Ant with Ivy(Ant)
  2. Maven
  3. Gradle

生成单元测试分析报告

3个主流的Java代码覆盖率统计工具

  1. Serenity BDD
  2. JCov
  3. JaCoCo

如果没有改动代码的需求,就不要增加单元测试

以下3 种场景就不需要进行单元测试。

  1. 留在系统中的未经动过的代码
  2. 过于简单的单元不需要测试,如某些 POJO类
  3. 第三方提供的库

代码覆盖率的意义

1.代码覆盖率与测试覆盖率的不同之处

代码覆盖率:覆盖代码百分率

测试覆盖率:覆盖需求百分率

插装

  1. 代码插装
  2. 运行时插装
  3. 中间代码插装。

2.不要被 100%的代码覆盖率欺骗

(1)100%的代码覆盖率不代表代码没有问题

(2)有些语句并没有需要覆盖的价值

有些语句不需要覆盖,如私有方法。我们需要坚持“一个实现类就有一个测试类”的法则,一个单元测试类至少应该对这个类的公共接口进行测试。

不应该和代码的实现有太耦合,代码耦合太过紧密,就会令人“厌烦”。当代码重构时单元测试就可能会因此无法再次运行

敏捷XP的专家Kent Beck也认可这一观点,测试 getter、setter 或其他简单的实现(如没有任何条件逻辑的实现)不会因此得到任何价值。

(3)100%的代码覆盖率会让人迷失目标。因此得到任何价值。

敏捷大师 Brian Marick 所述,设计初始测试套件来达到 100%的代码覆盖率是一个更糟糕的主意

Martin Fowler 曾在博客中写道:“我不时听到人们问代码覆盖率价值是什么,或者自豪地陈述他们的代码覆盖率水平。这种说法没有抓住问题的关键码覆盖率是发现代码库中未测试部分的有用工具,而代码覆盖率作为测试好坏的数字,几乎没有任何用处。”

没有断言的测试(Assertion Free Testing)

100%的目标设置会让人怀疑,那么代码覆盖率达到80%或90%以上即可。

更应该关注测试的充分性,而不是代码覆盖率

·很少有 Bug 会逃逸到生产环境

·很少会因为担心导致 Bug 而犹豫是否要更改代码。

代码覆盖率分析的价值是什么呢?它可以帮助发现代码哪些部分没有被测试,从而提高测试的充分性。

目录
相关文章
|
1天前
|
机器学习/深度学习 人工智能 测试技术
软件测试中的自动化测试实践与挑战
本文深入探讨了软件测试领域中的自动化测试,从基本概念到实际应用案例,揭示了自动化测试在提升软件开发效率和质量中的关键作用。同时,文章也分析了在实施自动化测试过程中面临的主要挑战,并提出了相应的解决策略。
12 5
|
2天前
|
Ubuntu jenkins 测试技术
软件测试中的自动化与持续集成实践
【9月更文挑战第15天】在软件开发的快节奏世界中,自动化测试和持续集成(CI)已成为确保质量和效率的关键策略。本文旨在揭示如何通过实施自动化测试框架和CI流程来优化开发周期,减少人为错误,并加快产品上市时间。我们将探讨一些实用的工具和技术,以及它们如何帮助团队实现更流畅、更可靠的软件发布。
|
3天前
|
监控 jenkins 测试技术
软件测试中的自动化测试策略与实践
本文将深入探讨自动化测试在软件开发中的重要性及其实施策略。我们将从自动化测试的基本概念入手,分析其在提高软件质量、缩短开发周期和降低维护成本方面的优势。通过具体案例,展示如何有效地规划和执行自动化测试,以及如何评估其效果。
13 1
|
4天前
|
Devops jenkins 测试技术
敏捷测试价值观、方法和实践读书笔记(10)
本文介绍了敏捷测试的延伸实践,重点讨论了持续集成(CI)和持续部署(CD)的概念与实践方法。持续集成强调频繁提交代码至主干并自动化构建测试,确保快速反馈和高质量代码。持续部署则进一步实现自动化部署,通过蓝绿部署、金丝雀发布等方式提升软件交付效率。此外,文章还探讨了持续反馈机制,如A/B测试和混沌工程,以及DevOps文化下的测试策略,强调测试在整个开发流程中的重要性。
11 0
敏捷测试价值观、方法和实践读书笔记(10)
|
4天前
|
XML 存储 API
敏捷测试价值观、方法和实践读书笔记(8)
本文介绍了API的基础知识,区分了Web Service和Web API的概念,详细阐述了Web Service中的SOAP服务和REST服务的特点及区别。同时,文章还探讨了如何在项目中进行API测试,包括API测试的类型和实施阶段,强调了API在现代软件开发中的重要性和优势。
6 0
敏捷测试价值观、方法和实践读书笔记(8)
|
1天前
|
敏捷开发 人工智能 测试技术
提升软件质量的关键——高效软件测试策略与实践
在软件开发过程中,测试是一个至关重要的环节。它不仅决定了产品的可靠性和用户体验,还直接影响到企业的声誉和市场竞争力。本文将详细探讨如何通过科学的测试策略和方法,确保软件质量达到高标准的要求。从测试流程的设计、自动化测试工具的应用,到持续集成和持续部署的实践,我们将全面解析如何在软件开发中实施有效的测试,以降低错误率,缩短上市时间,并提供优质的软件产品。
13 0
|
3天前
|
测试技术 持续交付
软件测试中的自动化测试实践与探索
在软件开发生命周期中,测试阶段是确保产品质量和稳定性的关键环节。随着技术的快速发展,自动化测试逐渐成为提升测试效率和覆盖率的重要手段。本文将探讨自动化测试的基本概念、工具选择、实施策略以及面临的挑战,旨在为读者提供关于如何在项目中有效应用自动化测试的指导性见解。
8 0
|
4天前
|
测试技术 UED 开发者
《敏捷测试价值观、方法与价值观》读书笔记(9)
本章节聚焦于非功能性测试,尤其深入探讨了可用性测试的重要性和实施方法。首先,阐述了可用性原则如简洁设计、一致性及高效性等,并强调用户而非开发者才是评判应用易用性的关键。接着介绍了可用性测试的不同技术和环境需求,包括卡片分类、结构化评估等方法,并讨论了测试实验室的具体配置。此外,详细说明了测试过程中的计划、执行、分析阶段,涵盖了从测试目标设定到测试结果优化的全流程。同时,还提供了测试参与者招募标准、测试材料准备及执行过程中注意事项的具体示例。最后,指导如何整合与分类测试结果,以及生成可用性测试报告的方法。
7 0
|
5天前
|
移动开发 JSON Java
Jmeter实现WebSocket协议的接口测试方法
WebSocket协议是HTML5的一种新协议,实现了浏览器与服务器之间的全双工通信。通过简单的握手动作,双方可直接传输数据。其优势包括极小的头部开销和服务器推送功能。使用JMeter进行WebSocket接口和性能测试时,需安装特定插件并配置相关参数,如服务器地址、端口号等,还可通过CSV文件实现参数化,以满足不同测试需求。
30 7
Jmeter实现WebSocket协议的接口测试方法
|
5天前
|
JSON 移动开发 监控
快速上手|HTTP 接口功能自动化测试
HTTP接口功能测试对于确保Web应用和H5应用的数据正确性至关重要。这类测试主要针对后台HTTP接口,通过构造不同参数输入值并获取JSON格式的输出结果来进行验证。HTTP协议基于TCP连接,包括请求与响应模式。请求由请求行、消息报头和请求正文组成,响应则包含状态行、消息报头及响应正文。常用的请求方法有GET、POST等,而响应状态码如2xx代表成功。测试过程使用Python语言和pycurl模块调用接口,并通过断言机制比对实际与预期结果,确保功能正确性。
24 3
快速上手|HTTP 接口功能自动化测试