SlideShare una empresa de Scribd logo
1 de 67
REFACTORING LEGACY WEB
FORMS FOR TEST AUTOMATION
Author: Stephen Fuqua
Last Revised: December 2014
All content by the author or from public domain sources unless otherwise noted
PART 1
The Challenge
Taking Over Legacy Code
• Given you understand the value of test
automation
• Given you are handed a legacy application to
maintain and enhance
• Given the application is in ASP.Net Web Forms
• When you try to add tests
• Then you find that test-driven development is
literally impossible.
Web Forms Challenge
• Testing ASP.Net Web Forms is problematic:
• Tutorials show poor design, leading many
developers to mix UI, business, and data access logic
into a single class (the code behind).
• ASP.Net functionality such as Session and ViewState
are difficult to manipulate in an automated test.
• Likewise, Web Forms architecture makes it difficult
to access and manipulate form controls in tests.
• Temptation: automate tests on the UI itself.
Test Pyramid
• In Succeeding with Agile, Mike Cohn describes a
pyramid of automated tests:
• Dominated by unit tests, and
• Featuring service (system) tests that functionally
integrate the units, and
• Including just a few UI tests, to confirm that form fields
connect to the services.
UI
Service
Unit
http://www.mountaingoatsoftware.com/blog/
the-forgotten-layer-of-the-test-automation-
pyramid
Right-siding the Pyramid
• UI tests are brittle, expensive to write, and time
consuming, to paraphrase Cohn.
• With judicious refactoring, it is possible to
continue using Web Forms and still achieve the
goals of test automation and test-driven
development
• To overcome this challenge…
• Use the Model-View-Presenter pattern, and
• Introduce test isolation techniques.
PART 2
Toolkit
Model-View-Presenter
• Model-View-Presenter, or MVP, is a specialized
version of Model-View-Controller (MVC).
• Split the traditional code behind into View and
Presenter.
View Presenter Model
Code-
Behind
Business Logic
Test Isolation Flow Chart
http://www.safnet.com/writing/tech/2014/08/unit-test-
isolation-for-legacy-net-code.html
Refactoring
• Start refactoring the code, carefully introducing
isolation techniques in moving to MVP.
• Sprouting – the code behind sprouts into model,
view, and presenter. AKA Extract Method.
• Adapters – write adapters for ASP.Net functionality
that cannot be manipulated in unit tests.
• Stubs & mocks – use interfaces and dependency
injection properly, then apply stubs and mocks in
the new unit test code.
The Straw Man
• To illustrate these techniques, I resurrected the
code for www.ibamonitoring.org.
• It is already split into web project and “back
end” library for business logic and data access.
• Contains unit and integration tests for the
library, but none for the web project.
• Originally used Microsoft Moles (now Fakes) to
isolate some of the code for unit testing.
• The app is sound, but patterns are used
inconsistently.
Site Conditions Page
PART 3
Refactoring to Adapters
Adapters for Caching
• Introduce adapters that wrap Session,
Application, etc.
• Side benefit: centralizes magic strings and
casting from object to appropriate types.
• Use lazy-loading for Property Injection,
combined with Test Specific Subclasses, to
allow production code to access real objects
and tests to access fake objects.
Example: An Adapter for Session
• Original code already
contained this
UserStateManager in
order to centralize
magic strings.
• It has now been
refactored to an
instance class with an
interface that can be
mocked.
Using the Session Adapter
• Adding the lazy-load to a base class.
• Note the use of HttpSessionStateWrapper, which
turns old Session into HttpSessionStateBase.
Unit Testing the Adapter
• Even an adapter can be unit tested… you’ll
need a fake Session for that. One that doesn’t
start the ASP.Net engine.
• In other words, a Test Specific Subclass.
• But Session is sealed.
• Hence the use of HttpSessionStateBase, which
is not sealed!
Unit Testing the Adapter, cont.
PART 4
Refactoring to Model-View-Presenter (MVP)
The MVP Pattern
• Model contains business
logic or accesses business
logic in services.
• View contains properties
and methods for accessing
form controls and changing
the display.
• Presenter connects the two;
all “UI logic” moves from
View to Presenter.
• Use Separation of Interfaces
to facilitate testing the
Presenter.
Deconstructing the View
• Move use of dependencies to the Presenter.
• Create a property for each control that needs
to be accessed by the UI logic in the Presenter.
• And methods for behavior changes that the
Presenter should invoke in the UI.
Using the Presenter
• Add the Presenter to the concrete View.
• The view’s events make calls to the Presenter.
Class Diagrams After Refactor
Discussion
• View’s interface and Presenter are still in the
web project.
• This example does not show behavior changes
in the view.
• This app’s Model is not well-constructed:
• Presenter calls static methods that can’t be mocked.
• Presenter is invoking business logic, not just UI logic
– extract that into the Model.
Evaluating the Presenter
• Green – UI layer
logic
• Red – business
logic
• Yellow – extract
to methods with
validation
• Business logic
should return a
modified SiteVisit
object after
performing
inserts.
• Just noticed –
first line isn’t
used! Remove
GlobalMap from
constructor.
Business Logic – the Facade
• For business logic, I prefer to create a Façade
class that takes just a few arguments and hides
the complexity of data access and
manipulation.
• The Façade itself can be injected into the
Presenter’s constructor.
Refreshing the Class Diagram
SiteConditionsFacade
Refactored Presenter
• Accesses
state.
• Retrieves
and validates
form values.
• Forwards
values to the
business
layer.
• Is fully
testable.
Result
• Original code behind was impossible to test,
netting 40 lines of untested code.
• Now, code behind has:
• Some untested properties – but low risk of defects.
• Event handler with one new line of untested code.
• A new constructor with one line of untested code.
• The presenter, and the wrappers for Session
and Application, are 100% unit testable.
PART 5
Unit Testing the View / Introducing Dependency Injection
Web Forms and Dependency Injection
• Without dependency injection, I cannot test
the View’s constructor or events.
• There is a means available for setting up full
dependency in Web Forms: an HttpModule.
• … and an open source solution to setup Unity
as an Inversion of Control (IoC) container:
https://bitbucket.org/KyleK/unity.webforms
• Likely there are similar packages for other IoC
containers, but Unity is my current tool.
Evaluating the View’s Constructor
• Here is the updated constructor for the View,
injecting the new Façade into the Presenter.
• The presence of the View in the Presenter’s
constructor introduces a circular dependency,
thus preventing use of any IoC container.
• View depends on Presenter depends on View
Solution: Abstract Factory
• A solution to this conundrum is a Factory class
with methods to build the presenters.
• The Factory can wrap the IoC container.
• It can access session and app state from
HttpContext.Current.
• In order to unit test the factory, we’ll want to access
state variables through lazy-loaded properties.
• Be sure to keep the abstract factory in the web
project. Discussion: http://odetocode.com/Articles/112.aspx
PresenterFactory
Injecting View and State
• To set the instance-specific view and state
values resolving the presenter type, use the
ResolverOverride parameter argument.
Setup Dependency Injection
• The installed package created class
UnityWebFormsStart in the web project – add
dependencies to this class.
• Use Registration by Convention to auto-map
the classes in the web project and library.
Modify The View
• Add the Factory as a
constructor argument
in the view / code
behind file.
• Call the Factory to
create the Presenter.
Unit Testing the View
• We should be able to unit test the view’s
constructor quite easily now
• What about the event that calls the presenter?
It has two more dependencies to isolate:
• Page.IsValid – create adapter an lazy load.
• Response.Redirect – lazy load an instance of
HttpResponseBase, and create a TSS class.
• Best to move these calls to the Presenter –
skipping that for time’s sake right now.
PageAdapter
• Three commonly used
properties to start with, can
be expanded as needed.
• Temporarily violating YAGNI
principle, but it is a trivial and
likely useful violation.
• Page is not sealed and thus
could be sub-classed for
testing, but it simply isn’t
worth it for 4 lines of code.
Code Coverage
• The entire web project is up to 7% coverage.
• 25% uncovered in the Factory from lazy loading.
• GlobalMap and UserStateManager are legacy –
they can be tested now, but are not fully yet.
• The View has 5.5% coverage, Presenter 100%.
PART 6
Interlude – Toward MVC
• The goal for this project is to automate tests,
not to migrate the framework, but…
• … in retrospection, the Presenter we’ve
developed is definitely starting to look like a
Controller from an ASP.Net MVC project.
• The next step in test automation is to address
service level testing – and that will be made
cleaner by refactoring the Presenter to be very
close to an MVC Controller.
Refactoring the Presenter Interface
• A Controller has direct access to HttpContext:
• The Presenter is in the web project - we can use
HttpContext.Current to access these values.
• Controller actions accept form data, either as a
set of variables or using model binding.
• Use the View as the “model” (View Model) and pass
to the action instead of to the constructor.
• Validation should stay with the View Model.
Gap Analysis
Session Response
Application Request
• The lazy-loaded adapters
were previously in a base
class for ASPX pages.
• Move to a base class for
Presenters.
• Leave out PageAdapters
because those values
belong in the View /
ViewModel.
Lazy Loading Adapters
• Normally an MVC view model would be a
concrete class, not an interface.
• In this case, convenient to leave as an interface
– if changing from Web Forms to MVC, then it
will be trivial to change the interface to a
concrete POCO.
• Updated signatures:
Convert to ViewModel
• The project uses validation controls in the ASPX
file - need to rely on Page.IsValid for validation.
• For test automation, best to have the Presenter
react to validation problems
• Create an IsValid property in the view interface,
and utilize it in the presenter.
• Limitation – can’t test the validation details,
only the Presenter’s response to invalid data.
• Might not need PageAdapter at all now…
View Model Validation
• The code behind in the View has become much
simpler – call the factory, then call the
SaveConditions “action”, passing the View itself
as the View Model.
• What about the exception handling? In this
case, it is ASPX specific and I will leave it alone.
Updated View
PART 6
Service Level Testing with SpecFlow
• SpecFlow is a Visual Studio extension for
writing business requirements / acceptance
tests using the Gherkin language.
• Using SpecFlow, we can add service-level tests
that connect to the Presenter classes.
• … and, when we’re ready to enhance the
application, we can write new acceptance tests
in a Behavior Driven Development mode.
SpecFlow
• As an IBA observer, I want to
record the conditions for a
site visit so that I can submit
point count observations.
• Try entering realistic data in all
the fields – expect to go to the
Point count page.
• Try using end time less than
start time – expect error.
• Try leaving the form blank –
expect error.
User Story and Brief Confirmations
• Assuming SpecFlow is installed, and you have a
test project configured for MSTest*...
• Create a Feature file called SiteConditions.
• Modify the user story and scenario name.
• I will remove the tag and customize the steps in
the following slides.
Add a Feature
* Or leave with NUnit if you prefer
• Since this test is driving
a UI, input values
include the available
options for dropdowns
controls.
• We could initialize these
through a Test Hook, or
make the initialization
transparent by including
them the test definition.
Happy Path – Setup Dropdowns
• Fill in valid values.
• Simulate pressing the
Next button.
• Confirm the expected
page redirect.
• And the unstated
expectation that the
submitted data will be
saved into a database.
Happy Path – Fill in Form, Submit It
• Run the scenarios…
• Not surprisingly, the scenarios fails to run:
there is no connection between the scenario
and our application code.
Run the Scenario
• Need to right-click and choose Generate Step
Definitions.
• Creates a step definition file that provides a
template for linking the data and actions to the
application code.
Generate Step Definitions
• The metadata values need to be inserted into
the database – which brings us to…
• As with any database-integrated tests, you’ll
want to use a sandbox database. I will use the
same LocalDB instance that I already created
for stored procedure testing.
• Make sure the test project’s app.config file is
properly setup for data access.
• Use an ORM for quick-and-easy backdoor data
access (showing OrmLite here).
Sandbox Database
• Before the
complete test
run: setup
database
access.
• Before each
individual
test: clear out
all of the
tables that
will be used.
Test Hooks
• Now, edit the
generated step
definition
template, reading
the data from
SpecFlow and
writing into the
database.
• For convenience,
cache a local copy
of the objects and
their Id values.
Insert the Metadata
Setup the View Model
• The step “I have entered these values into the
form” contains the View Model / form data.
• Create a stub implementation of the View, and
populate it using SpecFlow’s Assist Helpers.
• Store the view model in a static variable for use
in the Given step.
• In order to call the Presenter without using
ASP.Net, create stub implementations of
IUserStateManager and HttpResponseBase.
• Instantiate the Presenter using Unity and inject
the stub objects.
Call the Presenter
• First validate the redirect.
• Then use OrmLite again to validate that the
actual data stored in the database matches the
View Model.
Evaluate the Results
• Now we have a fully automated regression test
of the “happy path” scenario for saving site
conditions – using the entire system except for
the ASPX page itself.
• Each additional confirmation can be written as
a new scenario in the same feature file.
• When you re-use a Given, When, or Then
phrase, you will have instant C# code re-use.
• Note that the feature file is essentially a
business requirements document.
Service Test Wrap-Up
CONCLUSION
Keys to Success
• Split code behind into Model-View-Presenter.
• Introduced adapters for ASP.Net classes.
• Session
• Application
• Response
• Introduced interfaces and a Factory class.
• Added Unity for Web Forms to achieve DI.
• Utilized SpecFlow for service-level tests.
• The entire solution is available under the MIT
license, hosted on GitHub.
• Files of particular interest:
• SiteConditions.aspx.cs
• SiteConditionsPresenter.cs
• PresenterFactory.cs
• UserState.cs
• SiteConditionsFacade.cs
• SiteConditions.feature
• SiteConditionsSteps.cs
• TestHooks.cs
Source Code
Preview: Moving to MVC
• Should be able to do something like this…
1. Create an MVC project.
2. Run the original application. Save the generated web
pages as .cshtml pages.
3. Change the Id values, e.g.
ctl00_contentBody_SiteVisitedInput to SiteVisitedInput
(find and replace “ctl00_contentBody” with “”).
4. Move the Presenters to MVC and rename as XyzController.
5. Change View interfaces to concrete ViewModel classes.
6. Update the validation, e.g. with Fluent Validator.

