среда, 25 февраля 2015 г.

Briefly. About factories

In a certain sense following on the theme raised here - Briefly. About triggering the exceptions

Partly following on this theme - Factory Method

How can we create an object?

Of course, in this way:

type
 TmyObject = class
  public
   constructor Create(aSomeData: TSomeData);
 end;//TmyObject

...
var
 myObject : TmyObject;
...
 myObject := TmyObject.Create(aSomeData);

Or in this way:

type
 TmyObject = class
  protected
   constructor Create(aSomeData: TSomeData);
  public
   class function Make(aSomeData: TSomeData): TmyObject;
 end;//TmyObject

...
class function TmyObject.Make(aSomeData: TSomeData): TmyObject;
begin
 if IsValidData(aSomeData) then
  Result := Self.Create(aSomeData)
 else
  Result := nil;
end;
...
var
 myObject : TmyObject;
...
 myObject := TmyObject.Make(theConcreteData);

What is the difference?

The difference is that in "fabric method" certain business logic can be introduced.

In our case it is - IsValidData(aSomeData).

But we can go further:

interface
...
type
 TmyObject = class
  protected
   constructor Create(aSomeData: TSomeData);
   procedure SomeMethodToOverride; virtual;
  public
   class function Make(aSomeData: TSomeData): TmyObject;
 end;//TmyObject
...
implementation
...
 TmySpecialObject = class(TmyObject)
  protected
   procedure SomeMethodToOverride; override;
 end;//TmySpecialObject

...
class function TmyObject.Make(aSomeData: TSomeData): TmyObject;
begin
 if IsMySpecialData(aSomeData) then
  Result := TmySpecialObject.Create(aSomeData) 
 else
 if IsValidData(aSomeData) then
  Result := Self.Create(aSomeData)
 else
  Result := nil;
end;
...
var
 myObject : TmyObject;
...
 myObject := TmyObject.Make(theConcreteData);

And then more further:

interface
...
type
 TmyObject = class
  protected
   constructor Create(aSomeData: TSomeData);
   procedure SomeMethodToOverride; virtual;
  public
   class function Make(aSomeData: TSomeData): TmyObject;
 end;//TmyObject
...
implementation
...
 TmyNULLObject = class(TmyObject)
  protected
   procedure SomeMethodToOverride; override;
 end;//TmyNULLObject

 TmySpecialObject = class(TmyObject)
  protected
   procedure SomeMethodToOverride; override;
 end;//TmySpecialObject

...
class function TmyObject.Make(aSomeData: TSomeData): TmyObject;
begin
 if IsMySpecialData(aSomeData) then
  Result := TmySpecialObject.Create(aSomeData) 
 else
 if IsValidData(aSomeData) then
  Result := Self.Create(aSomeData)
 else
  Result := TmyNULLObject.Create(aSomeData);
end;
...
var
 myObject : TmyObject;
...
 myObject := TmyObject.Make(theConcreteData);

And even more further:

interface
...
type
 ImyInterface = interface
  procedure SomeMethodToOverride;
 end;//ImyInterface

 TmyObject = class(TIntefacedObject, ImyInterface)
  protected
   constructor Create(aSomeData: TSomeData);
   procedure SomeMethodToOverride; virtual;
  public
   class function Make(aSomeData: TSomeData): ImyInterface;
 end;//TmyObject
...
implementation
...
 TmyNULLObject = class(TIntefacedObject, ImyInterface)
  protected
   procedure SomeMethodToOverride;
   // - Of course, we no longer need override here
   constructor Create(aSomeData: TSomeData);
  public
   class function Make(aSomeData: TSomeData): ImyInterface;
   // - and here, generally, we can apply singleton
 end;//TmyNULLObject

 TmySpecialObject = class(TmyObject)
  protected
   procedure SomeMethodToOverride; override;
 end;//TmySpecialObject

...
class function TmyObject.Make(aSomeData: TSomeData): ImyInterface;
begin
 if IsMySpecialData(aSomeData) then
  Result := TmySpecialObject.Create(aSomeData) 
 else
 if IsValidData(aSomeData) then
  Result := Self.Create(aSomeData)
 else
  Result := TmyNULLObject.Make(aSomeData);
end;
...
var
 myObject : ImyInterface;
...
 myObject := TmyObject.Make(theConcreteData);

And that is all.

I hope somebody will like it.

Again I will mention it is only a “model”.

Oh!

One more brief remark.

How can we ensure that we have not “passed the factory”?

I mean ensure that not to call the “parasite default constructor” from TObject.

We can do it in this way:

type
 TmyObject = class
  protected
   constructor Create(aSomeData: TSomeData); overload;
  public
   class function Make(aSomeData: TSomeData): TmyObject;
   constructor Create; overload;
 end;//TmyObject
...
constructor TmyObject.Create;
begin
 Assert(false, 'We should call the factory method, and not the inherited constructor');
end;

We can also do it more “fun”, in this way:

type
 TmyObject = class
  protected
   constructor InternalCreate(aSomeData: TSomeData);
  public
   class function Make(aSomeData: TSomeData): TmyObject;
   procedure Create;
 end;//TmyObject
...
procedure TmyObject.Create;
begin
 Assert(false, 'We should call the factory method, and not the inherited constructor');
end;

In this case error code will not be compiled at all.

Probably, it can be done in this way:

