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,72 +15,64 @@ Nehmen wir zum Beispiel ein einfaches Formular. Nach Absenden des Formulars soll
|
||||
|
||||
<img src="/images/unittests-webforms/TestableWebForm.jpg" alt="Testable WebForm"/> <img src="/images/unittests-webforms/TestableWebForm_Sent.jpg" alt="Testable WebForm Sent"/>
|
||||
|
||||
Und nun zum Quellcode: Die automatisch erstellte .designer.cs muss entfernt werden, was man sowieso tun sollte, da man automatisch erstellten Code – also Code, den keiner außer Microsoft unter Kontrolle hat – vermeiden sollte.
|
||||
Und nun zum Quellcode: Die automatisch erstellte .designer.cs muss entfernt werden, was man sowieso tun sollte, da man automatisch erstellten Code – also Code, den keiner außer Microsoft unter Kontrolle hat – vermeiden sollte.
|
||||
Die Inhalte der .designer.cs – also die Definitionen der Web-Elemente – werden in der Klasse als `Public Properties` erstellt und instantiiert, um bei Zugriffen wie `TextBox.Text` keine NullReferenceException zu bekommen.
|
||||
|
||||
```
|
||||
using System;
|
||||
using System.Web.UI.HtmlControls;
|
||||
using System.Web.UI.WebControls;
|
||||
using framework.Testable.Web.UI;
|
||||
5
|
||||
namespace TestableWebForm
|
||||
```csharp
|
||||
using System;
|
||||
using System.Web.UI.HtmlControls;
|
||||
using System.Web.UI.WebControls;
|
||||
using framework.Testable.Web.UI;
|
||||
namespace TestableWebForm
|
||||
{
|
||||
public class DefaultPage : System.Web.UI. Page
|
||||
public class DefaultPage : System.Web.UI. Page
|
||||
{
|
||||
10
|
||||
#region Controls
|
||||
public HtmlForm Formular;
|
||||
public TextBox Value1 = new TextBox ();
|
||||
public TextBox Value2 = new TextBox ();
|
||||
public Label Result = new Label ();
|
||||
public Button Submit = new Button ();
|
||||
#region Controls
|
||||
public HtmlForm Formular;
|
||||
public TextBox Value1 = new TextBox ();
|
||||
public TextBox Value2 = new TextBox ();
|
||||
public Label Result = new Label ();
|
||||
public Button Submit = new Button ();
|
||||
#endregion
|
||||
```
|
||||
|
||||
Um das Verhalten testen zu können, haben wir Adapter für die Klassen `System.Web.UI.Page`, `System.Web.HttpRequest` und `System.Web.HttpResponse` geschrieben, und zwar für die Properties und Methoden die uns vorerst interessieren: z.B. `Page.IsPostBack, Page.Request, Response.Redirect(string url, bool endResponse)`. Bei der Benennung haben wir einfach den Namespace `System` mit `framework.Testable` ersetzt und wir haben natürlich zu jedem Testable-Objekt einen Interface erstellt.
|
||||
|
||||
```
|
||||
1
|
||||
namespace framework.Testable.Web
|
||||
```csharp
|
||||
namespace framework.Testable.Web
|
||||
{
|
||||
namespace UI
|
||||
namespace UI
|
||||
{
|
||||
public interface IPage
|
||||
public interface IPage
|
||||
{
|
||||
bool IsPostBack{ get ; }
|
||||
IHttpRequest Request{ get ; set ; }
|
||||
IHttpResponse Response{ get ; set ; }
|
||||
bool IsPostBack{ get ; }
|
||||
IHttpRequest Request{ get ; set ; }
|
||||
IHttpResponse Response{ get ; set ; }
|
||||
}
|
||||
12
|
||||
public class Page : IPage
|
||||
public class Page : IPage
|
||||
{
|
||||
private readonly System.Web.UI. Page m_page;
|
||||
private IHttpRequest m_request;
|
||||
private IHttpResponse m_response;
|
||||
18
|
||||
public Page( System.Web.UI. Page page )
|
||||
private readonly System.Web.UI. Page m_page;
|
||||
private IHttpRequest m_request;
|
||||
private IHttpResponse m_response;
|
||||
public Page( System.Web.UI. Page page )
|
||||
{
|
||||
m_page = page;
|
||||
m_request = new HttpRequest ( m_page );
|
||||
m_response = new HttpResponse ( m_page );
|
||||
m_request = new HttpRequest ( m_page );
|
||||
m_response = new HttpResponse ( m_page );
|
||||
}
|
||||
25
|
||||
public bool IsPostBack
|
||||
public bool IsPostBack
|
||||
{
|
||||
get { return m_page.IsPostBack; }
|
||||
get { return m_page.IsPostBack; }
|
||||
}
|
||||
30
|
||||
public IHttpRequest Request
|
||||
public IHttpRequest Request
|
||||
{
|
||||
get { return m_request; }
|
||||
set { m_request = value ; }
|
||||
get { return m_request; }
|
||||
set { m_request = value ; }
|
||||
}
|
||||
36
|
||||
public IHttpResponse Response
|
||||
public IHttpResponse Response
|
||||
{
|
||||
get { return m_response; }
|
||||
set { m_response = value ; }
|
||||
get { return m_response; }
|
||||
set { m_response = value ; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,68 +83,61 @@ Um alle gemockte Objekte setzen zu können, haben wir unserer `Page`-Klasse auch
|
||||
|
||||
Das war ungefähr alles: in der Seite nutzt man dann anstelle der eigenen Request und Response-Objekten die `Testable`-Objekte.
|
||||
|
||||
```
|
||||
public class DefaultPage : System.Web.UI. Page
|
||||
```csharp
|
||||
public class DefaultPage : System.Web.UI. Page
|
||||
{
|
||||
…
|
||||
private IPage m_page;
|
||||
public void SetTestableObjects( IPage page )
|
||||
private IPage m_page;
|
||||
public void SetTestableObjects( IPage page )
|
||||
{
|
||||
m_page = page;
|
||||
}
|
||||
24
|
||||
public void Page_Load( object sender, EventArgs e )
|
||||
public void Page_Load( object sender, EventArgs e )
|
||||
{
|
||||
if (m_page == null ) m_page = new Page ( this );
|
||||
int multiple = 1;
|
||||
if (! string .IsNullOrEmpty( m_page.Request.GetParamValue( "multiple" ) )) multiple = Convert .ToInt32( m_page.Request.GetParamValue( "multiple" ) );
|
||||
if (m_page.IsPostBack)
|
||||
if (m_page == null ) m_page = new Page ( this );
|
||||
int multiple = 1;
|
||||
if (! string .IsNullOrEmpty( m_page.Request.GetParamValue( "multiple" ) )) multiple = Convert .ToInt32( m_page.Request.GetParamValue( "multiple" ) );
|
||||
if (m_page.IsPostBack)
|
||||
{
|
||||
Result.Text = ( ( Convert .ToInt32( Value1.Text ) + Convert .ToInt32( Value2.Text ))*multiple ).ToString();
|
||||
Result.Text = ( ( Convert .ToInt32( Value1.Text ) + Convert .ToInt32( Value2.Text ))*multiple ).ToString();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Damit ist die Web-Anwendung bereit zum Testen. So schaut zum Beispiel ein Test für das Laden der Seite aus:
|
||||
|
||||
```
|
||||
using framework.Testable.Web;
|
||||
using framework.Testable.Web.UI;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
using TestableWebForm;
|
||||
6
|
||||
namespace Tests
|
||||
```csharp
|
||||
using framework.Testable.Web;
|
||||
using framework.Testable.Web.UI;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
using TestableWebForm;
|
||||
namespace Tests
|
||||
{
|
||||
[ TestFixture ]
|
||||
public class WebFormTests
|
||||
public class WebFormTests
|
||||
{
|
||||
private IPage m_page;
|
||||
private IHttpRequest m_request;
|
||||
private IHttpResponse m_response;
|
||||
private DefaultPage m_defaultPage;
|
||||
16
|
||||
private IPage m_page;
|
||||
private IHttpRequest m_request;
|
||||
private IHttpResponse m_response;
|
||||
private DefaultPage m_defaultPage;
|
||||
[ SetUp ]
|
||||
public void Init()
|
||||
public void Init()
|
||||
{
|
||||
m_page = MockRepository .GenerateStub< IPage >();
|
||||
m_request = MockRepository .GenerateStub< IHttpRequest >();
|
||||
m_response = MockRepository .GenerateStub< IHttpResponse >();
|
||||
m_page = MockRepository .GenerateStub< IPage >();
|
||||
m_request = MockRepository .GenerateStub< IHttpRequest >();
|
||||
m_response = MockRepository .GenerateStub< IHttpResponse >();
|
||||
m_page.Request = m_request;
|
||||
m_page.Response = m_response;
|
||||
}
|
||||
26
|
||||
[ Test ]
|
||||
public void PageLoad_Loading_EmptyFields()
|
||||
public void PageLoad_Loading_EmptyFields()
|
||||
{
|
||||
// Arrange
|
||||
31
|
||||
32
|
||||
// Act
|
||||
m_defaultPage = new DefaultPage ();
|
||||
m_defaultPage = new DefaultPage ();
|
||||
m_defaultPage.SetTestableObjects(m_page);
|
||||
m_defaultPage.Page_Load( null , null );
|
||||
37
|
||||
m_defaultPage.Page_Load( null , null );
|
||||
// Assert
|
||||
Assert .IsEmpty( m_defaultPage.Value1.Text );
|
||||
Assert .IsEmpty( m_defaultPage.Value2.Text );
|
||||
@@ -162,24 +147,22 @@ Assert .IsEmpty(m_defaultPage.Result.Text);
|
||||
|
||||
Und so für PostBack inklusive QueryString-Parameter:
|
||||
|
||||
```
|
||||
```csharp
|
||||
[ Test ]
|
||||
public void PageLoad_PostBackWithRequestValue_ResultIsCorrect()
|
||||
public void PageLoad_PostBackWithRequestValue_ResultIsCorrect()
|
||||
{
|
||||
// Arrange
|
||||
const int value1 = 1;
|
||||
const int value2 = 2;
|
||||
const int value3 = 3;
|
||||
m_request.Expect( a => a.GetParamValue( "multiple" ) ).IgnoreArguments().Repeat.Twice().Return( value3.ToString() );
|
||||
m_page.Expect( a => a.IsPostBack ).Return( true );
|
||||
72
|
||||
const int value1 = 1;
|
||||
const int value2 = 2;
|
||||
const int value3 = 3;
|
||||
m_request.Expect( a => a.GetParamValue( "multiple" ) ).IgnoreArguments().Repeat.Twice().Return( value3.ToString() );
|
||||
m_page.Expect( a => a.IsPostBack ).Return( true );
|
||||
// Act
|
||||
m_defaultPage = new DefaultPage ();
|
||||
m_defaultPage = new DefaultPage ();
|
||||
m_defaultPage.SetTestableObjects( m_page );
|
||||
m_defaultPage.Value1.Text = value1.ToString();
|
||||
m_defaultPage.Value2.Text = value2.ToString();
|
||||
m_defaultPage.Page_Load( null , null );
|
||||
79
|
||||
m_defaultPage.Page_Load( null , null );
|
||||
// Assert
|
||||
Assert .IsTrue( m_defaultPage.Result.Text == ( (value1 + value2)*value3 ).ToString() );
|
||||
}
|
||||
@@ -187,6 +170,6 @@ Assert .IsTrue( m_defaultPage.Result.Text == ( (value1 + value2)*value3 ).ToStri
|
||||
|
||||
Dieses Vorgehen hat uns nicht nur den seit langen gesuchten Weg zum Testen von Webanwendungen geebnet, sondern zwingt auch den Entwickler dazu, alle Funktionalitäten, die nicht in einer Webseite sondern in die dll-s gehören, auszulagern. Damit dürfte es auch der richtige Weg der Clean Code Developers für die Arbeit mit WebForms sein.
|
||||
|
||||
Was die Adapter-Klassen betrifft: inzwischen haben wir auch System.IO “adaptiert” und bald werden die anderen System-Klassen folgen, je nach Bedarf.
|
||||
Was die Adapter-Klassen betrifft: inzwischen haben wir auch System.IO “adaptiert” und bald werden die anderen System-Klassen folgen, je nach Bedarf.
|
||||
[Download VS2008-Projekt](/assets/TestableWebForm.zip)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user