Monday, February 8, 2016

That thing called testing


There is always a lot of discussions about testing in general, what to test (class, method, behavior, use case , feature, UI) ? when to test (after, before, during) ? how to do it ? like this one between DHH Martin Fowler and Kent Beck, which got a lot of attention, so i decided to make a blog series to discuss the different aspects.of testing, that summarize my thoughts and my experience, i will cover their different types, why, how, when ? what are the common problems related to tests in general that got some people away from it, then i will present what worked for me, i will try to be objective and pragmatic, hopefully you will find it helpful. So let's start by:

Why we do testing:

Mainly there's two reasons for testing :


Safety from regressions



Safety from regressions is very important to support and maintain the software during it’s lifetime, they represent the reference and source of truth as software evolve and change over the years and the teams and persons that worked on it changed too.

By experience i have noticed two major problems
-Adding more of these tests to support new behaviours and requirements usually become more and more difficult as time goes by, which pushes developers to abandon testing and reduces the coverage.
- Also the existing tests will become brittle, slow and a burden to maintain which of course increases the cost of maintenance for the software, or the customer and management will push the developers to do a compromise on quality in order to ship faster, which in fact shorten the life of software (in the long run). Then the cost of maintenance will be higher and higher, this is the point where the product is called legacy code and nobody wants to touch it anymore until it's dead (usually a rewrite) and the cycle starts over again.

Have you been in similar scenario ? I have ! This scenario happens very often in IT industry. With very bad consequences to the business, the employees that get their lives miserable with impossible deadlines, overtime, bugs popping from everywhere, every change causes different problems.

I don't think the tests themselves are bad but the way to do them and their relation to the design.is what caused these issues.

Some people thought the solution is to to take in consideration the testing from the start, Test first or TDD also as Michael Feathers in his famous book Working with legacy code define a good software as testable software, which bring us to the second reason for testing.

Drive design


To TDD or not to TDD that's the question !
TDD i think is the most problematic one, basically many of the problems that were mentioned by DHH and others that it leads to over-engineering a lot of mocks since when you start with tests you have nothing so you fake it until you make it :) it leads to too decoupled systems that loose cohesion ! Also the end result depends on the ability of the developer to refactor ! So let's ask the question: 
do TDD really lead to a good design ? i will let the father of TDD answer that :

So TDD help to spot a bad design and keep you away from it, not necessarily leads to good design, also the problem i see with TDD is that the process is well documented in Kent Beck's book but get deformed and corrupted over time.
Also how to tell if a design is good ? Michael Feather says a good design (software) is easy to test.

This is why i believe that we have to make test easy and build infrastructure to support and help testing the system without being tied to implementation detail. Tests should be easy to write so developers dont hesitate to write a lot of them. They should be relevant when they broke, they should reflect a defect introduced by the new code. Remember : "Changing code and tests in the same time is not refactoring !"

There is different ways to testing and i firmly believe that we should adapt the way we do testing to what we test. For example testing a business process is different on how we do testing to algorithm or when we build API. 

More about this will be the topic for next coming posts, i will talk about what worked and what didn't and summarize the best approaches to do testing ! Stay tuned

Tuesday, December 11, 2012

Use technologies as tools, don’t let them drive your design





Many problems on software development are due to which I call technology driven design obsession. Don’t let technologies and stacks dictate your design, keep it as simple as possible. Many developers try to use new technologies on their projects (because they are cool :P or have cool names like Marionette.Js I love that name) and forget about the business value and what they are trying to achieve. Instead of focusing on the use case and the goal of the application, they try to fit the problem to technology and suddenly they become slaves to these tools. 

Friday, August 17, 2012

Open / Close principle and Design Patterns



The open/ closed principle represents the second principle of SOLID design, in this post I will give an overview of this principle and how to achieve it using different design patterns (including some notes about the limits of each solution) then I will present a different way of achieving it using a less known pattern which is the marker interface combined with some dependency injection and auto-wiring.