type
 TmyObject = class
  protected
   constructor InternalCreate(aSomeData: TSomeData);
  public
   class function Create(aSomeData: TSomeData): TmyObject;
 end;//TmyObject

Discussion - https://plus.google.com/u/0/113567376800896602748/posts/gkD3a9D1HXp

вторник, 24 февраля 2015 г.

How to keep labor productivity up and not to lose self-respect

Original in Russian: http://18delphi.blogspot.ru/2013/04/blog-post_2.html

Every day, from the morning, going to work by bus, I set myself a TASK FOR A DAY. I have to make it.

It can be to:
1. Write a debugged class.
2. Fix an error.
3. Write a test.
4. Implement requirement specification.
5. Draw UML-diagram.
6. Write documentation.
7. Make RS more exact resulting in concrete artifacts.

At least something in the list. Or better – a few of them. Chatter in CQ and e-mail do not enter the list. As well as internet surfing. As well as idle talks at meetings. What is important is the RESULT, not the process.

These must be artefacts put in CVS or SVN.

If the day passed without it – I think it is spent in vain.

Fire And Motion.
http://www.joelonsoftware.com/articles/fog0000000339.html
http://www.joelonsoftware.com/articles/fog0000000056.html

Try it. May be you will like it.


понедельник, 23 февраля 2015 г.

What else I want to tell about TDD (not finished)

Original in Russian: http://programmingmindstream.blogspot.ru/2013/11/tdd_28.html
What else I want to tell about TDD

The previous series was here - http://programmingmindstream.blogspot.ru/2013/11/tdd.html

Many people I’ve communicated with about tests in general and about TDD in particular “crashed” on the following difficulty:

TestFirst. TestDriven.

I don’t want to “argue”, much less to disprove or criticize any of respected "theorists of TDD".nbsp;

I just want to describe my interpretation. Or – how I have understood it.

TDD says just about the following:

1. Launch ALL tests, make sure they has been done.
2. Add new test for new functionality. Make sure it has not been done.
3. Add functionality. Make sure the test has been done.

What question arises immediately?

This one: “How can I write a test for something that DOES NOT EXIST?”?

That is a FAIR question.

When I looked at TDD for the first time, I thought: - "what a bullshit" (that is an “epithet”, not an “foul term”);

How can I write test for something that DOES NOT EXIST? Let it be even NOT appropriate.

“A lady gave me a gift she had not,
And I received her gift I took not...”

I have been considering the matter for a long time and that is what I’ve decided.

There is NO TestFirst.

There is NO TestFirst.

In the FIRST place – there is a “draft” of the ARCHITECTURE. Neither code, nor tests, nor anything else, but – the “draft” of the ARCHITECTURE.

And TestFirst is made not for something “abstract”, that we’ll have “sometime later”, but for a VERY SPECIAL purpose.

 Described in the architecture and having legs growing from requirements specification (RS).

The simplest example:

 Let’s say that you need to create a “button” that brings new functionality.

Then, test can be as follows:

 AssureThatButtonExists('SomeButton');

And, OF COURSE, it won’t be done- until we add a button and will be done – WHEN we finally ADD it.

But! The test "KNOWS" that the button exists. At least, it knows its name. It means it doesn’t test “whatever”, but something certain. A part of architecture concepts. A BUTTON, described in requirements specification and in the architecture.

Let’s move on.

For example, we have to write TmyIntegerList class (I’ll analyze it in detail below).

What do we do?

We write:

“The design of the architecture” and a draft of the designed class.

EXACTLY these, but not the TEST.

Once again:

We write: “The design of the architecture” and a draft of the designed class.

EXACTLY these, but not the TEST.

Something like this:

type
 TmyIntegerList = class
  public
   procedure Add(anItem: Integer);
 end;//TmyIntegerList

procedure TmyIntegerList.Add(anItem: Integer);
begin
 Assert(false, 'Not implemented');
end;

Only AFTER THAT, we write TEST.

Once AGAIN – ONLY after we write TEST.

Let us say, of this kind:

procedure TmyIntegerListTest.ListAdd;
var
 l_List : TmyIntegerList;
begin
 l_List := TmyIntegerList.Create;
 try
  l_List.Add(47879);
 finally
  FreeAndNil(l_List);
 end;
end;

And this test – OF COURSE – WILL NOT BE DONE. And THEN, when we add IMPLEMENTATION of the method TmyIntegerList. Add – it will be done.

So.

There is no any TestFirst.

When speaking of "first" – the architecture and the design are PRIMARY, only after – the TESTS. And only after - the implementation.

So the chain of developing looks like this:
 RS -> Draft of architecture -> Test -> Code

Then, there are all possible options:
 RS -> Draft of architecture -> Test -> Code -> Test -> Code
 RS -> Draft of architecture -> Test -> Code -> Architecture -> Test -> Code
 RS -> Draft of architecture -> Test -> Code -> Architecture -> Test -> Code -> RS -> Architecture -> Test -> Code

and so on and so forth

First is - RS and the draft of the architecture – then begins – “iteration development”.

Especially delicious is that, as soon as we have written the test, we DON’T HAVE to think about “where can we check the functionability of our class”.

We have ALREADY configured all infrastructure to test it and to check.
EXACTLY this is what the word Driven in TDD means.

TESTS – HELP but not INTERFERE with the process of development.

The developer is “lead” by the tests.

Tests influence the code, and the code influences the tests.