Más contenido relacionado

La actualidad más candente

Unit Testing Concepts and Best Practices
Unit Testing Concepts and Best PracticesUnit Testing Concepts and Best Practices
Unit Testing Concepts and Best PracticesDerek Smith
 
Testing the Untestable
Testing the UntestableTesting the Untestable
Testing the UntestableMark Baker
 
Unit & integration testing
Unit & integration testingUnit & integration testing
Unit & integration testingPavlo Hodysh
 
An Introduction to Unit Testing
An Introduction to Unit TestingAn Introduction to Unit Testing
An Introduction to Unit TestingJoe Tremblay
 
Unit Testing Fundamentals
Unit Testing FundamentalsUnit Testing Fundamentals
Unit Testing FundamentalsRichard Paul
 
How to Build Your Own Test Automation Framework?
How to Build Your Own Test Automation Framework?How to Build Your Own Test Automation Framework?
How to Build Your Own Test Automation Framework?Dmitry Buzdin
 
Unit testing (workshop)
Unit testing (workshop)Unit testing (workshop)
Unit testing (workshop)Foyzul Karim
 
Apex Testing and Best Practices
Apex Testing and Best PracticesApex Testing and Best Practices
Apex Testing and Best PracticesJitendra Zaa
 
Unit testing in Force.com platform
Unit testing in Force.com platformUnit testing in Force.com platform
Unit testing in Force.com platformChamil Madusanka
 
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)Jen Wong
 