The open/closed principle can be summarized by:
“Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.” Uncle Bob
As mentioned in an earlier post, good software is a software that can be adapted to a change easily. Change in requirement or functionality happen quiet often during the life of the software. Its ability to change and adapt to fulfill the new requirements is the key for its success otherwise the cost of maintenance and support as time goes by can cause its  death or even the death of the company (specially if it is a startup with limited resources).

The goal of this principle is to avoid cascading changes every time that we add a small change, making the chances to breaking existing code much smaller. Open for extension means that we can change the behaviour and extend the functionality of the module. Close for modifications means that the module can be changed but without touching the source code. At first look, this seems awkward, how can we change the functionality without touching the code?
This can be achieved using some known behavioural design patterns. I should note that it is hard to apply OCP at 100 % but at least we can make our code extensible and resilient for a certain type of changes, this depends on the context (TDD helps identifying these changes during the iterative process, more on this in a later post dedicated to TDD and how/when to apply design patterns ).

Traditional solutions:

Strategy Pattern:


This pattern is well documented and well known, the main idea is to separate the abstract generic algorithm from its implementation this allows a decoupling between the functionality (behaviour) and the class that uses the behaviour. This way we can add more functionality by implementing the Strategy interface then tell the context to use it.

Notes and limitations:

-I have seen some examples in books where they implement a method in the context to choose the strategy to use, depending on the context, personally if the choice of the strategy is a configuration concern i try to avoid this way since it implies some changes to the context and it can lead to many if-else statements which i hate, as alternative i like to push this responsibility to IOC container like structure map since he is responsible for managing the creation and the life cycle of  objects, this always depends on the context, for example if the selection of the right strategy depends on some internal processing inside the context then placing a method that choose the strategy inside the context makes sense but i always try to avoid if statements with some fashion :P).
-If the concrete strategies contains only one method they can be replaced by delegates instead of classes.
-I think we should pay attention when using Strategy pattern specially on how we select the strategy to use. If  the selection is inside the context class and each time we add a strategy we must change the code in the context than it is still open for modification and breaks the OCP.

Visitor Pattern:



In short, the visitor pattern allows to add functionality to classes without modifying them by implementing a class (visitor) that contains different implementations of the desired functionality.

Notes and limitations:

-It is clear that the visitor pattern fits well to OCP but in my point of view it is against POO encapsulation primciple which consists in encapsulating data and methods manipulating that data in the same class, but it is a trade off between adding a method for each element every time we need to add a functionality to a hierarchy or just derive a class from the visitor.
- Another drawback is that we need to add a visit method for each derived visitor class each time we add an element, which means all classes will be open for modification each time we derive from Element.

There is other patterns that helps to apply OCP like decorator, chain of responsibility, ...

Summary

For this post i tried to focus more on the limitations and drwabacks of each pattern as i think it is essential in order to make a good use. Design patterns are not magic they should be well understood and used carefully otherwise it would result in an over-complicated software, i will dedicate a post on how to use design patterns, their relatioin with TDD process.

For the next post i will write about a less known solution but very powerful based on marker interface with DI and Pub/Sub pattern (my favourite solution).
Stay tuned :)

Thursday, August 9, 2012

D.R.Y principle and the Broken clock




This will be a short post, it is more like taking note of a thought that crossed my mind.







         VS






 
Dry: Don't repeat yourself principle, a well known and preached principle.
We should pay attention how to apply "Dryness".
Depending on the context, for example the source code for create and update may be identical in many scenarios but at sometime the logic may change and they can be different, if you try to apply dry by trying to use the same code base for the two operations this can catch you later. they are the same by accident, but will likely vary by design later and should therefore not share code in common.


Remember: even a broken clock is right twice per day. (but it is still broken)

:)

Tuesday, July 24, 2012

General thoughts about TDD, BDD, SOLID and Agile


TDD