In turn, these two TOGETHER influence the architecture and RS.

Most important is that if the tests “lead”, then, nevertheless, one way or another they are – written and they check the functionability of designed classes. It means there is NOT THE SLIGHTEST reason not to use them.

Even if you write something like this:

unit myListTests;

interface

uses
 TestFramework
 ;

type
 TmyIntegerListTests = class(TTestCase)
  published
   procedure ListAdd;
 end;//TmyIntegerListTests

implementation

procedure TmyIntegerListTests.ListAdd;
begin
 Assert(false, 'Not implemented');
end;

initialization
  TestFramework.RegisterTest(TmyIntegerListTests.Suite);

end.

Class TmyIntegerList is “kind of” not used, but it doesn’t mean that class TmyIntegerList doesn’t “exist in nature”. It exists, at least, “in your head”. If you’ve written something like TmyIntegerListTests.ListAdd, it MEANS, there is something having operation Add.

If you say to me “phew… the list of the integers… how banal… test for us the nuclear reactor…”

I’d answer - "yes, banal". But! “A journey of a thousand miles started with a first step”. And the “nuclear reactors” somewhere inside “consist of the lists of integers”.

If you’d like the REAL examples of the tests – I’ve got them. A WHOLE lot of them. I’m ready to show “in exchange for” your “at least one test”, so that to “SPEAK ON EQUAL TERMS” instead of bringing grist to the non-working mill.

Now, the remark about where, on my humble opinion, the notion of TestFirst is taken from and about “first you test what does not exist”.

It seems to me that the thing is everything comes from Java and JUnit, where reflection, duck-typing, injections and other “tricks” of this kind are used vastly.

There you can write “sort of” this test:

procedure TmyIntegerListTest.ListAdd;
var
 l_List : Object;
begin
 l_List := Framework.GetClassByName('TmyIntegerList').Create;
 try
  l_List.MethodByName.Execute('Add', [47879]);
 finally
  FreeAndNil(l_List);
 end;
end;

I am not a great expert on Java, but I hope the idea is clear.

Here, you get the “wag the dog” situation. We “kind of” know NOTHING about the tested class but, however, we can “test” it without writing it first. TestFirst. Sort of…

But let’s think – “do we really know nothing about the tested class?” OF COURSE, NO. We know its name and the fact that it has method Add.

TestFirst? Not on your life! ArchitectureFirst!!!

It does not matter that we have NOT written A SINGLE line of our class – we have ALREADY started to DESIGN its ARCHITECTURE, ONLY after – STARTED to write the test.

It does not matter that we have NOT written A SINGLE line of the code, except from the test, but! “The design of the architecture” – EXISTS when approaching with reflection or duck-typing. It EXISTS – AT LEAST “in our heads”.

But it EXISTS!

ArchitectureFirst!!!

I hope that my thought is understandable and denies all doubts about “How can I write test for something that does NOT EXIST?”

Not something that “does not exist”! But something that is being “developed and designed”. I hope it is clear.
And this does not mean that I “criticize” TDD. Quite the contrary - I am trying to do my best to ensure that as many as possible programmers are inspired with this idea.

I am trying to “deny doubts” and show that it is “not all that complicated”.

Now I will venture to give an example of “how I usually do”.

In the example I will keep to the MOST undesirable and pessimistic scenario – when either the customer or “Quality Control Group” ignore the questions of the developer.

So, the developer has either to “think up on his own”, or to write Asserts - http://18delphi.blogspot.ru/2013/04/blog-post.html

The whole code of the example is available in SVN here -  https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/Blogger/TDD/Chapter0/

Let us say, we have to write the same class TmyIntegerList.  

Let’s say, its specification is as follows:

TmyIntegerList is a list of integers.

Supports operations of:

1. Inserting an item.
2. Adding an item.
3. Deleting an item.
4. Getting the number of items.
5. Getting the value of the item by its index number.

This is “sort of” - RS.

Let’s write the “first draft” of the test:

program TDD;

uses
  Vcl.Forms,
  GUITestRunner,
  myListTests in 'Tests\myListTests.pas';

{$R *.res}

begin
 Application.Initialize;
 GUITestRunner.RunRegisteredTests;
end.

unit myListTests;

interface

uses
 TestFramework
 ;

type
 TmyIntegerListTests = class(TTestCase)
  published
   procedure ListAdd;
   procedure ListInsert;
   procedure ListDelete;
   procedure ListCount;
   procedure ListItem;
 end;//TmyIntegerListTests

implementation

procedure TmyIntegerListTests.ListAdd;
begin
 Assert(false, 'Not implemented');
end;

procedure TmyIntegerListTests.ListInsert;
begin
 Assert(false, 'Not implemented');
end;

procedure TmyIntegerListTests.ListDelete;
begin
 Assert(false, 'Not implemented');
end;

procedure TmyIntegerListTests.ListCount;
begin

 Assert(false, 'Not implemented');
end;


procedure TmyIntegerListTests.ListItem;
begin
 Assert(false, 'Not implemented');
end;

initialization
  TestFramework.RegisterTest(TmyIntegerListTests.Suite);
end.

-- it this draft we “kind of do not know” WHAT we test, but, in fact, the “draft of the architecture” is ALREADY sitting “in our head”. We know about the methods Add, Insert, Delete, Count and Item.

And, of course, the test is not done.

Let us state the “draft of the architecture” in the designed class.

