fix code blocks

This commit is contained in:
2026-09-10 22:54:41 +02:00
parent 2dc30200a1
commit cd8a75ec13
9 changed files with 519 additions and 666 deletions
+75 -92
View File
@@ -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)