Unit Testing Done Right
Unit Testing Done RightUnit Testing Done Right
Unit Testing Done RightBrian Fenton
 
Unit testing php-unit - phing - selenium_v2
Unit testing   php-unit - phing - selenium_v2Unit testing   php-unit - phing - selenium_v2
Unit testing php-unit - phing - selenium_v2Tricode (part of Dept)
 
Clean Unit Test Patterns
Clean Unit Test PatternsClean Unit Test Patterns
Clean Unit Test PatternsFrank Appel
 
Writing good unit test
Writing good unit testWriting good unit test
Writing good unit testLucy Lu
 
Java Unit Test and Coverage Introduction
Java Unit Test and Coverage IntroductionJava Unit Test and Coverage Introduction
Java Unit Test and Coverage IntroductionAlex Su
 
Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...
Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...
Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...seleniumconf
 

La actualidad más candente (20)

Unit Tesing in iOS
Unit Tesing in iOSUnit Tesing in iOS
Unit Tesing in iOS
 
Unit Testing Concepts and Best Practices
Unit Testing Concepts and Best PracticesUnit Testing Concepts and Best Practices
Unit Testing Concepts and Best Practices
 
Best practices unit testing
Best practices unit testing Best practices unit testing
Best practices unit testing
 
Testing the Untestable
Testing the UntestableTesting the Untestable
Testing the Untestable
 