Exactly – let us “put on paper” what “was in our brain” when we’ve been writing the first test.

Let’s describe the “prototype” of our class:

unit myIntegerList;

interface

type
 TmyIntegerList = class
  public
   type
    IndexType = Integer;
    ItemType = Integer;
  protected
   function pm_GetCount: IndexType;
   function pm_GetItem(anIndex: IndexType): ItemType;
  public
   procedure Add(anItem: ItemType);
   procedure Insert(anIndex: IndexType; anItem: ItemType);
   procedure Delete(anIndex: IndexType);
   property Count: IndexType
    read pm_GetCount;
   property Items[anIndex: IndexType]: ItemType
    read pm_GetItem;
 end;//TmyIntegerList

implementation

// TmyIntegerList

function TmyIntegerList.pm_GetCount: IndexType;
begin
 Result := -1;
 Assert(false, 'Not implemented');
end;

function TmyIntegerList.pm_GetItem(anIndex: IndexType): ItemType;
begin
 Result := -1;
 Assert(false, 'Not implemented');
end;

procedure TmyIntegerList.Add(anItem: ItemType);
begin
 Assert(false, 'Not implemented');
end;

procedure TmyIntegerList.Insert(anIndex: IndexType; anItem: ItemType);
begin
 Assert(false, 'Not implemented');
end;

procedure TmyIntegerList.Delete(anIndex: IndexType);
begin
 Assert(false, 'Not implemented');
end;

end.

And let’s modify the tests:

unit myIntegerListTests;

interface

uses
 TestFramework
 ;

type
 TmyIntegerListTests = class(TTestCase)
  published
   procedure ListAdd;
   procedure ListInsert;
   procedure ListDelete;
   procedure ListCount;
   procedure ListItem;
 end;//TmyIntegerListTests

implementation

uses
 System.SysUtils,
 myIntegerList
 ;

procedure TmyIntegerListTests.ListAdd;
var
 l_List : TmyIntegerList;
begin
 l_List := TmyIntegerList.Create;
 try
  l_List.Add(Random(1000));
 finally
  FreeAndNil(l_List);
 end;//try..finally
end;

procedure TmyIntegerListTests.ListInsert;
var
 l_List : TmyIntegerList;
begin
 l_List := TmyIntegerList.Create;
 try
  l_List.Insert(0, Random(1000));
 finally
  FreeAndNil(l_List);
 end;//try..finally
end;

procedure TmyIntegerListTests.ListDelete;
var
 l_List : TmyIntegerList;
begin
 l_List := TmyIntegerList.Create;
 try
  l_List.Delete(0);
 finally
  FreeAndNil(l_List);
 end;//try..finally
end;

procedure TmyIntegerListTests.ListCount;
var
 l_List : TmyIntegerList;
begin
 l_List := TmyIntegerList.Create;
 try
  l_List.Count;
 finally
  FreeAndNil(l_List);
 end;//try..finally
end;

procedure TmyIntegerListTests.ListItem;
var
 l_List : TmyIntegerList;
begin
 l_List := TmyIntegerList.Create;
 try
  l_List.Item[0];
 finally
  FreeAndNil(l_List);
 end;//try..finally
end;

initialization
  TestFramework.RegisterTest(TmyIntegerListTests.Suite);

end.

-- at this point, we already KNOW about the REAL designed class and use it.

Tests in this state SURELY will not be done.

Let’s move on.

Let’s implement at least one of the methods of our designed class.

Or rather – TWO of them, the simplest ones - Add and Count.

That’s how it looks like:

unit myIntegerList;
 
interface
 
type
 TmyIntegerList = class
  public
   type
    IndexType = Integer;
    ItemType = Integer;
  private
   type
    ItemsArray = array of ItemType;
  private
   f_Items : ItemsArray;
  protected
   function pm_GetCount: IndexType;
   function pm_GetItem(anIndex: IndexType): ItemType;
  public
   procedure Add(anItem: ItemType);
   procedure Insert(anIndex: IndexType; anItem: ItemType);
   procedure Delete(anIndex: IndexType);
   property Count: IndexType
    read pm_GetCount;
   property Item[anIndex: IndexType]: ItemType
    read pm_GetItem;
 end;//TmyIntegerList
 
implementation
 
// TmyIntegerList
 
function TmyIntegerList.pm_GetCount: IndexType;
begin
 Result := Length(f_Items);
end;
 
function TmyIntegerList.pm_GetItem(anIndex: IndexType): ItemType;
begin
 Result := -1;
 Assert(false, 'Not implemented');
end;
 
procedure TmyIntegerList.Add(anItem: ItemType);
begin
 SetLength(f_Items, Length(f_Items) + 1);
 f_Items[High(f_Items)] := anItem;
end;
 
procedure TmyIntegerList.Insert(anIndex: IndexType; anItem: ItemType);
begin
 Assert(false, 'Not implemented');
end;
 
procedure TmyIntegerList.Delete(anIndex: IndexType);
begin
 Assert(false, 'Not implemented');
end;
 
end.

-- we’ll see that two tests – have been DONE. Not necessarily correctly done, but they has been DONE.

Here we happen to deal with what is called Driven.

We’ve filled the “skeleton” of the prototype with the “flesh” and got a response from tests at once.

Let’s move on.

Let’s implement the other methods of our designed class:

unit myIntegerList;

interface