I still remember first time i heard about TDD, i was curious and a little suspicious; my first thought was how spending time and money  writing more code that doesn’t add functionality to the product could be beneficial???
First answer I come up with to this question was reducing maintenance cost, I mean it will cost probably more to create the product but on the long run it will be advantageous and we will get a good ROI (return on investment) but couldn’t we reduce the maintenance cost by usual unit tests? Aren’t they enough? Unit tests (test after) are less expensive, why should we create tests first which imply of course creating mocks, interfaces, fakes (which costs more time and more money).  To answer these question I decided to give it a try in a pet project, I fired  visual studio and I started (tried to start) coding.
Simple Remind of TDD steps:
1/ Write test
2/ Test should fail
3/ Write code to make test pass
4/ if test pass, start refactoring and clean the code than run tests of course


So the first step consists of writing a test that fails and it should focus on one feature and suddenly I started thinking differently, focusing only on the feature and the requirements, use cases, scenarios, trying to isolate this feature, trying to define its responsibility then define interfaces needed for mocking purposes and using IOC container and dependency injection. Repeating the steps 2,3,4 made me realize the difference between unit tests and the added value of TDD.
In fact doing TDD pushes the developer to focus on the requirement, and to produce high quality code that respects many practices and principles of good software design.
It pushes the developer to focus on the single responsibility of the feature and develop only the functionality needed to pass the test (avoiding YAGNI), using the mocks will force the design by contract (interfaces) and, the step of refactoring is very important trying to keep your code dry and probably using design patterns in order to make your tests pass and more resilient to change as adding more feature, tests and changes will affect precedent tests.
I think of TDD as way to automate producing a good code, maintainable and resilient to change but of course it requires some discipline and writing good tests as Roy Osherove said bad tests (specially false green tests) are worse than no tests at all, at least with no tests you don’t assume your code is correct.
TDD fits well with Agile since it is a repetitive and incremental process that focuses on single feature at a time. Logically related features can be grouped to represent the portion of the final product released in each iteration.

BDD

After TDD I heard about BDD, at first look from a developer perspective I found them similar
TDD
BDD
Arrange
Given
Act
When
Assert
Then

There is no big changes, personally I found BDD useful for workflows and business rules and process. It forces the developer to think about the functionality of the code not the code itself.
I will try to explain the difference with an example:
Let's take, Crediting an account scenario
-In TDD it will result in writing a code that tests focuses on the add operation rather than its context.
-In BDD it will result in a code to test crediting the account though hiding the technical details about the implementation, it focuses more on why the code is created and putting it in a context usually using ubiquitous language that can be understood by stakeholders, QA and Devs .
This is why i said from a developer perspective there is no difference but  in workflows and complex business operation having a lot of business rules BDD is more convenient. Usually workflows define the business and can be represented by a state machine. State machine can be represented by:
Given the actual state when an event happen then trigger action X and update state.
Also BDD can represent a living documentation describing business rules and processes. This is a huge advantage especially in DDD and complex applications with complex business rules. Greg Young have elaborated a simple testing framework that gives this magnificent output (note that this output is a result of a whole architecture and design decision (CQRS and ES) that made generating such results easy) :
Creating customer opens bill - Passed

Date Is(2011, 10, 6)

When: Create customer 'Northwind' with id 7

Expectations:
  [Passed] Created customer 7 'Northwind'
  [Passed] Opened bill 7/1 from 20111006

Imagine all the value you can get by this, developers can understand easily what is going on, completely understandable by stakeholders that can sign the acceptance based on this output. Even more, tests can be written by business team than passed to developers, BDD frameworks like Specflow generate the corresponding code for unit tests and then developers code the functionality.

Summary

Even if TDD contains the word test on it, it is really different from testing and unit testing, its goal is much more than ensuring regression, it pushes developers to code better and produces good software. As a successor BDD isn’t different from TDD it just abstract how functionality is coded and focus on why code is created, making this understandable by different actors in an agile Team. In the end it is important to master the art of unit testing and avoid bad tests as they are worse than no tests at all.

