Alexander Beletsky's Development Blog: 2010-11

You stucked? Stop and ask for help!

I've been working on one task for last several days. In a product I work there is feature that looks like Excel - template editor. User is able to put a data to sheets and do some simple calculation with formulas (SUM, MUL etc.). This functionality has been created far ago and worked fine. But recently we've changed our engine and now we support not only "plain" data structure, but expanded one - when user is able to say - "this column or row is expanded with such data". Data size is dynamic and could be predicted while template edition and SUM function have to take into account that data is expanded.

So, I've created a number of test cases that cover new functionality and implemented simple mechanism that process formula like SUM(A1:B2) and adjust start and finish indexes according to size of expansion. At the beginning that AdjustIndexes function were small and easy to read, it just perform simple calculation based on current row/current column, original indexes places and some information from expanded data.

But after a demo we've found some bugs: if several expansions present in template - it fails. If not expanded data comes after expanded it fails and some more..

I've extended tests cases with all found issues and start to attack problem. I start to adjust AdjustIndexes to work with new cases. And after a while I've completed it, but this function started to look scary - a lot of nested if-else cases, calculation of indexes, += operations etc. OK, I thought.. it could be refactored later, so I did a checkin. My happiness was not long, because while developers tests I just found another bug. OK, usual stuff - test and fix. Let's go.

Meanwhile I've already spent about 6 hours on that. At stand-up I said - "it seems to be alright, only one case is failing now, I'm finishing up and commit". But I was wrong. As I changed the function to work with new case, new case became green but existing ones became red.. I fell stress that task originally estimated for 1 day is now 1.5 and supposed to be about 2 days. I wanted to do quickly, so started to play a game - "Change something, re-run the tests if GREEN - you win!" (such approach actually works some time as soon as you got good test cases and double check the results). Already created function became more scary and what is worse - it does not satisfied all tests. I lost the control of it and only one I understand - "I do not now understand this function works now.. It does something wrong and I have to go with another idea to solve the problem".

It was about 10 hours spend on that. I started to lose the game, but just didn't want to give up. I need another idea, I need another approach.. I need additional data structures that would help to map original table indexes to expanded ones, I need to change the engine to fill those mapping, I need to change AdjustIndexes function to take mapping into account. I felt a little tired and stressed but proceed with coding like crazy.

14 hours passes, late evening, and I was finishing up my Mapping mechanism, while I was contacted by my teammate Carsten - "How is it going with SUM?". I've explained my issues, my approach and my status, but basically it was like - "It is not yet done". But he said that it seems like I'm doing something wrong and proposed another algorithm of doing this adjustment, with no other additional data structures. I probably was to tired to understand everything there, so he just said "Wait for 30 minutes, I'll do some code and show to?". I've been shocked, something I worked for 14 already he want to do in 0.5 hour? OK.

He sent his first solution. Hopefully it was only one function to change I could easily integrated and re-run all test cases I had so far. Most of them were failed at first try. We started a pair session and after several run/fix/test sessions it started to work! All tests are green! It works in application.. Function is quite small and easy to read.

Yeahh.. that was such great work (respect to Carsten) and so such shame on me. 15 hours.. vs 40 minutes.. 30 lines of code vs. 300.. one point of integration vs. many. I've been defeated.

When I finished it up and went a street to walk with dog, I've tried to find at lest something positive in all this situation. And you know what? I can up with one another feature to trackyt.net.

If you have a task in progress and you spend 12 (8, 6 or even 4) hours, task is became red and interacts with user:

  • Take a rest, re-think
  • Ask for help

That would prevent my situation somehow. It will try to say to you "Man, you probably stucked with that, ask some one to take a look".. This seems to be obvious, but if you are in hurry you are not just doing that hoping to handle everything by yourself. This is not good. I was happy about such idea and lesson learned, so back to home with good mood already :).

Have you been to such situation, do you think it's valuable if you have some stopwatch and notifier that would push you shoulder and say "Stop now ask for help!" ?

I've released my own product - Trackyt.net

I've not been blogging for awhile, not because I'm too lazy but because I've spent all the time for my own first release. And today I'm very happy, proud and excited to announce that Trackyt.net is now available online.

Trackyt.net is a simple time management application. I've chosen this idea while I was working on my self-study project, just because it is well known, something I can use for myself. Latter on I started to feel that I see a bigger picture, bigger product that I want to build, where time tracking is just a small part of. That time I created first draft of Product Roadmap and now I try to follow it. That was mainly been influenced how we work in my current company what tools and process we are using.

It is a spare time project. At the beginning I spent no more that 3-4 hours per week mainly on weekends. But last several month's when I've been approaching release dates I put almost all my free time on that.

Project is already on milestone 2, "Look & Feel Release" and supposed to be public. Nevertheless the time and effort it is still MVP. It's primary functionality could be described in one sentence: "give you task a name; add new task on dashboard; start timer as you started to work on this task and release timer as you stopped; submit task on server;". Originally I haven't planned to go in a public availability with that. It was just a playground for HTML/CSS, MVC 2, jQuery. So, even now it lacks some common site functionality as mail notifications, remember me and so on.

I were inspired by some other small projects that people releasing just for fun. I like that someone working to have own "footprint" in web having a product placed there. I like that way of learning the things not only from books but from practical exercises. I like to have something to care, plan, enhance, develop. I hope that I would be receiving feedbacks that might would make a product better.

It is open source. You can crawl the repository and find something interesting for you. It would be great if you fork it and push some changes. I continue to make it open sources as far as I can do.