type
 TmyIntegerList = class
  public
   type
    IndexType = Integer;
    ItemType = Integer;
  private
   type
    ItemsArray = array of ItemType;
  private
   f_Items : ItemsArray;
  protected
   function pm_GetCount: IndexType;
   function pm_GetItem(anIndex: IndexType): ItemType;
  public
   procedure Add(anItem: ItemType);
   procedure Insert(anIndex: IndexType; anItem: ItemType);
   procedure Delete(anIndex: IndexType);
   property Count: IndexType
    read pm_GetCount;
   property Item[anIndex: IndexType]: ItemType
    read pm_GetItem;
 end;//TmyIntegerList

implementation

// TmyIntegerList

function TmyIntegerList.pm_GetCount: IndexType;
begin
 Result := Length(f_Items);
end;

function TmyIntegerList.pm_GetItem(anIndex: IndexType): ItemType;
begin
 Result := f_Items[anIndex];
end;

procedure TmyIntegerList.Add(anItem: ItemType);
begin
 SetLength(f_Items, Length(f_Items) + 1);
 f_Items[High(f_Items)] := anItem;
end;

procedure TmyIntegerList.Insert(anIndex: IndexType; anItem: ItemType);
begin
 if (anIndex = Self.Count) then
  Add(anItem)
 else
  Assert(false, 'Not implemented');
 // - I really don’t know what I am to do here since RS is kind of INCOMPLETE
end;

procedure TmyIntegerList.Delete(anIndex: IndexType);
begin
 if (anIndex < 0) OR (anIndex >= Self.Count) then
 // - there is really nothing to delete
  Exit
 else
  Assert(false, 'Not implemented');
end;

end.

We launch the tests, and what do we see?

ONE test – has NOT been done - TmyIntegerListTests.ListItem - AV has occurred there.

The reason is in TmyIntegerList.pm_GetItem method.

What are we to do?

Let’s rewrite method TmyIntegerList.pm_GetItem in this way:

function TmyIntegerList.pm_GetItem(anIndex: IndexType): ItemType;
begin
 if (Self.Count = 0) then
  // - I really don’t know what to do since RS does not specify it
  Result := Random(5676)
 else
  Result := f_Items[anIndex];
end;

We launch the tests and see – they have been DONE!

Have we done our work? "Probably". But not a fact.

Suppose, we have sent our application to “Quality Control Group” and it found the following:

var
 l_List : TmyIntegerList;
begin
 l_List := TmyIntegerList.Create;
 try
  l_List.Add(Random(54365));
  l_List.Delete(0);
 finally
  FreeAndNil(l_List);
 end;//try..finally
end;

And “opened the ticket in QC with number 1”.

After having “got the details” and the negotiations with QCG – we make sure that, however, there is an error.

We write the NEW TEST:

procedure TmyIntegerListTests.QCTicket1;
var
 l_List : TmyIntegerList;
begin
 l_List := TmyIntegerList.Create;
 try
  l_List.Add(Random(54365));
  l_List.Delete(0);
 finally
  FreeAndNil(l_List);
 end;//try..finally
end;

And we make sure that, however , it is NOT done!

What are we to do?

We edit the code of the designed class:

procedure TmyIntegerList.Delete(anIndex: IndexType);
begin
 if (anIndex < 0) OR (anIndex >= Self.Count) then
 // - there’s really nothing to delete
  Exit
 else
 if (anIndex = Self.Count - 1) then
  SetLength(f_Items, Self.Count - 1)
 else
  Assert(false, 'I really don’t know what am I to do here');
end;

We launch the tests - and they are done. Have we done our work? I DON’T KNOW. Probably…

Now let’s suppose, our QCG has found one more error:

... to be continued ...

Should I continue? Or the idea is clear?

----

Discussion - https://plus.google.com/u/0/113567376800896602748/posts/8pG2gJNkG7F

Once again about "testing levels"

Original in Russian: http://programmingmindstream.blogspot.ru/2014/01/blog-post_27.html

I have written a bit here - http://18delphi.blogspot.ru/2013/11/gui_9423.html

I will follow up on it.

