четверг, 28 апреля 2011 г.

Lazy

Recently a Lazy<T> class was introduced in TP in Tp.Utils namespace (Tp.Utils project). This is a helper for lazy initialized variables, similar to .NET 4 Lazy<T>, but more simple and not thread-safe.

The public API of Lazy<T> consists of:
public static class Lazy
{
 public static Lazy<TCreate<T>(Func<T> loader);
}

public class Lazy<T>
{
 public Lazy(Func<T> loader);
 public T Value { get; }
 public static implicit operator T(Lazy<T> lazy);
} 
 
First static class is a helper class to simplify Lazy<T> creation.

Example:

Lazy<Uri> lazyUri = Lazy.Create(() => HttpContext.Current.Request.Url);

Uri value = lazyUri.Value;
Uri casted = lazyUri;
 
The highlighted function would be called only whether the Value property will be used or lazy variable will be converted to its generic type.

After .NET 4 migration the Lazy<T> will be replaced with CLR .NET Lazy<T>.

среда, 27 апреля 2011 г.

Functional Tests Migration from Selenium to WebDriver

Today Anna and me spent a few hours on integration WebDriver into our Functional Tests.

There are numerous advantages of using WebDriver over traditional Selenium:

  • Fluent API. Who used WatiN may understand why it's so precious feature!
  • Native support the most browsers: Chrome, Firefox and Internet Explorer. Native support means -- no js scripts involved into browser controlling! All DOM manipulations are claimed to be done by native browser APIs, so tests can be performed even faster than now (though have not estimated it yet
  • There is also a special web driver - HtmlUnitDriver which can perform in memory tests with no open browser (the most speedy driver ever!)
  • No separate selenium*.jar package need to be run!

Today we done the first part so far: extracted test context preparation (database initialization, business layer preparations etc) into separate class and made possible implement tests without necessity to derive from FunctionalBaseTest.

Tomorrow will write a bunch of first tests!

PS

There is an interesting project -- Web Driver Extensions. Will try it out tomorrow :)

вторник, 26 апреля 2011 г.

Technical Debt

When it comes time to pay down your technical debt, don't be afraid to break stuff. It's liberating, even energizing to tear down code in order to build it up stronger and better than it was before. Be brave, and realize that paying your technical debt every so often is a normal, necessary part of the software development cycle to avert massive interest payments later. After all, who wants to live forever?

http://www.codinghorror.com/blog/2009/02/paying-down-your-technical-debt.html

пятница, 22 апреля 2011 г.

5 Lessons We’ve Learned Using AWS

http://techblog.netflix.com/2010/12/5-lessons-weve-learned-using-aws.html

"When I look back at what the team has accomplished this year in our AWS migration, I’m truly amazed. But it didn’t always feel this good. AWS is only a few years old, and building at a high scale within it is a pioneering enterprise today. There were some dark days as we struggled with the sheer size of the task we’d taken on, and some of the differences between how AWS operates vs. our own data centers."

Extension methods

There are numbers of general purpose extensions methods in TP:
System.StringExtensions, Tp.Utils

string Fmt(this string format, params object[] args) 
This is a shortcut for string.Format() method:
"Hi, {0}! Today is {1}".Fmt(name, DateTime,Now);
 
T ParseEnum<T>(this string name)
bool TryParseEnum<T>(this string name, out T result)
Generic versions (exception-ful and exception-free) version of Enum.Parse.
MyEnum enumValue = "EnumValue".Parse<MyEnum>();
if ("Invalid Enum".TryParse<MyEnum>(out enumValue))
{
}

System.Linq.EnumerableExtensions, Tp.Utils

string ToString<T>(this IEnumerable<T> source, string separator)
string ToString(this IEnumerable source, string separator)
 
Similar to "string.Join(string separator, string[] array)"
string ToSqlString<T>(this IEnumerable<T> ids)
Returns either "( null )" for null or empty collection or smth like "(1,2,3)"
bool IsOrdered<T>(this IEnumerable<T> items)
bool IsOrdered<T>(this IEnumerable<T> items, Comparer<T> comparer)
bool IsOrdered<T>(this IEnumerable<T> items, Comparison<T> comparison)
Checks whether a collection is ordered. Versions with Comparer and Comparison are also available (for custom comparison or inverted order).
IEnumerable<TConcat<T>(this IEnumerable<T> items, T item)
 Shortcut for .Concat(new [] {item})
 
void ForEach<T>(this IEnumerable<T> items, Action<T> action)
Similar to List.ForEach()
IEnumerable<TResultZip<T1T2TResult>(this IEnumerable<T1> first, IEnumerable<T2> second, Func<T1,T2TResult> selector)
See http://msdn.microsoft.com/en-us/library/dd267698%28VS.100%29.aspx
bool Empty<T>(this IEnumerable<T> enumerable)
bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable) 
Pretty obvious.

