《Java Testing with Spock》_3Spock功能

本文介绍了如何利用Spock框架对火灾警报软件和核反应堆控制系统进行测试。内容包括:火灾警报系统的各种情况测试,如单一和多个传感器触发时的响应;Spock中的Stubbing技术,通过指定方法调用的返回值;以及mocking和stubbing的应用,控制特定方法的调用次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3.1火灾警报软件的例子

1.png
处理单元连接到多个火灾传感器,并连续轮询它们以获取异常读数。发现火灾时,警报声响起。如果火势开始蔓延并且触发了另一个检测器,则会自动呼叫消防队。具体需求如下:

  • 如果所有传感器均报告无异常,则表明系统正常,无需采取任何措施。
  • 如果触发了一个传感器,则会发出警报声(但由于吸烟者粗心大意,无法抗拒香烟,这可能是误报)。
  • 如果触发了多个传感器,则会呼叫消防队(因为火势蔓延到一个以上的房间)。
//实现监视的主类
public class FireEarlyWarning {
	private int sensorsDetectingFire = 0;
	
	//方法称为每秒钟由传感器软件
    public void feedData(int triggeredFireSensors)
    {
		sensorsDetectingFire = triggeredFireSensors;
    }
    //状态报告获取方法
    public WarningStatus getCurrentStatus() 
    {
       return new WarningStatus(sensorsDetectingFire > 0, sensorsDetectingFire > 1);
    }
}
//状态报告内容(状态类)
public class WarningStatus {
	private final boolean alarmActive;
	private final boolean fireDepartmentNotified;
	
	public WarningStatus(boolean alarmActive,boolean fireDepartmentNotified)
	{
		this.alarmActive = alarmActive;
		this.fireDepartmentNotified = fireDepartmentNotified;
	}
	
    //若为true,警报就会响起。
	public boolean isAlarmActive() {
		return alarmActive;
	}
    //若为true,就呼叫消防队。
	public boolean isFireDepartmentNotified() {
		return fireDepartmentNotified;
	}
}

3.5 火警系统的全部Spock测试

class FireSensorSpec extends spock.lang.Specification{
    //清楚地解释这个测试的作用
    def "如果所有传感器都不活动,那么一切都没问题"() {
        
        given: "所有的火灾传感器都关闭了" 
        FireEarlyWarning fireEarlyWarning = new FireEarlyWarning()
        int triggeredSensors = 0
        
        when: "我们询问消防状况" 
        fireEarlyWarning.feedData(triggeredSensors)
        WarningStatus status = fireEarlyWarning.getCurrentStatus()
        
        then: "不应触发警报/通知" 
        !status.alarmActive 
        !status.fireDepartmentNotified
    }

    def "如果一个传感器是启动的,警报应该发出预防措施"() { 
        given: "只有一个火情传感器是启动的"
        FireEarlyWarning fireEarlyWarning = new FireEarlyWarning()
        int triggeredSensors = 1

        when: "我们询问消防状况"
        fireEarlyWarning.feedData(triggeredSensors)
        WarningStatus status = fireEarlyWarning.getCurrentStatus()

        then: "只应触发警报"
        status.alarmActive
        !status.fireDepartmentNotified
    }
     def "如果不止一个传感器处于启动状态,且我们着火了"() {
        given: "两个火灾传感器已经启动"
        FireEarlyWarning fireEarlyWarning = new FireEarlyWarning()
        int triggeredSensors = 2

        when: "我们询问消防状况"
        fireEarlyWarning.feedData(triggeredSensors)
        WarningStatus status = fireEarlyWarning.getCurrentStatus()

        then: "警报被触发,消防部门被通知"
        status.alarmActive
        status.fireDepartmentNotified
    }
}

3.7核反应堆测试

接下来上升到更复杂的场景—核反应堆的测试,涉及到多个输入集。
具体功能如下:
1.png
1.png

class NuclearReactorSpec extends spock.lang.Specification{
	
    //测试描述需通俗易懂,人类可读
    def "全面测试所有核反应场景"() {
		given: "一个核反应和传感器的数据"
			NuclearReactorMonitor nuclearReactorMonitor =new NuclearReactorMonitor()
        
        //输入
        when: "当我们检查传感器数据"
			nuclearReactorMonitor.feedFireSensorData(fireSensors)
			nuclearReactorMonitor.feedRadiationSensorData(radiation)
			nuclearReactorMonitor.feedPressureInBar(pressure)
			NuclearReactorStatus status = nuclearReactorMonitor.getCurrentStatus()
		
        //输出
        then: "我们根据安全准则行动"
			status.alarmActive == alarm
			status.shutDownNeeded == shutDown
			status.evacuationMinutes == evacuation
		
        //参数的来源
        where: "可能的核事故有:"
       	 	//输入和输出的定义
			pressure | fireSensors | radiation || alarm | shutDown | evacuation
        	//所有场景的表格表示
			150 	 | 0           | []        || false | false    | -1
			150      | 1           | []        || true  | false    | -1
			150      | 3           | []        || true  | true     | -1
			150      | 0| [110.4f ,0.3f, 0.0f] || true  | true     | 1
			150      | 0| [45.3f ,10.3f, 47.7f]|| false | false    | -1
			155      | 0| [0.0f ,0.0f, 0.0f]   || true  | false    | -1
			170      | 0| [0.0f ,0.0f, 0.0f]   || true  | true     | 3
			180      | 0| [110.4f ,0.3f, 0.0f] || true  | true     | 1
			500      | 0| [110.4f ,300f, 0.0f] || true  | true     | 1
			30       | 0|[110.4f ,1000f, 0.0f] || true  | true     | 1
			155      | 4| [0.0f ,0.0f, 0.0f]   || true  | true     | -1
			170      | 1| [45.3f ,10.3f, 47.7f]|| true  | true     | 3
	}
}