Many say “oh, we have to test” (Many more say, of course, "there is no need in testing” :-) But this post is not for them).

Let’s ask ourselves - "what exactly is to be tested"?

I’ll try to pick out the "testing layers" and the "appropriate tools".

The choices are the following:

1. Testing of "base classes" and "data structure". The “bare” DUnit (http://dunit.sourceforge.net/) and "atomic tests" - http://18delphi.blogspot.ru/2013/04/blog-post_2326.html and http://programmingmindstream.blogspot.ru/2013/11/tdd_28.html  are appropriate for using here. Don’t forget about the “pre-” and “postconditions”.

2. Testing of classes with "parameterized entry" (entry mock's). Here it is appropriate to use DUnit add-in - http://18delphi.blogspot.ru/2013/04/blog-post_7108.html and also - "the etalons" - http://18delphi.blogspot.ru/2013/04/blog-post_8791.html
3. Testing of “user stories” (of “base classes”). Here it is appropriate to use this - http://roman.yankovsky.me/?p=1355. May be, one can also test “use cases”, but Roman has not yet developed this theme.

4. Testing of “separate components". Here this one is appropriate - http://programmingmindstream.blogspot.ru/2014/01/openctf-component-test-framework.html

5. Testing of “use cases". Here this one can be used - http://18delphi.blogspot.com/2015/03/gui-testing-11-gui-testing-in-spoken.html and http://18delphi.blogspot.ru/2013/11/gui-back-to-basics_22.html and http://18delphi.blogspot.ru/2013/11/gui-dunit.html

6. Testing of GUI-application as a “black” (“gray”) box (“honest” pressing the “buttons”). Here it is appropriate to use - TestComplete (http://smartbear.com/products/qa-tools/automated-testing-tools/ ). Do not forget about - http://programmingmindstream.blogspot.ru/2014/01/blog-post_626.html (in particular, it is written about “gray box” there).

7. Testing of GUI-application as a “true user” (we do not trust "bots”). What here? Here – MANUAL TESTING is used. "No tools"... The only tool is a sort of RequisitePro (http://www-03.ibm.com/software/products/ru/reqpro/ ) and writing HLTC (www.qualitytesting.info/forum/topics/low-and-high-level-test-cases?commentId=2064344%3AComment%3A427950&xg_source=activity  and http://pic.dhe.ibm.com/infocenter/rqmhelp/v2r0/index.jsp?topic=/com.ibm.rational.test.qm.doc/topics/c_testcases.html and http://www.sqaforums.com/showflat.php?Number=663587) for "live testers".

It is so somehow...

Should I develop the theme?

P.S. What else do I want to tell?

The PREFERENCE of tests increases when moving from item 7 to item 1.

Why?

Because the COMPLEXITY of tests increases in the REVERSE order - when moving from item 1 to item 7.

That is why (other things equal), one should first try to write a test of a “lower level” and then (if it “goes wrong”) – to increase the level.

ONE more (very important) EXEPTION – if “similar test” has ALREADY been WRITTEN. In this case, the testing level is – NOT IMPORTANT. You have to take the ALREADY EXISTING test and try to ADOPT it to the “new tested case”.

суббота, 14 февраля 2015 г.

Briefly. “Shooting the breeze”. The “practices” I use writing the code

Original in Russian: http://programmingmindstream.blogspot.ru/2014/12/blog-post_24.html
“Practices” – what an abstruse word.

But...

What I use is:

1. Inheritance
2. Virtual functions
3. Lambdas (when virtuality is not enough)
4. Pattern Publisher/Subscriber
5. Parameterized containers (Generic's or stl)
6. Interfaces
7. Mixins (when other practices are not enough)
8. The understanding of the fact that business logic SHOULD NOT depend on GUI

In general, that is ALL.
No “higher mathematics”.
Oh… Well, and also...

9. Pattern - Decorator

Why do I tell about all this?

For I’ve tried to persuade one person that my code is “as simple as ABC".

It is a code of a “craftsman” who knows three chords and plays on them.

No “higher mathematics”.

And yes, this is not a “thing to be proud of”.

I am just sharing.

Well, and also:

10. Tests. Just tests. "Always and everywhere".

пятница, 13 февраля 2015 г.

MindStream. Part 5. Testing

Original in Russian: http://habrahabr.ru/post/248027/

Table of contents

Hello, dear readership.

In this post I want to tell about the changes in our project and also about the technologies and methods we’ve used to achieve our goals.

Now our project looks as follows:



четверг, 5 февраля 2015 г.

Link. Why UML (for Delphi users)

"Delphi is actively used in company "Garant" together with UML. Sometimes someone consider RAD tools and UML-modeling as opposite technologies. Developers and architects in "Garant" do use a combination of UML-modeling, automatic code generation andDelphi - leading RAD tool. Let’s ask them how do they do this and why do they use UML.
Vsevolod Leonov (Seva)- product manager, Embarcadero Technologies.
Alexander Lulin (Alex) - developer lead, Garant.
Maxim Krylov (Max) - project manager, Garant
Company "Garant" is well known in Russia due to its key product - "Garant" system to provide information on Russian laws. Most of the company’s employees are developers. Their key product was started 23 years ago. The system has tens of millions of code lines, written in different programming languages. Today we’ll ask the leads how they have managed to support high quality of the product for this scale, language heterogeneity, production tempo, and requirements on stability. One of the key technologies aside from Delphi (as well as C++)  is UML.
Seva: did you start using UML from the beginning?
Max: No, we didn’t. When we started the system "Garamt", UML was not invented then. In about 1997 one of our young developer (we all were pretty young that time) brought a diskette with one of the first UML modeling tool. We think that was a beginning of UML in our company. But it took years to start an effective use of UML as a full integrated part of the process.
Alex: Moreover, bringing UML into development was long and in some steps. But as the complexity of our software grew, we needed more and more some tool to manage the code and architecture of the system."

More - http://blogs.embarcadero.com/vsevolodleonov/2013/08/12/why-uml-for-delphi-users/

MindStream. How we develop software for FireMonkey. Part 4. Serialization


Table of contents
Original in russian: http://habrahabr.ru/post/245441/

When I became enthusiastic about programming I liked working with files. Actually, the work was mainly to read the inputs and test records. Then I worked with databases and used files less often (except for IniFile sometimes). That is why I was rather interested in serialization.

Today I’ll tell you about how we’ve added serialization to our application, about the difficulties we’ve had and how we’ve overcome them. This material is not new, so it would more likely appeal to beginners. Though, anyone would be able to learn criticize some methods.

How to find out the true size of the memory allocated for by GetMem

Original in russian: http://18delphi.blogspot.ru/2013/04/getmem.html

Haven’t you thought that FreeMem does NOT REQUIRE this size? This means it somehow “knows” it.

It is quite simple:
function _l3MemorySize(aP: Pointer): Longint; 
 
{$IFDEF XE}
const
  {----------------------------Block type flags---------------------------}
  {The lower 3 bits in the dword header of small blocks (4 bits in medium and
   large blocks) are used as flags to indicate the state of the block}
  {Set if the block is not in use}
  IsFreeBlockFlag = 1;
  {Set if this is a medium block}
  IsMediumBlockFlag = 2;
  {Set if it is a medium block being used as a small block pool. Only valid if
   IsMediumBlockFlag is set.}
  IsSmallBlockPoolInUseFlag = 4;
  {Set if it is a large block. Only valid if IsMediumBlockFlag is not set.}
  IsLargeBlockFlag = 4;
  {Is the medium block preceding this block available?}
  PreviousMediumBlockIsFreeFlag = 8;
  {Is this large block segmented? I.e. is it actually built up from more than
   one chunk allocated through VirtualAlloc? (Only used by large blocks.)}
  LargeBlockIsSegmented = 8;
  {The flags masks for small blocks}
  DropSmallFlagsMask = -8;
  ExtractSmallFlagsMask = 7;
  {The flags masks for medium and large blocks}
  DropMediumAndLargeFlagsMask = -16;
  ExtractMediumAndLargeFlagsMask = 15;
{------------------------------Private types------------------------------}
type
 
  {Move procedure type}
  TMoveProc = procedure(const ASource; var ADest; ACount: NativeInt);
 
  {-----------------------Small block structures--------------------------}
 
  {Pointer to the header of a small block pool}
  PSmallBlockPoolHeader = ^TSmallBlockPoolHeader;
 
  {Small block type (Size = 32 bytes for 32-bit, 64 bytes for 64-bit).}
  PSmallBlockType = ^TSmallBlockType;
  TSmallBlockType = record
    {True = Block type is locked}
    BlockTypeLocked: Boolean;
    {Bitmap indicating which of the first 8 medium block groups contain blocks
     of a suitable size for a block pool.}
    AllowedGroupsForBlockPoolBitmap: Byte;
    {The block size for this block type}
    BlockSize: Word;
    {The minimum and optimal size of a small block pool for this block type}
    MinimumBlockPoolSize: Word;
    OptimalBlockPoolSize: Word;
    {The first partially free pool for the given small block. This field must
     be at the same offset as TSmallBlockPoolHeader.NextPartiallyFreePool.}
    NextPartiallyFreePool: PSmallBlockPoolHeader;
    {The last partially free pool for the small block type. This field must
     be at the same offset as TSmallBlockPoolHeader.PreviousPartiallyFreePool.}
    PreviousPartiallyFreePool: PSmallBlockPoolHeader;
    {The offset of the last block that was served sequentially. The field must
     be at the same offset as TSmallBlockPoolHeader.FirstFreeBlock.}
    NextSequentialFeedBlockAddress: Pointer;
    {The last block that can be served sequentially.}
    MaxSequentialFeedBlockAddress: Pointer;
    {The pool that is current being used to serve blocks in sequential order}
    CurrentSequentialFeedPool: PSmallBlockPoolHeader;
{$ifdef UseCustomFixedSizeMoveRoutines}
    {The fixed size move procedure used to move data for this block size when
     it is upsized. When a block is downsized (which usually does not occur
     that often) the variable size move routine is used.}
    UpsizeMoveProcedure: TMoveProc;
{$else}
    Reserved1: Pointer;
{$endif}
{$if SizeOf(Pointer) = 8}
    {Pad to 64 bytes for 64-bit}
    Reserved2: Pointer;
{$ifend}
  end;
 
  {Small block pool (Size = 32 bytes for 32-bit, 48 bytes for 64-bit).}
  TSmallBlockPoolHeader = record
    {BlockType}
    BlockType: PSmallBlockType;
{$if SizeOf(Pointer) <> 8}
    {Align the next fields to the same fields in TSmallBlockType and pad this
     structure to 32 bytes for 32-bit}
    Reserved1: Cardinal;
{$ifend}
    {The next and previous pool that has free blocks of this size. Do not
     change the position of these two fields: They must be at the same offsets
     as the fields in TSmallBlockType of the same name.}
    NextPartiallyFreePool: PSmallBlockPoolHeader;
    PreviousPartiallyFreePool: PSmallBlockPoolHeader;
    {Pointer to the first free block inside this pool. This field must be at
     the same offset as TSmallBlockType.NextSequentialFeedBlockAddress.}
    FirstFreeBlock: Pointer;
    {The number of blocks allocated in this pool.}
    BlocksInUse: Cardinal;
    {Small block pool signature. Used by the leak checking mechanism to
     determine whether a medium block is a small block pool or a regular medium
     block.}
    SmallBlockPoolSignature: Cardinal;
    {The pool pointer and flags of the first block}
    FirstBlockPoolPointerAndFlags: NativeUInt;
  end;
 
  {Small block layout:
   At offset -SizeOf(Pointer) = Flags + address of the small block pool.
   At offset BlockSize - SizeOf(Pointer) = Flags + address of the small block
   pool for the next small block.
  }
 
  {------------------------Medium block structures------------------------}
 
  {The medium block pool from which medium blocks are drawn. Size = 16 bytes
   for 32-bit and 32 bytes for 64-bit.}
  PMediumBlockPoolHeader = ^TMediumBlockPoolHeader;
  TMediumBlockPoolHeader = record
    {Points to the previous and next medium block pools. This circular linked
     list is used to track memory leaks on program shutdown.}
    PreviousMediumBlockPoolHeader: PMediumBlockPoolHeader;
    NextMediumBlockPoolHeader: PMediumBlockPoolHeader;
    {Padding}
    Reserved1: NativeUInt;
    {The block size and flags of the first medium block in the block pool}
    FirstMediumBlockSizeAndFlags: NativeUInt;
  end;
 
  {Medium block layout:
   Offset: -2 * SizeOf(Pointer) = Previous Block Size (only if the previous block is free)
   Offset: -SizeOf(Pointer) = This block size and flags
   Offset: 0 = User data / Previous Free Block (if this block is free)
   Offset: SizeOf(Pointer) = Next Free Block (if this block is free)
   Offset: BlockSize - 2*SizeOf(Pointer) = Size of this block (if this block is free)
   Offset: BlockSize - SizeOf(Pointer) = Size of the next block and flags
 
  {A medium block that is unused}
  PMediumFreeBlock = ^TMediumFreeBlock;
  TMediumFreeBlock = record
    PreviousFreeBlock: PMediumFreeBlock;
    NextFreeBlock: PMediumFreeBlock;
  end;
 
  {-------------------------Large block structures------------------------}
 
  {Large block header record (Size = 16 for 32-bit, 32 for 64-bit)}
  PLargeBlockHeader = ^TLargeBlockHeader;
  TLargeBlockHeader = record
    {Points to the previous and next large blocks. This circular linked
     list is used to track memory leaks on program shutdown.}
    PreviousLargeBlockHeader: PLargeBlockHeader;
    NextLargeBlockHeader: PLargeBlockHeader;
    {The user allocated size of the Large block}
    UserAllocatedSize: NativeUInt;
    {The size of this block plus the flags}
    BlockSizeAndFlags: NativeUInt;
  end;
 
 
{---------------------------Private constants-----------------------------}
const
  {The size of the block header in front of small and medium blocks}
  BlockHeaderSize = SizeOf(Pointer);
  {The size of a small block pool header}
  SmallBlockPoolHeaderSize = SizeOf(TSmallBlockPoolHeader);
  {The size of a medium block pool header}
  MediumBlockPoolHeaderSize = SizeOf(TMediumBlockPoolHeader);
  {The size of the header in front of Large blocks}
  LargeBlockHeaderSize = SizeOf(TLargeBlockHeader);
 
function _l3MemorySize(aP: Pointer): Longint;
var
lBlockHeader: Cardinal;
LPSmallBlockType: PSmallBlockType;
LOldAvailableSize: Cardinal;
begin
if (aP = nil) then
  Result := 0
else
begin
{Get the block header: Is it actually a small block?}
  LBlockHeader := PNativeUInt(PByte(aP) - BlockHeaderSize)^;
  {Is it a small block that is in use?}
  if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
  begin
    {----------------------------Small block------------------------------}
    {The block header is a pointer to the block pool: Get the block type}
    LPSmallBlockType := PSmallBlockPoolHeader(LBlockHeader).BlockType;
    {Get the available size inside blocks of this type.}
    Result := LPSmallBlockType.BlockSize - BlockHeaderSize;
  end
  else
  begin
    {Is this a medium block or a large block?}
    if LBlockHeader and (IsFreeBlockFlag or IsLargeBlockFlag) = 0 then
    begin
     Result:= (LBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize;
    end
    else
    begin
      {Is this a valid large block?}
      if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag) = 0 then
      begin
        {-----------------------Large block------------------------------}
        {Get the block header}
        //LBlockHeader := PNativeUInt(PByte(aP) - BlockHeaderSize)^;
        {Subtract the overhead to determine the useable size in the large block.}
        Result := (LBlockHeader and DropMediumAndLargeFlagsMask) - (LargeBlockHeaderSize + BlockHeaderSize);
      end
      else
      begin
        {-----------------------Invalid block------------------------------}
        {Bad pointer: probably an attempt to reallocate a free memory block.}
        Result := 0;
      end;
    end;
  end;
end;
end;
{$ELSE XE}
const
  cThisUsedFlag = 2;
  cPrevFreeFlag = 1;
  cFillerFlag   = Integer($80000000);
  cFlags        = cThisUsedFlag or cPrevFreeFlag or cFillerFlag;
 
type
  PUsed = ^TUsed;
  TUsed = packed record
    sizeFlags: Integer;
  end;//TUsed
 
function _l3MemorySize(aP: Pointer): Longint; 
begin
 if (aP = nil) then
  Result := 0
 else
  Result := PUsed(PAnsiChar(aP)-SizeOf(TUsed)).sizeFlags and not cFlags - sizeof(TUsed);
//  Result := (PLong(Long(aP) - 4)^ AND not cFlags) - 4;
end;
{$EndIf XE}

Why do we need it? You’d ask… So that not to store Capacity. We’re being “penny wise”. May be I will get to the theme “my micro-STL for Delphi”, where it would be highlighted.

Disclaimer. This code was written not only by me, it was taken from standard library. The colleague helped me to make it out. If I infringe somebody’s copyright, I’ll delete this post.