Site design have been created by Sasha. She have never tried a web design before this project. We've started together with simple tutorials and articles, replicating some existing site designs, learning photoshop. I was happy to see how fast she delivered first results. Even if we usually have completely different vision on things we came to one result. So, during the project we also formed a nice team of developer and designer.

So, as soon as original idea was to learn something new, what have I learned so far? A lot I think:

  • Getting Real - methodology and recommendations by creators of Ruby on Rails, that fits my project perfectly.
  • ASP.net MVC 2 - I've got a great introduction to MVC2 with a lot of help of Steven Sanderson great book Pro ASP.NET MVC 2 Framework, Second Edition.
  • JavaScript/jQuery - this one became one of my favorite language and jQuery one of the favorite frameworks.
  • HTML/CSS - I've never done so much HTML/CSS before. I would not say I'm good on it, but I definitely improved.
  • UpperCut and Roundhouse - good tools that help you with versioning and deployment.
  • Moq, Automapper, JSon, REST and more :).

What should I do now? I would take one week rest and to refresh my eyes a bit. I would start with a planning next release and next sprints to work on. I hope to back for my normal blogging rhythm again as well as support a product blog. So, I still got something to do :). But today is the birthday of Trackyt.net and on weekend I would be definitely celebrating it!

Testing database and Test database

Testing the database is important. It is absolutely required then your DAL is simply a bunch of methods that do SQL against the DB. It still required to be done then you rely on some ORM (like Linq to SQL, NHibernate etc).

Testing database

By testing the database I mean unit tests that runs database operation tasks (create, update, delete, stored procedures calls) and test asserts that operation is being completed successfully.

Even the SQL is simple enough (like a SELECT or UPDATE) it have to be tested. More complex stuff, like JOIN, UNION etc. have to tested with much more care (meaning different scenarios, different input set etc.). As more complex query you have, as more complex test you should do. I personally trying to avoid complex SQL queries, because they difficult to read and support, but in many cases is the only way you can do.

Example of test case of DAL methods that runs SELECT statement:

https://gist.github.com/662906

Using ORM simplifies life a bit. You are working with objects, creation of new record in DB is just creation of new object and call InsertOnSubmit() method. In many cases you have to trust that framework does its job correctly and do not write the tests for framework, that is simple waste of time. But what usually happens is that you have a wrappers against ORM DataContext, like a Repository and the behavior of Repository have to be validated with tests (check out about repositories here). Typically repositories interface looks like that:

https://gist.github.com/662918

All interface methods and properties are covered by tests. Moreover, if you have repositories extensions that helps you to select record by Id, or do paging they are also part of testing. Please check how this IBlogPostsRepostitory is tested by this example - example.

What else about testing database you should know?

First of all, database tests might required some initial data to be put in database before each test start. It could be easily solved by using [TestSetup] and [TestTearDown] methods of Fixture. Bad side of this that you could not customize the data for particular test, because Setup/TearDown runs the same code that you could not parameterize. I prefer to use a static method (or methods) that is placed in TestFixture and called at the beginning of unit test. Please see SubmitTenBlogpostsToRepository from previous example.

Second at all, you have to care about test isolation some how. It means that previous tests results should not affect next test cases, each tests case have to leave the database in the same state as it enters it. There are different approaches how to do that, I like something like this. I'm having FixtureInit that does kind of initialization for any test case and it contains DbSetup instance in it. DbSetup itself holds DataContext as well as TransactionScope object. TransactionScope is a very nice way of handling implicit transactions. Inside DbSetup constructor we also could put some test data initialization, as it would be shared thought all tests. Dispose methods of the DbSetup disposes a transaction without committing it. Each test case body is placed in using (var fixture = new FixtureInit("http://localhost")), so any data that is placed (or deleted) from database during the tests would not affect the database then test is finished. It is simple and works good, please check out for implementation here.

The last thing - layout and naming of database tests. Database tests have to be separated from application unit tests. It a good way of doing the testing. Database tests are usually too slow and you do not need to re-run them after minor changes (except changes in DAL). That's why it is better to place DB tests to separate assembly. The name of assembly should contain *Database.Tests. It gives a clear understanding of assembly goal, as well as it will not be re-runned every time as you do build.bat by UppercuT

Test database

Test database is database against that database tests are running.. and typically Test Database == Developer Database, meaning the same database as developer tests the application is used for database unit tests.

It it bad in several reasons.

By doing developers testing you make database really "dirty". Putting new records in DB or deleting existing ones, doesn't matter. I usually do a small test that if I put new object(s) I check the count of objects in table after, like Assert.That(foundTasks.Count(), Is.EqualTo(2));. But if I Tasks table will have some data before, I'm not guaranteed that count will be 2, but Assert.That(foundTasks.Count(), Is.EqualTo(countOfObjectBeforeInsert + 2)); will be running OK. It works, but I don't really like it, because it mess up test with some not required details.

It is possible that some complex database tests, could leave database in inconsistency state during its failure (that is actually bad and you have to design the tests to prevent this happening.. but it is happening especially with no-ORM, no-Transactions DAL). Also, one bad surpise could be if database tests deletes some data required for development testing.

So, it should be Test Database != Developer Database, test database and developer database are different instances actually. Test database must be restored with save version of developers database, but in general it is empty and its state is not changed after testing complete.

I'm using RoundhousE to deploy database. As I was describing in previous post I use initdb.bat and resetdb.bat to initialize and reset developer database. I've just created 2 others that do exactly the same but for trackytest database, inittestdb.bat, resettestdb.bat. In the same time, app.confing of test project is pointed to trackytest (instead of trackydb before). So now tests are running on trackytest, but the application itself on trackydb.

It is very convenient to work like that.