3.9 在Spock中使用Stubbing

class CoolantSensorSpec extends spock.lang.Specification{
	
    def "如果当前温差在限制范围内,则一切正常"() {
		given: "读取的温度在限制范围内"
        	//预设温度
			TemperatureReadings prev = new TemperatureReadings(sensor1Data:20, 				      								sensor2Data:40,sensor3Data:80)
			TemperatureReadings current = new TemperatureReadings(sensor1Data:30, 	  											  sensor2Data:45,sensor3Data:73);
			//dummy接口实现
        	TemperatureReader reader = Stub(TemperatureReader)
		
        	//dummy接口返回预设温度
        	reader.getCurrentReadings() >>> [prev, current]

        	//被测试的类注入dummy接口
        	TemperatureMonitor monitor = new TemperatureMonitor(reader)

        //被测试的类调用dummy接口
        when: "我们查询现在的温度"
			monitor.readSensor()
			monitor.readSensor()

        //两次调用之后的结果
        then: "一切正常"
			monitor.isTemperatureNormal()
	}
    
	def "如果当前温差大于20度,则警报响起"() {
		given: "读取的温度不在限制范围内"
			TemperatureReadings prev = new TemperatureReadings(sensor1Data:20,  			  										 sensor2Data:40,sensor3Data:80)
			TemperatureReadings current = new TemperatureReadings(sensor1Data:30,
 										  sensor2Data:10,sensor3Data:73);
			//Stub的调用
        	TemperatureReader reader = Stub(TemperatureReader)
        	reader.getCurrentReadings() >>> [prev,current]
			TemperatureMonitor monitor = new TemperatureMonitor(reader)

        when: "我们查询现在的温度"
			monitor.readSensor()
			monitor.readSensor()

        then: "警报应该响起"
			!monitor.isTemperatureNormal()
	}
}

reader.getCurrentReadings() >>> [prev, current]
第一次调用getCurrentReadings方法返回prev,第二次调用返回current。

3.11使用Spock的mocking和stubbing

def "如果当前温差大于20度,则警报响起"() {
	given: "读取的温度不在限制范围内"
		TemperatureReadings prev = new TemperatureReadings(sensor1Data:20,   		   		                                    sensor2Data:40,sensor3Data:80)
		TemperatureReadings current = new TemperatureReadings(sensor1Data:30,
                                      sensor2Data:10,sensor3Data:73);
	
    	//为接口创建Stub
    	TemperatureReader reader = Stub(TemperatureReader)
	
    	reader.getCurrentReadings() >>> [prev, current]
	
    	//为一个具体的类创建mock
    	ReactorControl control = Mock(ReactorControl)
		//被测试的类注入mock和stub
    	ImprovedTemperatureMonitor monitor = new
            ImprovedTemperatureMonitor(reader,control)

    //后续场景调用mock方法
    when: "我们查询现在的温度"
		monitor.readSensor()
		monitor.readSensor()

    then: "警报应该响起"
		//验证mock的调用
    	0 * control.shutdownReactor()
		1 * control.activateAlarm()
}
def "如果当前温差大于50度,则核反应应该停止"(){
	given: "读取的温度不在限制范围内"
		TemperatureReadings prev = new TemperatureReadings(sensor1Data:20,
                                                       sensor2Data:40,sensor3Data:80)
		TemperatureReadings current = new TemperatureReadings(sensor1Data:30,
                                                         sensor2Data:10,sensor3Data:160);
		TemperatureReader reader = Stub(TemperatureReader)

    	reader.getCurrentReadings() >>> [prev, current]

    	ReactorControl control = Mock(ReactorControl)
		ImprovedTemperatureMonitor monitor = new
            ImprovedTemperatureMonitor(reader,control)

    when: "我们查询现在的温度"
		monitor.readSensor()
		monitor.readSensor()

    then: "警报应该响起,并且核反应应该停止"
		1 * control.shutdownReactor()
		1 * control.activateAlarm()
}

0 * control.shutdownReactor()
1 * control.activateAlarm()
表示control的shutdownReactor方法没有被调用,activateAlarm被调用一次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值