понедельник, 18 апреля 2011 г.

[Email Plugin Development Tale] Spec details


The devil is in details...

A week ago there was a feature described this way:

Given project 1
And requester with email 'sender@company.com'
And sender email is 'sender@company.com'
And profile has rules:
|Rule |
|then attach to project 2|
|then attach to project 1|
When the email arrived
Then the message should be attached to project 1

I sat down and implemented it, marked bug as 'Coded' and passed to QA.

Today Anastasia, our QA, come to me and said that this bug is still reproducible.
After short revising the specification was updated to:

A week ago there was a feature described this way:

Given project 1
And deleted project 2
And requester with email 'sender@company.com'
And sender email is 'sender@company.com'
And profile has rules:
|Rule |
|then attach to project 2|
|then attach to project 1|
When the email arrived
Then the message should be attached to project 1

You see? In first spec project was not actually created, in the second spec project exists in the system as deleted.

Just one line of code changed everything! And finally we've got a failed spec on "then the message should be attached to project 1" and it was easily fixed.

The conclusion is: don't underestimate your specs! Look through them carefully! :)

пятница, 15 апреля 2011 г.

NBehave: share context between action steps

Foreword
Some terms before we started.

Specification (AKA Spec) - describes a useful product feature which is supplied by several Scenarios

Context - is a System Under Test (SUT) state. Action Steps in general are actions to manipulate and validate Context

Main Part

A couple of days ago there was a private talking in the kitchen in the office.

We were discussing our approach of writing specifications.

Usually it comes with one solid class:


The problems with it are:
  • Specs class is too huge and often became unmanagable
  • We can't extract action steps into different classes as they refer to class instance fields so we can't reuse actions steps across specs
After some discussion we refactored it to the following model:



You see: our specs from now are absolutely independent on where Action Steps either Context located! That's a great benefit!

After a small refactoring in we've got 3 clearly separated classes: Specification, Action Steps and Context.

See some code below. Full examples can be found in US#33279, Plugins/Tp.Integration.Plugin.PopEmailIntegration folder

Specification

[TestFixture]

public class EmailProcessingSagaSpecs

{

[Test]

public void ShouldSkipAttachmentToDeletedProject()

{

@"Given project 1

And requester with email 'sender@company.com'

And sender email is 'sender@company.com'

And profile has rules:

|Rule |

|then attach to project 2 |

|then attach to project 1 |

When the email arrived

Then the message should be attached to project 1"

.Execute(In.Context<EmailProcessingSagaActionSteps>());

}

Action Steps

[ActionSteps]

public class EmailProcessingSagaActionSteps

{

[BeforeScenario]

public void BeforeScenarioInit()

{

ObjectFactory.Initialize(x => { });

}

#region Action Steps

private static EmailProcessingSagaContext Context

{

get { return ObjectFactory.GetInstance<EmailProcessingSagaContext>(); }

}

[Given("project $projectId")]

public void AddProject(int projectId)

{

var projectStorage = Context.Storage.Get<ProjectDTO>();

projectStorage.Add(new ProjectDTO {ProjectID = projectId});

}

Context

public class EmailProcessingSagaContext : PopEmailIntegrationContext

{

private readonly EmailMessage _emailMessage;

private readonly IList<AttachmentDTO> _attachments = new List<AttachmentDTO>();

public EmailProcessingSagaContext()

{

//Self-register Context in object factory in order to ensure single context instance during specs execution

ObjectFactory.Configure(x => x.For<EmailProcessingSagaContext>().Use(this));

_emailMessage = new EmailMessage {FromDisplayName = "John Smith"};

}

public EmailMessage EmailMessage

{

get { return _emailMessage; }

}

public IList<AttachmentDTO> Attachments

{

get { return _attachments; }

}

четверг, 14 апреля 2011 г.

How eBay migrates their systems

We just talked yesterday about how to migrate our new plugin framework. Here is good talk related to the problems we are trying to solve: Randy Shoup on Evolvable Systems.


Databases of big distributed systems like eBay and TargetProcess :-) have to evolve. The schema tends to be not just fixed where each column in the table represents a property in the business logic. But uses "key/values" to store bunches of properties and attributes. This approach is update friendly.

"Dual write" application migration
To migrate a system with zero downtime you need to split all your changes into small ones and guarantee that each change can easily be rolled back. In case when you need to migrate code and schema at once you should design your application the way it will dual write all data changes to both old and new databases. In this case you can downgrade your application to old version if needed.