Unit Testing 101
Unit Testing 101Unit Testing 101
Unit Testing 101
 
Unit & integration testing
Unit & integration testingUnit & integration testing
Unit & integration testing
 
An Introduction to Unit Testing
An Introduction to Unit TestingAn Introduction to Unit Testing
An Introduction to Unit Testing
 
Unit Testing Fundamentals
Unit Testing FundamentalsUnit Testing Fundamentals
Unit Testing Fundamentals
 
How to Build Your Own Test Automation Framework?
How to Build Your Own Test Automation Framework?How to Build Your Own Test Automation Framework?
How to Build Your Own Test Automation Framework?
 
Unit testing (workshop)
Unit testing (workshop)Unit testing (workshop)
Unit testing (workshop)
 
Workshop unit test
Workshop   unit testWorkshop   unit test
Workshop unit test
 
Apex Testing and Best Practices
Apex Testing and Best PracticesApex Testing and Best Practices
Apex Testing and Best Practices
 
Unit testing in Force.com platform
Unit testing in Force.com platformUnit testing in Force.com platform
Unit testing in Force.com platform
 
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
More on Fitnesse and Continuous Integration (Silicon Valley code camp 2012)
 
Unit Testing Done Right
Unit Testing Done RightUnit Testing Done Right
Unit Testing Done Right
 
Unit testing php-unit - phing - selenium_v2
Unit testing   php-unit - phing - selenium_v2Unit testing   php-unit - phing - selenium_v2
Unit testing php-unit - phing - selenium_v2
 
Clean Unit Test Patterns
Clean Unit Test PatternsClean Unit Test Patterns
Clean Unit Test Patterns
 
Writing good unit test
Writing good unit testWriting good unit test
Writing good unit test
 
