mirror of
https://github.com/skoelle/buildbroken-blog-archive.git
synced 2026-09-17 18:30:25 +00:00
fix code blocks
This commit is contained in:
@@ -15,44 +15,41 @@ Erstens muss man die IDE anpassen: im März kommt .NET 4.0 raus und da wird Desi
|
||||
|
||||
Jetzt zum Code: Nehmen wir eine ganz einfache Klasse `Bill` deren Objekte mit einem `IRepository` gespeichert bzw. geladen werden.
|
||||
|
||||
```
|
||||
using System.Diagnostics.Contracts;
|
||||
namespace ContractsPrototyp
|
||||
```csharp
|
||||
using System.Diagnostics.Contracts;
|
||||
namespace ContractsPrototyp
|
||||
{
|
||||
public class Bill
|
||||
public class Bill
|
||||
{
|
||||
public int Id { get ; set ; }
|
||||
public string Number { get ; set ; }
|
||||
public double Value { get ; set ; }
|
||||
public int Id { get ; set ; }
|
||||
public string Number { get ; set ; }
|
||||
public double Value { get ; set ; }
|
||||
}
|
||||
10
|
||||
11
|
||||
public interface IRepository
|
||||
public interface IRepository
|
||||
{
|
||||
Bill GetBill( string number);
|
||||
void SaveBill( Bill bill);
|
||||
Bill GetBill( string number);
|
||||
void SaveBill( Bill bill);
|
||||
}
|
||||
```
|
||||
|
||||
Die Kontrakte kann man in den einzelnen Methoden oder für eine ganze Klasse schreiben (unter dem Attribut `ContractInvariantMethode`) aber ich finde am schönsten, dass man die auch auslagern kann: durch eine gegenseitige Markierung können Kontrakt-Klassen und Interfaces als “Paare” definiert werden:
|
||||
|
||||
```
|
||||
```csharp
|
||||
[ ContractClass ( typeof ( RepositoryContracts ))]
|
||||
public interface IRepository
|
||||
public interface IRepository
|
||||
{
|
||||
Bill GetBill( string number);
|
||||
void SaveBill( Bill bill);
|
||||
Bill GetBill( string number);
|
||||
void SaveBill( Bill bill);
|
||||
}
|
||||
[ ContractClassFor ( typeof ( IRepository ))]
|
||||
public class RepositoryContracts : IRepository
|
||||
public class RepositoryContracts : IRepository
|
||||
{
|
||||
public Bill GetBill( string number)
|
||||
public Bill GetBill( string number)
|
||||
{
|
||||
Contract .Requires(! string .IsNullOrEmpty(number));
|
||||
return null ;
|
||||
return null ;
|
||||
}
|
||||
25
|
||||
public void SaveBill( Bill bill)
|
||||
public void SaveBill( Bill bill)
|
||||
{
|
||||
Contract .Ensures(bill.Id > 0);
|
||||
}
|
||||
@@ -63,43 +60,40 @@ Eine Vorbedingung wird mit `Contract.Requires` und eine Nachbedingung mit `Contr
|
||||
|
||||
Die eigentliche Implementierung der Klasse schaut dann so aus:
|
||||
|
||||
```
|
||||
public class Repository : IRepository
|
||||
```csharp
|
||||
public class Repository : IRepository
|
||||
{
|
||||
public Bill GetBill( string nummer)
|
||||
public Bill GetBill( string nummer)
|
||||
{
|
||||
//Würde das Objekt aus Datenhaltung laden
|
||||
return new Bill ();
|
||||
return new Bill ();
|
||||
}
|
||||
38
|
||||
public void SaveBill( Bill bill)
|
||||
public void SaveBill( Bill bill)
|
||||
{
|
||||
//Würde das Objekt speichern und ihm eine Id zuweisen
|
||||
if (BillIsValid( bill )) bill.Id++;
|
||||
if (BillIsValid( bill )) bill.Id++;
|
||||
}
|
||||
44
|
||||
private static bool BillIsValid( Bill bill)
|
||||
private static bool BillIsValid( Bill bill)
|
||||
{
|
||||
return ! string .IsNullOrEmpty(bill.Nummer);
|
||||
return ! string .IsNullOrEmpty(bill.Nummer);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Woher können wir wissen, dass das funktioniert? Es ist einfach, wir schreiben ein Paar Tests dazu!
|
||||
Woher können wir wissen, dass das funktioniert? Es ist einfach, wir schreiben ein Paar Tests dazu!
|
||||
Bei Kontraktverletzung wird eine Exception geworfen. Um diese – und dadurch die genaue Verletzung – überprüfen zu können braucht man etwas Workaround:
|
||||
|
||||
```
|
||||
```csharp
|
||||
[ TestFixture ]
|
||||
public class BillTests
|
||||
public class BillTests
|
||||
{
|
||||
private IRepository m_repository;
|
||||
private string m_message;
|
||||
60
|
||||
private IRepository m_repository;
|
||||
private string m_message;
|
||||
[ SetUp ]
|
||||
public void Setup()
|
||||
public void Setup()
|
||||
{
|
||||
m_repository = new Repository ();
|
||||
m_message = string .Empty;
|
||||
m_repository = new Repository ();
|
||||
m_message = string .Empty;
|
||||
Contract .ContractFailed += ( sender, e ) =>
|
||||
{
|
||||
e.SetUnwind();
|
||||
@@ -110,37 +104,32 @@ m_message = e.Message;
|
||||
|
||||
Danach sind die Tests dann einfach:
|
||||
|
||||
```
|
||||
```csharp
|
||||
[ Test ]
|
||||
public void Laden_mit_leerer_Nummer_verletzt_Kontrakt()
|
||||
public void Laden_mit_leerer_Nummer_verletzt_Kontrakt()
|
||||
{
|
||||
76
|
||||
try
|
||||
{
|
||||
m_repository.GetBill( null );
|
||||
m_repository.GetBill( null );
|
||||
}
|
||||
catch
|
||||
{
|
||||
//Nichts
|
||||
}
|
||||
85
|
||||
Assert .That( m_message, Is .EqualTo( "Precondition failed: !string.IsNullOrEmpty(number)" ) );
|
||||
Assert .That( m_message, Is .EqualTo( "Precondition failed: !string.IsNullOrEmpty(number)" ) );
|
||||
}
|
||||
88
|
||||
[ Test ]
|
||||
public void Speichern_Rechnung_ohne_Nummer_verletzt_Kontrakt()
|
||||
public void Speichern_Rechnung_ohne_Nummer_verletzt_Kontrakt()
|
||||
{
|
||||
92
|
||||
try
|
||||
{
|
||||
m_repository.SaveBill( new Bill {Value = 25} );
|
||||
m_repository.SaveBill( new Bill {Value = 25} );
|
||||
}
|
||||
catch
|
||||
{
|
||||
//Nichts
|
||||
}
|
||||
101
|
||||
Assert .That( m_message, Is .EqualTo( "Postcondition failed: bill.Id > 0" ) );
|
||||
Assert .That( m_message, Is .EqualTo( "Postcondition failed: bill.Id > 0" ) );
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user