Sunday, July 15, 2012

What is a good software ???


By googling this question probably you will get this answer : it must be maintainable, flexible, extensible, scalable, testable, modular, reusable, loosely coupled,…

Personally i define a good software as a software that can be adapted to a change easily.
The change could be of any kind, like change of environment and platform (portability), change of the number of users, number of requests (scalable) and specially change in functionality or requirement (flexible, maintainable)…
As developers we have been always struggling to keep up to these changes specially when we are limited in time and budget which puts us under stress and make our lives miserable. Of course we can’t blame clients for changing their mind we always knew that this will happen and management guys can’t explain how a change cost more than rewriting everything from scratch.
In years many experts have established practices, development processes, and software development methodologies trying to solve this problem (TDD, Agile, SOLID, design patterns(GOF), YAGNI, KISS, DRY(don’t repeat yourself…), used wisely these practices can make our lives much better.

I will post about these different practices and show the relation between them and how they are used to get a good software.

 Stay tuned.









Friday, June 29, 2012

How to face the huge amount of changes in IT world (from a software developer perspective)



IT professionals, specially developers how are we supposed to deal with this huge amount of changes, new concepts, APIs, technologies… ???

1-Open mind: 
     The IT world is moving fast we should be open minded to accept changes. For example the old debate around ASP webforms VS ASP MVC  or  when Node.js came out many said that using javascript in both client side and server side is out of question (I don’t like programming in javascript either but it is not a reason to treat Node.js as a crap before even trying it).

We as developers we should know that there is no magic solution or silver bullet for everything, we should use the right tool ( i will use the word tool to represent concept, pattern, methodology, language,…) for the right job.

Simple example: personally I found Webforms more fitted to administrative pages than MVC, and MVC more suited for internet applications than Webforms. Node.Js is more suited for certain type of application like sending updates to user in real time (long-polling).

Even some design patterns may become useless over the time (depends on the context). 
Let's take the repository pattern  as example, we can find this pattern everywhere but did we really need it ??
This pattern came out before actual ORMs. ORMs are already based on this pattern so why adding additional abstractions specially for reads operations (make a controller that calls a data service layer that calls a repository data access object that calls an ORM that generate an sql query). Note, that  I’m not saying that the repository pattern is useless but we should ask the question if it is really needed (e.g if it is used only for data access and we don’t need change tracking we could use simple solution like Massive instead of NH or EF with all the added overhead). This leads as to the next point

2-Don’t follow blindly, always try to understand when to use a tool and more importantly when not to use it:
     Usually when a new tool* came out we found articles, blog posts and demos(usually with trivial examples that mislead the community of developers) showing how to use this tool and advantages of using this tool. But we notice that it is hard to find when not to use this tool (you should dig and have an objective critical mind). As I said earlier, we should use the right tool for the right job, in order to do that we should know when and when not to use it depending on the context.

3-Orchestrate altogether: how to use the right tool is good, combining all the tools together to get the right result is better and also harder. Sometimes the problem is not on the set of the tools or knowledge of how to use the tools but the challenge is how to combine them. This story from Udi Dahan (one of my favourite blogger)  illustrate what i meant.

4-Understand the context: in short use the right tool for the right job within the right context, some tools that applies in situation or project X doesn't mean that it is true in another situation or project Y even if it may seem similar. 

Another point, don't lie on assumptions specially for performance or concurrency problems. For example, you may use every optimization technique that you are aware of to reduce response time but the only way to be sure of that is by testing it and doing a stress test. (just because that is an optimization technique that worked for another project doesn't necessarily mean that it will optimize your current one at least you can't be 100% sure of it).

That is it for this post probably there is more but these are the main advices that i have in mind now and  that i think that are the most important to face the huge amount of  innovations on the IT world.