Initial commit
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file was generated by the Gradle 'init' task.
|
||||
*
|
||||
* This generated file contains a sample Java application project to get you started.
|
||||
* For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
|
||||
* User Manual available at https://docs.gradle.org/7.2/userguide/building_java_projects.html
|
||||
*/
|
||||
|
||||
plugins {
|
||||
// Apply the application plugin to add support for building a CLI application in Java.
|
||||
id 'application'
|
||||
}
|
||||
|
||||
repositories {
|
||||
// Use Maven Central for resolving dependencies.
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Junit 5 dependencies
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.+'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.+'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.+'
|
||||
// Mockito dependencies
|
||||
testImplementation 'org.mockito:mockito-core:4.3.+'
|
||||
}
|
||||
|
||||
// Test task configuration
|
||||
test {
|
||||
// Use JUnit platform for unit tests
|
||||
useJUnitPlatform()
|
||||
// Output results of individual tests
|
||||
testLogging {
|
||||
events "PASSED", "SKIPPED", "FAILED"
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
// Define the main class for the application.
|
||||
mainClass = 'ch.zhaw.prog2.heartbeat.Heart'
|
||||
}
|
||||
|
||||
// Java plugin configuration
|
||||
java {
|
||||
// By default the Java version of the gradle process is used as source/target version.
|
||||
// This can be overridden, to ensure a specific version. Enable only if required.
|
||||
// sourceCompatibility = JavaVersion.VERSION_17 // ensure Java source code compatibility
|
||||
// targetCompatibility = JavaVersion.VERSION_17 // version of the created byte-code
|
||||
|
||||
// Java compiler specific options
|
||||
compileJava {
|
||||
// source files should be UTF-8 encoded
|
||||
options.encoding = 'UTF-8'
|
||||
// for more options see https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.CompileOptions.html
|
||||
}
|
||||
}
|
||||
|
||||
task autograding(type: Test) {
|
||||
description = "Runs specific tests for autograding"
|
||||
group = 'verification'
|
||||
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events "PASSED", "SKIPPED", "FAILED"
|
||||
}
|
||||
filter {
|
||||
failOnNoMatchingTests true
|
||||
includeTestsMatching "PacemakerTest"
|
||||
includeTestsMatching "HeartTest.testExecuteHartBeatErrorBehaviourWithStubbing"
|
||||
includeTestsMatching "HeartTest.testValvesBehavior"
|
||||
includeTestsMatching "HeartTest.testDiastoleException"
|
||||
includeTestsMatching "HeartTest.testSystoleException"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package ch.zhaw.prog2.heartbeat;
|
||||
|
||||
import ch.zhaw.prog2.heartbeat.parts.*;
|
||||
|
||||
/**
|
||||
* Represents one half of a heart.
|
||||
*
|
||||
* @author wahl, muon
|
||||
*/
|
||||
public class Half {
|
||||
|
||||
public enum Side {LEFT, RIGHT}
|
||||
|
||||
private Atrium atrium; // "Vorhof"
|
||||
private Ventricle ventricle; // "Herzkammer"
|
||||
private AtrioventricularValve atrioventricularValve; // "Segelklappe"
|
||||
private SemilunarValve semilunarValve; // "Taschenklappe"
|
||||
private final Side side;
|
||||
|
||||
public Half(Side side) {
|
||||
this.side = side;
|
||||
atrium = new Atrium(side);
|
||||
ventricle = new Ventricle(side);
|
||||
atrioventricularValve = new AtrioventricularValve(side);
|
||||
semilunarValve = new SemilunarValve(side);
|
||||
}
|
||||
|
||||
public void initializeState(Heart.State state) {
|
||||
atrioventricularValve.initializeState(state);
|
||||
semilunarValve.initializeState(state);
|
||||
}
|
||||
|
||||
public AtrioventricularValve getAtrioventricularValve() {
|
||||
return atrioventricularValve;
|
||||
}
|
||||
|
||||
public SemilunarValve getSemilunarValve() {
|
||||
return semilunarValve;
|
||||
}
|
||||
|
||||
public boolean isAtrioventricularValveOpen() {
|
||||
return atrioventricularValve.isOpen();
|
||||
}
|
||||
|
||||
public boolean isSemilunarValveOpen() {
|
||||
return semilunarValve.isOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the valve
|
||||
*
|
||||
* @throws IllegalStateException when valve is already closed
|
||||
*/
|
||||
public void closeSemilunarValve() throws Valve.IllegalValveStateException {
|
||||
semilunarValve.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the valve
|
||||
*
|
||||
* @throws Valve.IllegalValveStateException when the valve is already open
|
||||
*/
|
||||
public void openSemilunarValve() throws Valve.IllegalValveStateException {
|
||||
semilunarValve.open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the valve
|
||||
*
|
||||
* @throws Valve.IllegalValveStateException when valve is already closed
|
||||
*/
|
||||
public void closeAtrioventricularValve() throws Valve.IllegalValveStateException {
|
||||
atrioventricularValve.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the valve
|
||||
*
|
||||
* @throws Valve.IllegalValveStateException when the valve is already open
|
||||
*/
|
||||
public void openAtrioventricularValve() throws Valve.IllegalValveStateException {
|
||||
atrioventricularValve.open();
|
||||
}
|
||||
|
||||
public void contractVentricle() {
|
||||
ventricle.contract();
|
||||
}
|
||||
|
||||
public void relaxVentricle() {
|
||||
ventricle.relax();
|
||||
}
|
||||
|
||||
public void contractAtrium() {
|
||||
atrium.contract();
|
||||
}
|
||||
|
||||
public void relaxAtrium() {
|
||||
atrium.relax();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package ch.zhaw.prog2.heartbeat;
|
||||
|
||||
import ch.zhaw.prog2.heartbeat.parts.Valve;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class represents a human heart (https://en.wikipedia.org/wiki/Heart).
|
||||
* <p>
|
||||
* <p>
|
||||
* Example tests: before Chamber::contract() is called, the atrioventricular
|
||||
* valves must be closed.
|
||||
*
|
||||
* @author wahl
|
||||
*/
|
||||
public class Heart {
|
||||
private final static int AVERAGE_HEART_RATE = 60;
|
||||
|
||||
/**
|
||||
* Encodes the states of the heart
|
||||
*
|
||||
* @author wahl
|
||||
*/
|
||||
public enum State {
|
||||
SYSTOLE, DIASTOLE
|
||||
}
|
||||
|
||||
private List<Half> halves;
|
||||
private State state;
|
||||
private int heartRate;
|
||||
|
||||
/**
|
||||
* The default constructor calls the more specific constructor with a new left
|
||||
* and right half.
|
||||
*/
|
||||
public Heart() {
|
||||
this(new Half(Half.Side.LEFT), new Half(Half.Side.RIGHT));
|
||||
}
|
||||
|
||||
/**
|
||||
* The default constructor adds the left and the right half that are specified
|
||||
* as parameters and sets the start state to diastole.
|
||||
*/
|
||||
public Heart(Half leftHalf, Half rightHalf) {
|
||||
state = State.DIASTOLE;
|
||||
heartRate = AVERAGE_HEART_RATE;
|
||||
halves = new ArrayList<>();
|
||||
halves.add(leftHalf);
|
||||
halves.add(rightHalf);
|
||||
initalizeState();
|
||||
}
|
||||
|
||||
public void initalizeState(){
|
||||
for(Half half : halves){
|
||||
half.initializeState(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a sequence of diastole and systole, beginning with the current
|
||||
* state.
|
||||
*
|
||||
* @throws HeartBeatDysfunctionException when Valve.IllegalValveStateException or
|
||||
* InvalidValvePositionException occurs during diastole or systole
|
||||
* TODO Implement a pause mechanism based on the current heart rate
|
||||
*/
|
||||
public void executeHeartBeat() throws HeartBeatDysfunctionException {
|
||||
try {
|
||||
if (state.equals(State.DIASTOLE)) {
|
||||
executeDiastole();
|
||||
executeSystole();
|
||||
} else {
|
||||
executeSystole();
|
||||
executeDiastole();
|
||||
}
|
||||
} catch (Valve.IllegalValveStateException | InvalidValvePositionException e) {
|
||||
System.out.println(e.getMessage());
|
||||
throw new HeartBeatDysfunctionException("Heart is in an invalid state!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the diastole phase of the heart.
|
||||
*
|
||||
* @throws Valve.IllegalValveStateException when one of the valves has an illegal State
|
||||
*/
|
||||
public void executeDiastole() throws Valve.IllegalValveStateException {
|
||||
for (Half half : halves) {
|
||||
half.closeSemilunarValve();
|
||||
half.openAtrioventricularValve();
|
||||
half.relaxAtrium();
|
||||
half.relaxVentricle();
|
||||
}
|
||||
state = State.SYSTOLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the systole phase of the heart.
|
||||
*
|
||||
* @throws Valve.IllegalValveStateException when one of the valves has an illegal State
|
||||
*/
|
||||
public void executeSystole() throws Valve.IllegalValveStateException {
|
||||
for (Half half : halves) {
|
||||
half.openSemilunarValve();
|
||||
half.closeAtrioventricularValve();
|
||||
half.contractAtrium();
|
||||
half.contractVentricle();
|
||||
}
|
||||
state = State.DIASTOLE;
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(State state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the heart rate to the parameter value in Hz.
|
||||
* Rate has to be inside range >30 && <220
|
||||
* If rate is outside of range method rejects the change
|
||||
* and returns false.
|
||||
*
|
||||
* @param frequencyInHz which should be applied to the heart
|
||||
* @return true if the heart rate could be set, false otherwise
|
||||
*/
|
||||
public boolean setHeartRate(int frequencyInHz) {
|
||||
if (frequencyInHz < 30 || frequencyInHz > 220) {
|
||||
return false;
|
||||
}
|
||||
this.heartRate = frequencyInHz;
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getHeartRate() {
|
||||
return heartRate;
|
||||
}
|
||||
|
||||
public List<Half> getHalves() {
|
||||
return halves;
|
||||
}
|
||||
|
||||
public static class HeartBeatDysfunctionException extends Exception {
|
||||
public HeartBeatDysfunctionException() {
|
||||
}
|
||||
|
||||
public HeartBeatDysfunctionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public HeartBeatDysfunctionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main method instantiates a heart object and executes a single heartbeat.
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args) throws HeartBeatDysfunctionException {
|
||||
Heart heart = new Heart();
|
||||
heart.executeHeartBeat();
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package ch.zhaw.prog2.heartbeat;
|
||||
|
||||
public class InvalidValvePositionException extends RuntimeException {
|
||||
|
||||
public InvalidValvePositionException (String message) {
|
||||
super (message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package ch.zhaw.prog2.heartbeat;
|
||||
/**
|
||||
* This class represents a pacemaker for a human hart.
|
||||
*
|
||||
* This is an incomplete implementation, reduced to the essentials for the content of the lab.
|
||||
* There would be a lot more code needed to implement a pacemaker, but this is not part of this lab :-)
|
||||
*
|
||||
* @author muon
|
||||
*
|
||||
*/
|
||||
public class Pacemaker {
|
||||
Heart heart;
|
||||
|
||||
/**
|
||||
* Constructor of Pacemaker
|
||||
* @param heart
|
||||
*/
|
||||
public Pacemaker(Heart heart) {
|
||||
this.heart = heart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the heart rate to the parameter value in Hz.
|
||||
* Rate has to be inside range >30 && <220
|
||||
* If frequency can be applied on heart, the current frequency will be returned.
|
||||
* If heart does reject the change the method throws an exception.
|
||||
*
|
||||
* @param frequencyInHz needs to be applied on heart
|
||||
* @return current frequency of the heart
|
||||
* @throws IllegalArgumentException when heart does reject the change
|
||||
*/
|
||||
public int setHeartRate(int frequencyInHz) {
|
||||
if(!heart.setHeartRate(frequencyInHz)){
|
||||
throw new IllegalArgumentException("Frequency could not be set.");
|
||||
}
|
||||
return heart.getHeartRate();
|
||||
}
|
||||
|
||||
/*
|
||||
...
|
||||
There would be a lot more code needed to implement a pacemaker, but this is not part of this lab :-)
|
||||
...
|
||||
*/
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package ch.zhaw.prog2.heartbeat.parts;
|
||||
|
||||
import ch.zhaw.prog2.heartbeat.Half.Side;
|
||||
import ch.zhaw.prog2.heartbeat.Heart;
|
||||
|
||||
public class AtrioventricularValve extends Valve {
|
||||
|
||||
public AtrioventricularValve(Side side) {
|
||||
super (side);
|
||||
}
|
||||
|
||||
public void initializeState(Heart.State state) {
|
||||
if(Heart.State.DIASTOLE.equals(state)){
|
||||
setOpen(false);
|
||||
}else{
|
||||
setOpen(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package ch.zhaw.prog2.heartbeat.parts;
|
||||
|
||||
import ch.zhaw.prog2.heartbeat.Half.Side;
|
||||
|
||||
public class Atrium {
|
||||
private Side side;
|
||||
|
||||
public Atrium (Side side) {
|
||||
this.side = side;
|
||||
}
|
||||
|
||||
public void contract() {
|
||||
System.out.println("Atrium "+ side + " is contracting.");
|
||||
}
|
||||
|
||||
public void relax() {
|
||||
System.out.println("Atrium "+ side + " is relaxing.");
|
||||
}
|
||||
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package ch.zhaw.prog2.heartbeat.parts;
|
||||
|
||||
import ch.zhaw.prog2.heartbeat.Half.Side;
|
||||
import ch.zhaw.prog2.heartbeat.Heart;
|
||||
|
||||
public class SemilunarValve extends Valve {
|
||||
|
||||
public SemilunarValve(Side side) {
|
||||
super (side);
|
||||
}
|
||||
|
||||
public void initializeState(Heart.State state) {
|
||||
if(Heart.State.DIASTOLE.equals(state)){
|
||||
setOpen(true);
|
||||
}else{
|
||||
setOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package ch.zhaw.prog2.heartbeat.parts;
|
||||
|
||||
import ch.zhaw.prog2.heartbeat.Half.Side;
|
||||
|
||||
public abstract class Valve {
|
||||
|
||||
private boolean open;
|
||||
private Side side;
|
||||
|
||||
public Valve(Side side) {
|
||||
this.side = side;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the valve
|
||||
*
|
||||
* @throws IllegalValveStateException when the valve is already open
|
||||
*/
|
||||
public void open() throws IllegalValveStateException {
|
||||
if (open) {
|
||||
throw new IllegalValveStateException(this.getClass().getSimpleName() + " " + side + " valve is already open.");
|
||||
}
|
||||
System.out.println(this.getClass().getSimpleName() + " " + side + " is opening.");
|
||||
open = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the valve
|
||||
*
|
||||
* @throws IllegalValveStateException when valve is already closed
|
||||
*/
|
||||
public void close() throws IllegalValveStateException {
|
||||
if (!open) {
|
||||
throw new IllegalValveStateException(this.getClass().getSimpleName() + " " + side + " valve is already closed.");
|
||||
}
|
||||
System.out.println(this.getClass().getSimpleName() + " " + side + " is closing.");
|
||||
open = false;
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
return open;
|
||||
}
|
||||
|
||||
protected void setOpen(Boolean open) {
|
||||
this.open = open;
|
||||
}
|
||||
|
||||
public static class IllegalValveStateException extends Exception {
|
||||
|
||||
public IllegalValveStateException() {
|
||||
}
|
||||
|
||||
public IllegalValveStateException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public IllegalValveStateException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package ch.zhaw.prog2.heartbeat.parts;
|
||||
|
||||
import ch.zhaw.prog2.heartbeat.Half.Side;
|
||||
|
||||
public class Ventricle {
|
||||
private Side side;
|
||||
|
||||
public Ventricle (Side side) {
|
||||
this.side = side;
|
||||
}
|
||||
|
||||
public void contract() {
|
||||
System.out.println("Chamber "+ side + " is contracting.");
|
||||
}
|
||||
|
||||
public void relax() {
|
||||
System.out.println("Chamber "+ side + " is relaxing.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Test Class for Heart
|
||||
*/
|
||||
package ch.zhaw.prog2.heartbeat;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import ch.zhaw.prog2.heartbeat.parts.Valve;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import ch.zhaw.prog2.heartbeat.Heart.State;
|
||||
|
||||
class HeartTest {
|
||||
/**
|
||||
* This is a very simple test to check if Junit and Mockito are properly set up.
|
||||
*/
|
||||
@Test
|
||||
void testTheTest() {
|
||||
Heart classUnderTest = new Heart();
|
||||
assertNotNull(classUnderTest.getState(), "The heart must have a state.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a single heartbeat
|
||||
*/
|
||||
@Test
|
||||
void testHeartBeat() throws Heart.HeartBeatDysfunctionException {
|
||||
Heart heart = new Heart();
|
||||
State startState = heart.getState();
|
||||
|
||||
heart.executeHeartBeat();
|
||||
|
||||
// after one heartbeat, the heart must be in the same state as before
|
||||
assertEquals(startState, heart.getState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the valves are open or closed depending on the status of the heart
|
||||
*/
|
||||
@Test
|
||||
void testValveStatus() throws Heart.HeartBeatDysfunctionException {
|
||||
Heart heart = new Heart();
|
||||
|
||||
heart.executeHeartBeat();
|
||||
|
||||
State state = heart.getState();
|
||||
|
||||
if (state.equals(Heart.State.DIASTOLE)) {
|
||||
for (Half half : heart.getHalves()) {
|
||||
assertFalse(half.isAtrioventricularValveOpen());
|
||||
assertTrue(half.isSemilunarValveOpen());
|
||||
}
|
||||
} else if ((state.equals(Heart.State.SYSTOLE))) {
|
||||
for (Half half : heart.getHalves()) {
|
||||
assertTrue(half.isAtrioventricularValveOpen());
|
||||
assertFalse(half.isSemilunarValveOpen());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the hart throws the appropriate Exception, when malfunction was detected during hartBeat
|
||||
*/
|
||||
@Test
|
||||
void testExecuteHeartBeatErrorBehaviour() {
|
||||
Heart heart = new Heart();
|
||||
// prepare error situation due to wrong initialization
|
||||
heart.setState(State.SYSTOLE);
|
||||
|
||||
assertThrows(Heart.HeartBeatDysfunctionException .class, // verification using lambda
|
||||
() -> heart.executeHeartBeat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the hart throws the appropriate Exception, when malfunction was detected during hartBeat
|
||||
* with exception Stubbing
|
||||
*/
|
||||
@Disabled
|
||||
void testExecuteHartBeatErrorBehaviourWithStubbing() throws Valve.IllegalValveStateException {
|
||||
//TODO implement and replace the annotation @Disabled by @Test
|
||||
fail();
|
||||
}
|
||||
|
||||
/**
|
||||
* We test if Heart::executeHeartbeat() sends the right signals to both of its
|
||||
* halves.
|
||||
*
|
||||
* When Half::contractVentricle() is called, Half::closeAtrioventricularValve()
|
||||
* and Half::openSemilunarValve() must have been called earlier.
|
||||
*
|
||||
*/
|
||||
@Disabled
|
||||
void testValvesBehavior() {
|
||||
//TODO implementand replace the annotation @Disabled by @Test
|
||||
fail();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is code used for the lecture slide.
|
||||
*/
|
||||
@Test
|
||||
void testForSlide() throws Valve.IllegalValveStateException {
|
||||
Half mockedHalf = mock(Half.class);
|
||||
Heart heart = new Heart(mockedHalf, new Half(Half.Side.RIGHT));
|
||||
heart.setState(State.SYSTOLE);
|
||||
heart.initalizeState();
|
||||
|
||||
when(mockedHalf.isAtrioventricularValveOpen()).thenReturn(false);
|
||||
when(mockedHalf.isSemilunarValveOpen()).thenReturn(true);
|
||||
heart.executeSystole();
|
||||
|
||||
verify(mockedHalf).contractVentricle();
|
||||
verify(mockedHalf, times(1)).contractAtrium();
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package ch.zhaw.prog2.heartbeat;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/*
|
||||
* Test Class for Pacemaker
|
||||
*/
|
||||
public class PacemakerTest {
|
||||
|
||||
/**
|
||||
* Test if setHeartRate does throw correct exception when rate is rejected (because frequency is out of range)
|
||||
*/
|
||||
@Disabled
|
||||
void testSetHeartRateRejectsFrequenciesOutOfRange() {
|
||||
//TODO implement and replace the annotation @Disabled by @Test
|
||||
fail();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test if setHeartRate does correctly set the rate when frequency is inside range
|
||||
*/
|
||||
@Disabled
|
||||
void testSetHeartRateAppliesFrequenciesInsideRange() {
|
||||
//TODO implement and replace the annotation @Disabled by @Test
|
||||
fail();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
:source-highlighter: coderay
|
||||
:icons: font
|
||||
:experimental:
|
||||
:!sectnums:
|
||||
:imagesdir: ../images/
|
||||
:handout: ./code/
|
||||
|
||||
:logo: IT.PROG2 -
|
||||
ifdef::backend-html5[]
|
||||
:logo: image:PROG2-300x300.png[IT.PROG2,100,100,role=right,fit=none,position=top right]
|
||||
endif::[]
|
||||
ifdef::backend-pdf[]
|
||||
:logo:
|
||||
endif::[]
|
||||
ifdef::env-github[]
|
||||
:tip-caption: :bulb:
|
||||
:note-caption: :information_source:
|
||||
:important-caption: :heavy_exclamation_mark:
|
||||
:caution-caption: :fire:
|
||||
:warning-caption: :warning:
|
||||
endif::[]
|
||||
|
||||
= {logo} Lösungen zum Praktikum Mock-Testing
|
||||
|
||||
:sectnums:
|
||||
:sectnumlevels: 2
|
||||
// Beginn des Aufgabenblocks
|
||||
|
||||
== Herz
|
||||
|
||||
****
|
||||
Funktionserläuterung zum Herzschlag finden Sie in der Aufgabenstellung
|
||||
****
|
||||
|
||||
== Aufgaben
|
||||
|
||||
=== Einführung in Mockito
|
||||
[loweralpha]
|
||||
. Studieren Sie die Testmethoden `HeartTest::testValveStatus()` und `HeartTest::testExecuteHartBeatErrorBehaviour()`.
|
||||
Dort wird *ein Teil* des Verhaltens des Herzens getestet.
|
||||
|
||||
. Implementieren Sie die Methoden `Heart::executeDiastole()`, `Heart::executeSystole()` und `Heart::executeHeartBeat()` sodass die bestehenden Tests durchlaufen und das oben beschriebene Verhalten des Herzens modelliert wird.
|
||||
|
||||
. (optional) Ein echtes Herz hat eine Schlagfrequenz.
|
||||
Implementieren Sie, dass das Herz nach jeder Systole pausiert.
|
||||
|
||||
****
|
||||
Siehe Musterlösung: `Heart.executeDiastole()`, `Heart.executeSystole()` und `Heart.executeHeartBeat()`
|
||||
****
|
||||
|
||||
|
||||
=== Fragen zu Testing [TU]
|
||||
[loweralpha]
|
||||
. Testing kann in zwei unterschiedliche Strategien aufgeteilt werden.
|
||||
Zum einen gibt es White-Box-Testing und zum zweiten Black-Box-Testing.
|
||||
Was für Java Libraries gibt es, um diese zwei Strategien zu testen?
|
||||
Wann wenden Sie welche Strategie an?
|
||||
+
|
||||
****
|
||||
* JUnit ist ein Framework für Unit-Tests (auch Modul-Tests genannt).
|
||||
Es wird sowohl für White-Box-Testing als auch für Black-Box-Testing angewendet.
|
||||
|
||||
* Für White-Box-Testing eignet sich Mockito, weil damit die Abläufe innerhalb einer Klasse getestet werden können (behavior testing).
|
||||
|
||||
* Black-Box-Testing trifft keine Annahmen darüber, *wie* eine Klasse oder Methode eine Aufgabe erledigt.
|
||||
Es wird lediglich überprüft, ob auf eine bestimmte Eingabe die erwartete Ausgabe erfolgt.
|
||||
|
||||
* Nichttriviale Methoden sollten immer mindestens mit Black-Box-Testing getestet werden.
|
||||
Diese Tests werden am besten von einer anderen Person geschrieben als von der Programmierer:in.
|
||||
|
||||
* Für White-Box-Testing werden Informationen benötigt, *wie* eine Klasse oder Methode eine Aufgabe erledigt.
|
||||
****
|
||||
|
||||
. Das Erstellen von guten automatisierten Unit-Tests kann manchmal schwierig umzusetzen sein.
|
||||
Was ist der Hauptgrund dafür? Wie können Sie dieses Problem entschärfen?
|
||||
+
|
||||
****
|
||||
* Das Setup eines Unit-Tests kann kompliziert werden, wenn mehrere Klassen dafür instanziiert und konfiguriert werden müssen.
|
||||
* Das Problem kann entschärft werden, indem
|
||||
** die verschiedenen Klassen besser entkoppelt werden und
|
||||
** die Klassen, von denen die zu testende Klasse abhängig ist, zu mocken.
|
||||
****
|
||||
|
||||
. Der Testfokus war bisher auf der Klasse `Heart`.
|
||||
Testen Sie jetzt die Klasse `Half`. Wo verwenden Sie Stubbing, wo Mocking?
|
||||
+
|
||||
****
|
||||
* Die Funktionalität der Klasse Half nicht besonders umfangreich.
|
||||
Alle Methoden der Klasse bestehen aus nur genau einer Zeile und die Klasse hat bis auf das Datenfeld `Half.side` keinen State.
|
||||
Was schieflaufen könnte ist, dass jemand beispielweise auf einem Ventil anstatt der `close()` Methode die `open()` Methode aufruft.
|
||||
Weil wir in der Klasse `Half` nicht wirklich einen Status prüfen können, macht es mehr Sinn das Verhalten der Klasse zu prüfen (ruft die Klasse die richtigen Methoden auf)
|
||||
→ Das können wir mit Mocking (in Mockito mit `verify`) erreichen.
|
||||
Entsprechend würde sich hier ein Mock besser eignen als ein Stub.
|
||||
Über die Frage, wie sinnvoll es ist solch einfache Klassen zu simulieren, lässt sich Diskutieren und ist abhängig vom jeweiligen Testkonzept.
|
||||
|
||||
* Beim Auftrag die Klasse `Half` zu testen, stossen Sie auf das Problem, dass Sie weder Stubs noch Mocks anwenden können, weil die Klassen von welchen `Half` abhängt direkt im Konstruktor von `Half` initialisiert werden.
|
||||
Damit können Sie kein Test-Double verwenden (injecten).
|
||||
Das Problem könnten Sie lösen, wenn es einen entsprechenden Konstruktor in der Klasse `Half` gäbe, welche die Instanzen der Abhängigen Typen entgegennimmt (analog zu `Heart`).
|
||||
****
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user