Java Unit Test and Coverage Introduction
Java Unit Test and Coverage IntroductionJava Unit Test and Coverage Introduction
Java Unit Test and Coverage Introduction
 
Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...
Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...
Testing Rapidly Changing Applications With Self-Testing Object-Oriented Selen...
 

Similar a Refactoring Legacy Web Forms for Test Automation

Entity Framework: To the Unit of Work Design Pattern and Beyond
Entity Framework: To the Unit of Work Design Pattern and BeyondEntity Framework: To the Unit of Work Design Pattern and Beyond
Entity Framework: To the Unit of Work Design Pattern and BeyondSteve Westgarth
 
ALM@Work - Unit testing in Visual studio 2012
ALM@Work - Unit testing in Visual studio 2012ALM@Work - Unit testing in Visual studio 2012
ALM@Work - Unit testing in Visual studio 2012DomusDotNet
 
Patterns and practices for building enterprise-scale HTML5 apps
Patterns and practices for building enterprise-scale HTML5 appsPatterns and practices for building enterprise-scale HTML5 apps
Patterns and practices for building enterprise-scale HTML5 appsPhil Leggetter
 
ASP.NET MVC Best Practices malisa ncube
ASP.NET MVC Best Practices   malisa ncubeASP.NET MVC Best Practices   malisa ncube
ASP.NET MVC Best Practices malisa ncubeMalisa Ncube
 
Test Design for Fully Automated Build Architectures
Test Design for Fully Automated Build ArchitecturesTest Design for Fully Automated Build Architectures
Test Design for Fully Automated Build ArchitecturesMelissa Benua
 
Testing, a pragmatic approach
Testing, a pragmatic approachTesting, a pragmatic approach
Testing, a pragmatic approachEnrico Da Ros
 
Mobile App Architectures & Coding guidelines
Mobile App Architectures & Coding guidelinesMobile App Architectures & Coding guidelines
Mobile App Architectures & Coding guidelinesQamar Abbas
 
How to test models using php unit testing framework?
How to test models using php unit testing framework?How to test models using php unit testing framework?
How to test models using php unit testing framework?satejsahu
 

Similar a Refactoring Legacy Web Forms for Test Automation (20)

MVC Framework
MVC FrameworkMVC Framework
MVC Framework
 
Sitecore mvc
Sitecore mvcSitecore mvc
Sitecore mvc
 
Mvc
MvcMvc
Mvc
 
Entity Framework: To the Unit of Work Design Pattern and Beyond
Entity Framework: To the Unit of Work Design Pattern and BeyondEntity Framework: To the Unit of Work Design Pattern and Beyond
Entity Framework: To the Unit of Work Design Pattern and Beyond
 
Unit Testing in VS2012
Unit Testing in VS2012Unit Testing in VS2012
Unit Testing in VS2012
 
ALM@Work - Unit testing in Visual studio 2012
ALM@Work - Unit testing in Visual studio 2012ALM@Work - Unit testing in Visual studio 2012
ALM@Work - Unit testing in Visual studio 2012
 
Patterns and practices for building enterprise-scale HTML5 apps
Patterns and practices for building enterprise-scale HTML5 appsPatterns and practices for building enterprise-scale HTML5 apps
Patterns and practices for building enterprise-scale HTML5 apps
 
MVC architecture
MVC architectureMVC architecture
MVC architecture
 
QAorHighway2016
QAorHighway2016QAorHighway2016
QAorHighway2016
 
ASP.NET MVC Best Practices malisa ncube
ASP.NET MVC Best Practices   malisa ncubeASP.NET MVC Best Practices   malisa ncube
ASP.NET MVC Best Practices malisa ncube
 
Test Design for Fully Automated Build Architectures
Test Design for Fully Automated Build ArchitecturesTest Design for Fully Automated Build Architectures
Test Design for Fully Automated Build Architectures
 
Aspnet mvc
Aspnet mvcAspnet mvc
Aspnet mvc
 
Testing, a pragmatic approach
Testing, a pragmatic approachTesting, a pragmatic approach
Testing, a pragmatic approach
 
Webinar MVC6
Webinar MVC6Webinar MVC6
Webinar MVC6
 
Selenium
SeleniumSelenium
Selenium
 
Testing Angular
Testing AngularTesting Angular
Testing Angular
 
Asp.net,mvc
Asp.net,mvcAsp.net,mvc
Asp.net,mvc
 
Mobile App Architectures & Coding guidelines
Mobile App Architectures & Coding guidelinesMobile App Architectures & Coding guidelines
Mobile App Architectures & Coding guidelines
 
Mayur_Resume (2) (1)
Mayur_Resume (2) (1)Mayur_Resume (2) (1)
Mayur_Resume (2) (1)
 
How to test models using php unit testing framework?
How to test models using php unit testing framework?How to test models using php unit testing framework?
How to test models using php unit testing framework?
 

