Test of Object-oriented Programs |
A complete PDF version of the text book is now available. The PDF version is an almost complete subset of the HTML version (where only a few, long program listings have been removed). See here. |
55.1. Test Units
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
Which kind of program units are tested? What is the smallest testing unit? The program units for which there exists at least one test case |
|
In an object-oriented program, the test units are the individual, public operations in each class |
55.2. Unit Testing
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
Unit testing aims to ensure that each individual part of a program works correct in isolation Unit testing should assure correct behaviour of the given unit before it is integrated with other units Unit testing is black box testing |
55.3. A Unit Test example in C# (1)
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
Unit test of the class BankAccount |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | using System; public class BankAccount { private double interestRate; private string owner; private double balance; public BankAccount(string owner): this(owner, 0.0, 0.0) { } public BankAccount(string owner, double balance): this(owner, balance, 0.0) { } public BankAccount(string owner, double balance, double interestRate) { this.interestRate = interestRate; this.owner = owner; this.balance = balance; } public double Balance { get{ return balance; } } public double InterestRate { get{ return interestRate; } } public void Withdraw (double amount) { balance -= amount; } public void Deposit (double amount) { balance += amount; } public void AddInterests() { balance = balance + balance * interestRate; } public override string ToString() { return owner + "'s account holds " + + balance + " kroner"; } } | |||
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | using System; using NUnit.Framework; [TestFixture] public class BankAccountTest{ BankAccount ba1, ba2, ba3; const double tol = 0.000001; [SetUp] public void Init(){ ba1 = new BankAccount("Peter"); ba2 = new BankAccount("Jens", 1000.0); ba3 = new BankAccount("Martin", 2000.0, 0.03); } [Test] public void InitTest(){ Assert.AreEqual(0.0, ba1.Balance, tol, "ba1 init"); Assert.AreEqual(1000.0, ba2.Balance, tol, "ba2 init balance"); Assert.AreEqual(2000.0, ba3.Balance, tol, "ba3 init balance"); Assert.AreEqual(0.0, ba1.InterestRate, tol, "ba1 interest rate"); Assert.AreEqual(0.0, ba2.InterestRate, tol, "ba2 interest rate"); Assert.AreEqual(0.03, ba3.InterestRate, tol, "ba3 interest rate"); } [Test] public void DepositTest(){ ba1.Deposit(100); ba2.Deposit(100); ba3.Deposit(100); Assert.AreEqual(100.0, ba1.Balance, tol, "ba1 deposit"); Assert.AreEqual(1100.0, ba2.Balance, tol, "ba2 deposit"); Assert.AreEqual(2100.0, ba3.Balance, tol, "ba3 deposit"); } [Test] public void WithdrawTest(){ ba1.Withdraw(100.0); ba2.Withdraw(100.0); ba3.Withdraw(100.0); Assert.AreEqual(-100.0, ba1.Balance, tol, "ba1 withdraw"); Assert.AreEqual(900.0, ba2.Balance, tol, "ba2 withdraw"); Assert.AreEqual(1900.0, ba3.Balance, tol, "ba3 withdraw"); } [Test] public void AddInterestsTest(){ ba1.AddInterests(); ba2.AddInterests(); ba3.AddInterests(); Assert.AreEqual(0.0, ba1.Balance, tol, "ba1 add interest"); Assert.AreEqual(1000.0, ba2.Balance, tol, "ba2 add interest"); Assert.AreEqual(2060.0, ba3.Balance, tol, "ba3 add interest"); } } | |||
|
Figure 55.1 A screenshot of NUnit - all tests are successful |
55.4. A Unit Test example in C# (2)
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
Unit test of an erroneous version of class BankAccount |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | using System; public class BankAccount { private double interestRate; private string owner; private double balance; public BankAccount(string owner): this(owner, 0.0, 0.0) { } /* Wrong initialization of interest rate */ public BankAccount(string owner, double balance): this(owner, balance, 0.01) { // Should have been 0.0 } public BankAccount(string owner, double balance, double interestRate) { this.interestRate = interestRate; this.owner = owner; this.balance = balance; } public double Balance { get{ return balance; } } public double InterestRate { get{ return interestRate; } } public void Withdraw (double amount) { balance -= amount; } public void Deposit (double amount) { balance += amount; } public void AddInterests() { balance = balance + balance * interestRate; } public override string ToString() { return owner + "'s account holds " + + balance + " kroner"; } } | |||
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | using System; using NUnit.Framework; [TestFixture] public class BankAccountTest{ BankAccount ba1, ba2, ba3; const double tol = 0.000001; [SetUp] public void Init(){ ba1 = new BankAccount("Peter"); ba2 = new BankAccount("Jens", 1000.0); ba3 = new BankAccount("Martin", 2000.0, 0.03); } [Test] public void InitTest(){ Assert.AreEqual(0.0, ba1.Balance, tol, "ba1 init "); Assert.AreEqual(1000.0, ba2.Balance, tol, "ba2 init balance"); Assert.AreEqual(2000.0, ba3.Balance, tol, "ba3 init balance"); Assert.AreEqual(0.0, ba1.InterestRate, tol, "ba1 interest rate"); Assert.AreEqual(0.0, ba2.InterestRate, tol, "ba2 interest rate"); Assert.AreEqual(0.03, ba3.InterestRate, tol, "ba3 interest rate"); } [Test] public void DepositTest(){ ba1.Deposit(100); ba2.Deposit(100); ba3.Deposit(100); Assert.AreEqual(100.0, ba1.Balance, tol, "ba1 deposit"); Assert.AreEqual(1100.0, ba2.Balance, tol, "ba2 deposit"); Assert.AreEqual(2100.0, ba3.Balance, tol, "ba3 deposit"); } [Test] public void WithdrawTest(){ ba1.Withdraw(100.0); ba2.Withdraw(100.0); ba3.Withdraw(100.0); Assert.AreEqual(-100.0, ba1.Balance, tol, "ba1 withdraw"); Assert.AreEqual(900.0, ba2.Balance, tol, "ba2 withdraw"); Assert.AreEqual(1900.0, ba3.Balance, tol, "ba3 withdraw"); } [Test] public void AddInterestsTest(){ ba1.AddInterests(); ba2.AddInterests(); ba3.AddInterests(); Assert.AreEqual(0.0, ba1.Balance, tol, "ba1 add interest"); Assert.AreEqual(1000.0, ba2.Balance, tol, "ba2 add interest"); Assert.AreEqual(2060.0, ba3.Balance, tol, "ba3 add interest"); } } | |||
|
Figure 55.2 A screenshot of NUnit - some tests fail |
55.5. NUnit for C#
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
NUnit is a unit testing framework for C# |
|
Exercise 14.2. Install Nunit Download and install the latest stable version Nunit from www.nunit.org on your computer. If you have not already done so, consult the basic information about NUnit on the accompanying slide. More specifically, for Windows users, goto the NUnit download page and download the most recent stable version of NUNIT for .NET 2.0. As of February 2010 this is the NUnit-2.5.3.9345.msi file (a Windows installer file). You can also get this file directly from here. Mono users on Linux probably already have an installation of NUnit. According to the NUnit documentation, Mono 1.0 through Mono 1.9 include NUnit 2.2. Try calling the NUnit console runner named nunit-console from your shell. Please be aware of the version you are running. The newest stable version of NUnit is 2.5.3 (as of February 2010). Mono users are recommended to use version 2.4.8, however. See also the release notes. Next, consult the NUnit documentation. Pay attention to the menu to the right. In particular the CORE FEATURES documentation of Assertions and Attributes. There is no solution to this exercise |
55.6. NUnit Attributes
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
|
55.7. NUnit Assertions
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
|
55.8. Unit Test Concepts
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
Understanding the common concepts of Unit testing |
|
Test cases and Test suites may be organized as a Composite |
55.9. Another Unit Test example in C#
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
Unit test of the class Set<T> from the lecture about generics |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | using System; using System.Collections.Generic; using System.Collections; public class Set<T> { /* Invariant: At most one occurrence of an element in the array store Consequtive storing from low end. */ private int capacity; private static int DefaultCapacity = 3; private T[] store; private int next; public Set(int capacity){ this.capacity = capacity; store = new T[capacity]; next = 0; } public Set(): this(DefaultCapacity){ } public Set(T[] elements): this(elements.Length){ foreach(T el in elements) this.Insert(el); } // Copy constructor public Set(Set<T> s): this(s.capacity){ foreach(T el in s) this.Insert(el); } public bool Member(T element){ for(int idx = 0; idx < next; idx++) if (element.Equals(store[idx])) return true; return false; } public void Insert(T element){ if (!this.Member(element)){ if (this.Full){ Console.WriteLine("[Resize to {0}]", capacity * 2); Array.Resize<T>(ref store, capacity * 2); capacity = capacity * 2; } store[next] = element; next++; } } public void Delete(T element){ bool found = false; int foundIdx = 0; for(int idx = 0; !found && (idx < next); idx++){ if (element.Equals(store[idx])){ found = true; foundIdx = idx; } } if (found){ // shift remaining elements left for(int idx = foundIdx+1; idx < next; idx++) store[idx-1] = store[idx]; store[next-1] = default(T); next--; } } public int Count{ get{ return next; } } // Is this set a subset of other public bool Subset(Set<T> other){ foreach(T e in this) if (!other.Member(e)) return false; return true; } public Set<T> Intersection(Set<T> other){ Set<T> res = new Set<T>(this.Count); foreach(T e in this) if (other.Member(e)) res.Insert(e); return res; } public Set<T> Union(Set<T> other){ Set<T> res = new Set<T>(this.Count + other.Count); foreach(T e in this) res.Insert(e); foreach(T e in other) res.Insert(e); return res; } // Subtract other elements from this set. public Set<T> Diff(Set<T> other){ Set<T> res = new Set<T>(this); foreach(T e in other) res.Delete(e); return res; } public override string ToString(){ string elRes = ""; for(int idx = 0; idx < next; idx++) if (store[idx] != null) elRes += " " + store[idx]; return "{" + elRes + " "+ "}" + this.Count; } private class SetEnumerator: IEnumerator<T>{ private readonly Set<T> set; private int idx; public SetEnumerator (Set<T> s){ this.set = s; idx = -1; // position enumerator outside range } public T Current{ get { return set.store[idx]; } } Object IEnumerator.Current{ get { return set.store[idx]; } } public bool MoveNext(){ if (idx < set.next - 1){ idx++; return true; } else return false; } public void Reset(){ idx = -1; } public void Dispose(){ } } public IEnumerator<T> GetEnumerator (){ return new SetEnumerator(this); } private bool Full{ get{ return next == capacity; } } } | |||
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | using System; using NUnit.Framework; [TestFixture] public class SetTest{ Set<int> set1, set2, set3, set4; [SetUp] public void Init(){ set1 = new Set<int>(); set2 = new Set<int>(10); set3 = new Set<int>(new int[]{1,2,3}); set4 = new Set<int>(set3); } [Test] public void MemberTest(){ Assert.IsFalse(set1.Member(1), "Member test 1"); Assert.IsFalse(set2.Member(1), "Member test 2"); Assert.IsTrue(set3.Member(1), "Member test 3"); Assert.IsTrue(set4.Member(1), "Member test 4"); Assert.IsFalse(set3.Member(4), "Member test 5"); Assert.IsFalse(set4.Member(4), "Member test 6"); } [Test] public void InsertNonMember(){ set1.Insert(1); Assert.AreEqual(set1.Count, 1, "Insert test 1"); Assert.IsTrue(set1.Member(1), "Insert test 2"); set3.Insert(4); Assert.AreEqual(set3.Count, 4, "Insert test 3"); set4.Insert(4); Assert.AreEqual(set4.Count, 4, "Insert test 4"); } [Test] public void InsertMember(){ set1.Insert(1); set1.Insert(1); Assert.AreEqual(set1.Count, 1, "Insert test 1"); Assert.IsTrue(set1.Member(1), "Insert test 2"); set3.Insert(3); Assert.AreEqual(set3.Count, 3, "Insert test 3"); set4.Insert(3); Assert.AreEqual(set4.Count, 3, "Insert test 4"); } [Test] public void DeleteNonMember(){ set1.Delete(1); Assert.AreEqual(set1.Count, 0, "Delete test 1"); set3.Delete(4); Assert.AreEqual(set3.Count, 3, "Delete test 2"); } [Test] public void DeleteMember(){ set3.Delete(1); Assert.AreEqual(set3.Count, 2, "Delete test 1"); Assert.IsFalse(set3.Member(1), "Delete test 2"); } // Delete member twice [Test] public void DeleteMemberTwice(){ set3.Delete(1); set3.Delete(1); Assert.AreEqual(set3.Count, 2, "Delete test 1"); Assert.IsFalse(set3.Member(1), "Delete test 2"); } [Test] public void MultipleDelete(){ set3.Delete(1); set3.Delete(2); set3.Delete(3); Assert.AreEqual(set3.Count, 0, "Delete test 1"); Assert.IsFalse(set3.Member(1), "Delete test 2"); Assert.IsFalse(set3.Member(2), "Delete test 3"); Assert.IsFalse(set3.Member(3), "Delete test 4"); } } | |||
|
Exercise 14.3. Test of class Set In a previous exercise we have implemented the operations intersection, union, and set difference in class Set<T>. In continuation of class SetTest, perform unit tests of the intersection, union, and set difference operations. The natural starting point is your solution to the previous exercise. You can also chose to test my solution. There is no solution to this exercise |
55.10. Test Scaffolding
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
The test scaffolding denotes the auxilliary programs and classes that allow us to test a given program unit |
Figure 55.3 Test scaffolding |
|
It appears attractive to test the classes bottom up in order to avoid excessive use of stubs |
55.11. The Background and Context of Unit Testing
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
Unit testing was was popularized for Java by tool called JUnit Kent Beck and Erich Gamma are the originators of unit testing |
|
Unit testing is a cornerstone in Extreme Programming |
55.12. Test Driven Development
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
Test a little. Code a little. |
|
55.13. Unit Test Recommendation
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
|
55.14. Test of Object-oriented programs
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
What are the basic challenges of testing object-oriented programs? |
|