Último

Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...software pro Development
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsArshad QA
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024Mind IT Systems
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsAlberto González Trastoy
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplatePresentation.STUDIO
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providermohitmore19
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsAndolasoft Inc
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...Health
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfkalichargn70th171
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension AidPhilip Schwarz
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerThousandEyes
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech studentsHimanshiGarg82
 

Último (20)

Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...How to Choose the Right Laravel Development Partner in New York City_compress...
How to Choose the Right Laravel Development Partner in New York City_compress...
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 

Refactoring Legacy Web Forms for Test Automation

  • 1. REFACTORING LEGACY WEB FORMS FOR TEST AUTOMATION Author: Stephen Fuqua Last Revised: December 2014 All content by the author or from public domain sources unless otherwise noted
  • 3. Taking Over Legacy Code • Given you understand the value of test automation • Given you are handed a legacy application to maintain and enhance • Given the application is in ASP.Net Web Forms • When you try to add tests • Then you find that test-driven development is literally impossible.
  • 4. Web Forms Challenge • Testing ASP.Net Web Forms is problematic: • Tutorials show poor design, leading many developers to mix UI, business, and data access logic into a single class (the code behind). • ASP.Net functionality such as Session and ViewState are difficult to manipulate in an automated test. • Likewise, Web Forms architecture makes it difficult to access and manipulate form controls in tests. • Temptation: automate tests on the UI itself.
  • 5. Test Pyramid • In Succeeding with Agile, Mike Cohn describes a pyramid of automated tests: • Dominated by unit tests, and • Featuring service (system) tests that functionally integrate the units, and • Including just a few UI tests, to confirm that form fields connect to the services. UI Service Unit http://www.mountaingoatsoftware.com/blog/ the-forgotten-layer-of-the-test-automation- pyramid
  • 6. Right-siding the Pyramid • UI tests are brittle, expensive to write, and time consuming, to paraphrase Cohn. • With judicious refactoring, it is possible to continue using Web Forms and still achieve the goals of test automation and test-driven development • To overcome this challenge… • Use the Model-View-Presenter pattern, and • Introduce test isolation techniques.
  • 8. Model-View-Presenter • Model-View-Presenter, or MVP, is a specialized version of Model-View-Controller (MVC). • Split the traditional code behind into View and Presenter. View Presenter Model Code- Behind Business Logic
  • 9. Test Isolation Flow Chart http://www.safnet.com/writing/tech/2014/08/unit-test- isolation-for-legacy-net-code.html
  • 10. Refactoring • Start refactoring the code, carefully introducing isolation techniques in moving to MVP. • Sprouting – the code behind sprouts into model, view, and presenter. AKA Extract Method. • Adapters – write adapters for ASP.Net functionality that cannot be manipulated in unit tests. • Stubs & mocks – use interfaces and dependency injection properly, then apply stubs and mocks in the new unit test code.
  • 11. The Straw Man • To illustrate these techniques, I resurrected the code for www.ibamonitoring.org. • It is already split into web project and “back end” library for business logic and data access. • Contains unit and integration tests for the library, but none for the web project. • Originally used Microsoft Moles (now Fakes) to isolate some of the code for unit testing. • The app is sound, but patterns are used inconsistently.
  • 14. Adapters for Caching • Introduce adapters that wrap Session, Application, etc. • Side benefit: centralizes magic strings and casting from object to appropriate types. • Use lazy-loading for Property Injection, combined with Test Specific Subclasses, to allow production code to access real objects and tests to access fake objects.
  • 15. Example: An Adapter for Session • Original code already contained this UserStateManager in order to centralize magic strings. • It has now been refactored to an instance class with an interface that can be mocked.
  • 16. Using the Session Adapter • Adding the lazy-load to a base class. • Note the use of HttpSessionStateWrapper, which turns old Session into HttpSessionStateBase.
  • 17. Unit Testing the Adapter • Even an adapter can be unit tested… you’ll need a fake Session for that. One that doesn’t start the ASP.Net engine. • In other words, a Test Specific Subclass. • But Session is sealed. • Hence the use of HttpSessionStateBase, which is not sealed!
  • 18. Unit Testing the Adapter, cont.
  • 19. PART 4 Refactoring to Model-View-Presenter (MVP)
  • 20. The MVP Pattern • Model contains business logic or accesses business logic in services. • View contains properties and methods for accessing form controls and changing the display. • Presenter connects the two; all “UI logic” moves from View to Presenter. • Use Separation of Interfaces to facilitate testing the Presenter.
  • 21. Deconstructing the View • Move use of dependencies to the Presenter. • Create a property for each control that needs to be accessed by the UI logic in the Presenter. • And methods for behavior changes that the Presenter should invoke in the UI.
  • 22. Using the Presenter • Add the Presenter to the concrete View. • The view’s events make calls to the Presenter.
  • 24. Discussion • View’s interface and Presenter are still in the web project. • This example does not show behavior changes in the view. • This app’s Model is not well-constructed: • Presenter calls static methods that can’t be mocked. • Presenter is invoking business logic, not just UI logic – extract that into the Model.
  • 25. Evaluating the Presenter • Green – UI layer logic • Red – business logic • Yellow – extract to methods with validation • Business logic should return a modified SiteVisit object after performing inserts. • Just noticed – first line isn’t used! Remove GlobalMap from constructor.
  • 26. Business Logic – the Facade • For business logic, I prefer to create a Façade class that takes just a few arguments and hides the complexity of data access and manipulation. • The Façade itself can be injected into the Presenter’s constructor.
  • 29. Refactored Presenter • Accesses state. • Retrieves and validates form values. • Forwards values to the business layer. • Is fully testable.
  • 30. Result • Original code behind was impossible to test, netting 40 lines of untested code. • Now, code behind has: • Some untested properties – but low risk of defects. • Event handler with one new line of untested code. • A new constructor with one line of untested code. • The presenter, and the wrappers for Session and Application, are 100% unit testable.
  • 31. PART 5 Unit Testing the View / Introducing Dependency Injection
  • 32. Web Forms and Dependency Injection • Without dependency injection, I cannot test the View’s constructor or events. • There is a means available for setting up full dependency in Web Forms: an HttpModule. • … and an open source solution to setup Unity as an Inversion of Control (IoC) container: https://bitbucket.org/KyleK/unity.webforms • Likely there are similar packages for other IoC containers, but Unity is my current tool.
  • 33. Evaluating the View’s Constructor • Here is the updated constructor for the View, injecting the new Façade into the Presenter. • The presence of the View in the Presenter’s constructor introduces a circular dependency, thus preventing use of any IoC container. • View depends on Presenter depends on View
  • 34. Solution: Abstract Factory • A solution to this conundrum is a Factory class with methods to build the presenters. • The Factory can wrap the IoC container. • It can access session and app state from HttpContext.Current. • In order to unit test the factory, we’ll want to access state variables through lazy-loaded properties. • Be sure to keep the abstract factory in the web project. Discussion: http://odetocode.com/Articles/112.aspx
  • 36. Injecting View and State • To set the instance-specific view and state values resolving the presenter type, use the ResolverOverride parameter argument.
  • 37. Setup Dependency Injection • The installed package created class UnityWebFormsStart in the web project – add dependencies to this class. • Use Registration by Convention to auto-map the classes in the web project and library.
  • 38. Modify The View • Add the Factory as a constructor argument in the view / code behind file. • Call the Factory to create the Presenter.
  • 39. Unit Testing the View • We should be able to unit test the view’s constructor quite easily now • What about the event that calls the presenter? It has two more dependencies to isolate: • Page.IsValid – create adapter an lazy load. • Response.Redirect – lazy load an instance of HttpResponseBase, and create a TSS class. • Best to move these calls to the Presenter – skipping that for time’s sake right now.
  • 40. PageAdapter • Three commonly used properties to start with, can be expanded as needed. • Temporarily violating YAGNI principle, but it is a trivial and likely useful violation. • Page is not sealed and thus could be sub-classed for testing, but it simply isn’t worth it for 4 lines of code.
  • 41. Code Coverage • The entire web project is up to 7% coverage. • 25% uncovered in the Factory from lazy loading. • GlobalMap and UserStateManager are legacy – they can be tested now, but are not fully yet. • The View has 5.5% coverage, Presenter 100%.
  • 42. PART 6 Interlude – Toward MVC
  • 43. • The goal for this project is to automate tests, not to migrate the framework, but… • … in retrospection, the Presenter we’ve developed is definitely starting to look like a Controller from an ASP.Net MVC project. • The next step in test automation is to address service level testing – and that will be made cleaner by refactoring the Presenter to be very close to an MVC Controller. Refactoring the Presenter Interface
  • 44. • A Controller has direct access to HttpContext: • The Presenter is in the web project - we can use HttpContext.Current to access these values. • Controller actions accept form data, either as a set of variables or using model binding. • Use the View as the “model” (View Model) and pass to the action instead of to the constructor. • Validation should stay with the View Model. Gap Analysis Session Response Application Request
  • 45. • The lazy-loaded adapters were previously in a base class for ASPX pages. • Move to a base class for Presenters. • Leave out PageAdapters because those values belong in the View / ViewModel. Lazy Loading Adapters
  • 46. • Normally an MVC view model would be a concrete class, not an interface. • In this case, convenient to leave as an interface – if changing from Web Forms to MVC, then it will be trivial to change the interface to a concrete POCO. • Updated signatures: Convert to ViewModel
  • 47. • The project uses validation controls in the ASPX file - need to rely on Page.IsValid for validation. • For test automation, best to have the Presenter react to validation problems • Create an IsValid property in the view interface, and utilize it in the presenter. • Limitation – can’t test the validation details, only the Presenter’s response to invalid data. • Might not need PageAdapter at all now… View Model Validation
  • 48. • The code behind in the View has become much simpler – call the factory, then call the SaveConditions “action”, passing the View itself as the View Model. • What about the exception handling? In this case, it is ASPX specific and I will leave it alone. Updated View
  • 49. PART 6 Service Level Testing with SpecFlow
  • 50. • SpecFlow is a Visual Studio extension for writing business requirements / acceptance tests using the Gherkin language. • Using SpecFlow, we can add service-level tests that connect to the Presenter classes. • … and, when we’re ready to enhance the application, we can write new acceptance tests in a Behavior Driven Development mode. SpecFlow
  • 51. • As an IBA observer, I want to record the conditions for a site visit so that I can submit point count observations. • Try entering realistic data in all the fields – expect to go to the Point count page. • Try using end time less than start time – expect error. • Try leaving the form blank – expect error. User Story and Brief Confirmations
  • 52. • Assuming SpecFlow is installed, and you have a test project configured for MSTest*... • Create a Feature file called SiteConditions. • Modify the user story and scenario name. • I will remove the tag and customize the steps in the following slides. Add a Feature * Or leave with NUnit if you prefer
  • 53. • Since this test is driving a UI, input values include the available options for dropdowns controls. • We could initialize these through a Test Hook, or make the initialization transparent by including them the test definition. Happy Path – Setup Dropdowns
  • 54. • Fill in valid values. • Simulate pressing the Next button. • Confirm the expected page redirect. • And the unstated expectation that the submitted data will be saved into a database. Happy Path – Fill in Form, Submit It
  • 55. • Run the scenarios… • Not surprisingly, the scenarios fails to run: there is no connection between the scenario and our application code. Run the Scenario
  • 56. • Need to right-click and choose Generate Step Definitions. • Creates a step definition file that provides a template for linking the data and actions to the application code. Generate Step Definitions
  • 57. • The metadata values need to be inserted into the database – which brings us to… • As with any database-integrated tests, you’ll want to use a sandbox database. I will use the same LocalDB instance that I already created for stored procedure testing. • Make sure the test project’s app.config file is properly setup for data access. • Use an ORM for quick-and-easy backdoor data access (showing OrmLite here). Sandbox Database
  • 58. • Before the complete test run: setup database access. • Before each individual test: clear out all of the tables that will be used. Test Hooks
  • 59. • Now, edit the generated step definition template, reading the data from SpecFlow and writing into the database. • For convenience, cache a local copy of the objects and their Id values. Insert the Metadata
  • 60. Setup the View Model • The step “I have entered these values into the form” contains the View Model / form data. • Create a stub implementation of the View, and populate it using SpecFlow’s Assist Helpers. • Store the view model in a static variable for use in the Given step.
  • 61. • In order to call the Presenter without using ASP.Net, create stub implementations of IUserStateManager and HttpResponseBase. • Instantiate the Presenter using Unity and inject the stub objects. Call the Presenter
  • 62. • First validate the redirect. • Then use OrmLite again to validate that the actual data stored in the database matches the View Model. Evaluate the Results
  • 63. • Now we have a fully automated regression test of the “happy path” scenario for saving site conditions – using the entire system except for the ASPX page itself. • Each additional confirmation can be written as a new scenario in the same feature file. • When you re-use a Given, When, or Then phrase, you will have instant C# code re-use. • Note that the feature file is essentially a business requirements document. Service Test Wrap-Up
  • 65. Keys to Success • Split code behind into Model-View-Presenter. • Introduced adapters for ASP.Net classes. • Session • Application • Response • Introduced interfaces and a Factory class. • Added Unity for Web Forms to achieve DI. • Utilized SpecFlow for service-level tests.
  • 66. • The entire solution is available under the MIT license, hosted on GitHub. • Files of particular interest: • SiteConditions.aspx.cs • SiteConditionsPresenter.cs • PresenterFactory.cs • UserState.cs • SiteConditionsFacade.cs • SiteConditions.feature • SiteConditionsSteps.cs • TestHooks.cs Source Code
  • 67. Preview: Moving to MVC • Should be able to do something like this… 1. Create an MVC project. 2. Run the original application. Save the generated web pages as .cshtml pages. 3. Change the Id values, e.g. ctl00_contentBody_SiteVisitedInput to SiteVisitedInput (find and replace “ctl00_contentBody” with “”). 4. Move the Presenters to MVC and rename as XyzController. 5. Change View interfaces to concrete ViewModel classes. 6. Update the validation, e.g. with Fluent Validator.