Oren Eini

CEO of RavenDB

a NoSQL Open Source Document Database

Get in touch with me:

oren@ravendb.net +972 52-548-6969

Posts: 7,565
|
Comments: 51,184
Privacy Policy · Terms
filter by tags archive
time to read 1 min | 90 words

What happen if you are doing something like this in ASP.Net:

File.WriteAllText("fun.txt", "Foo Fum");

On my machine, it goes to: c:\windows\system32\inetsrv\fun.txt

Remember that the current directory in ASP.Net is wherever the server process has decided it should be, if you want the directory that your application is on, use AppDomain.Current.BaseDirectory for it.

time to read 4 min | 767 words

I've started looking at writing integration tests for an ASP.Net application. I have a farily complex application with quite a bit happening in the UI layer. Some pages are fairly simple data entry forms, and some contains UI that is scary to just think about (the amount of work it would take). It has long been my belief that it is not worth testing the UI layer. A lot of effort goes into this, and it is usually fragile in the face of changes.

The problem is that without tests is it very easy to break stuff, and I get lost in the amount of pages / controls that I have there already. It took a failure (in the middle of a client demo, of course) to convince me that it is not enough to verify changes manually. I'm currently investigating WATIR, and it looks like it is farily simple to work with it once we learn ruby, the API and the quirks.

Current things that I have issues with are speed, state, enconding, controls naming, popups and alerts.

Speed seems to be an issue, so far I have only tested it in interactive mode, but it looks like it takes quite a bit of time to run. Quite a bit of time means that I can see things happening. Looking at the documentation, I noticed such things as -f (fast), so it may be a debug mode slowdown so I could keep track of what is going on.

State is the issue of what is the current state of the application for the test. For instance, I may want to try updating an entity, and this means that it have to exists, or creating an entity when it has a certain parents, etc. This require a lot more thought than just Unit Tests, since in Unit Tests I either mocks the whole infrastructure layer (and it is not fun, trust me), or I nuke the entire DB between tests. Testing it via ASP.Net is more complex, since I have to take into account such things as caching, etc. This make it a more real test case, but make it harder to write the test. Oh well, at least the secretary wouldn't do it.

Enconding may be a problem. This is still a heresay only, but I understand that ruby has issues with unicode. A lot of the texts that I need to verify in my pages is in Hebrew, so this may be a real problem. We haven't run into it yet, but we are just beginning.

Controls naming is an ugly beast in ASP.Net, you get names like this one "ShowArchive1$dgArchive$_ctl3$_ctl0", and they may change very easily. I really don't like to see them in the tests. I think that using indexes to find the controls is just as evil in any case.

Rant: Why on hell WATIR indexes are 1-based?

Popups and alerts seems to be a weak point in WATIR, I couldn't get it to work no matter what I did, and eventually I had to resort to this to get a simple confirm dialog to work. Just so you would understand, this opens a whole new process just to send an OK to the window. There doesn't seem to be any way to get the text of the cofirm/alert window. More worrying is the popup functionality, I can't find a way to handle a modal popup nicely. It looks like I would need two tests there, one to handle the code up to the popup and afterward, and another to handle the popup itself.

Some interesting links about WATIR:

Reading the logs

time to read 1 min | 99 words

I spent a couple of minutes now going over the logs, it's quite amazing what you can find there. It looks like my posts about Brail started a bit of interest, since I suddenly started to see searches for information about it.

Another interesting things is that I started to get a lot more readers ~2,800 a day in the browser, and ~1,300 a day from RSS aggerators. I'm not using those numbers for their numeric values, since they don't really mean much by themselves, but it's interesting about 1,000 more readers via the browser, and ~700 more in RSS.

time to read 1 min | 89 words

I've updated the code that is avialable. Now I also bundle the parts of Castle that you need in order to run it, so you will have easier time build and testing this code. All the binaries you need can be found in the Deps directory, you'll need to change the references to point to that directory. You will also need SQL Server or SQL Express for the database, as well as .Net 2.0.

You can get the new release here (1.2Mb).

time to read 16 min | 3103 words

Okay, one thing that I'm sure that you have noticed is that we have a bug in the DeleteUser method. The problem is that we don't check for foreign keys violations, and that will cause problems down the road (someone tries to delete a user that is assigned to a project, and he suddenly stares at an error page asking "But what did I do?"

Let's try to prevent it before we get a bug report about it. First, we need to make a couple of policy decsisions. There are two ways that a user can be attached to a project. Either the user is  the default assignee, or he is assigned to the project. From my perspective it's perfectly fine to delete a user if he is assigned to a project, the assignment to the project should just go away, but it's bad to remove a user that is the default assignee of a project.

How do I do this? When we first defined the relationship between users and projects, I decided that the class that should be responsible for maintain the association between the objects would be Project class. Because of this, we defined the Projects as the ones responsible for the connection. This ensure that the association is managed properly (otherwise you might get into a case when you save both the user and the project, and each tries to update the same association, which is a waste at best, and can be dangerous at worst.

Because of that, we will not instruct Active Record to deal with that, but do it manually, and here is the result:

    public void DeleteUser([ARFetch("id")]User user)

    {

        if (user == null)

        {

            RenderView("NoSuchUser");

            return;

        }

        if (AssertUserIsNoDefaultAssigneeInAnyProjecT(user)==false)

        {

            return;

        }

        user.Projects.Clear();

        user.Delete();

        RedirectToAction("Users");

    }

As you can see, we Clear() the Projects collection before we delete the user, and that is all. Active Record takes care of the rest.

Now to the AssertUserIsNoDefaultAssigneeInAnyProject() method:

private bool AssertUserIsNoDefaultAssigneeInAnyProject(User user)

    {

        Project[] projectsUserIsDefaultAssigneeIn = Project.FindAll(Expression.Eq("DefaultAssignee", user));

       

        if (projectsUserIsDefaultAssigneeIn.Length > 0)

        {

            List<string> names = new List<string>();

            Array.ForEach(projectsUserIsDefaultAssigneeIn,

                delegate(Project project) { names.Add(project.Name); });

 

            Flash["bad"] = string.

                Format("User {0} is the default assignee for {1}, and so cannot be deleted",

                    user.Name,

                    string.Join(",",names.ToArray()));

            RenderView("GenericMessage");

            return false;

        }

        return true;

    }

 

We ask to get all the projects where this user is the default assignee. We don't deal with SQL, or need to pass the correct Id, or anything like this. We just tell Active Record, "give me all the projects where this user is the default assignee". This is one of the main reasons I like Active Record so much.

We then unpack the projects names to an array, and tell the user he can't delete a user that is the default assignee in a project (we also tell him what the projects are, which would save some frustrations in the future).

This is it, we solved the problem.

But wait, we have another problem, again with users. The problem is with the password field. If the user doesn't change it, it's set to the default "Dummy Password", which is probably not a good thing. Let's see how we can fix it.

First, we remove the "Dummy Password" default from the view, and leave it with an empty password (this will make it easy afterward to tell whatever the user changed the password.

Then, we change the SaveUser() method to this:

    public void SaveUser(

        [ARDataBind("user", AutoLoadUnlessKeyIs = 0,

            Exclude="Password")] User user)

    {

        RenderMessage("Back to Users", "Users");

        try

        {

            string pass = Request.Params["user.Password"];

            if (string.IsNullOrEmpty(pass) == false)

                user.Password = pass;

            user.Save();

            Flash["good"] = string.Format(

                "User {0} was saved successfully.", user.Name);

        }

        catch (Exception e)

        {

            Flash["DataBindErrors"] = user.ValidationErrorMessages;

            Flash["bad"] = e.Message;

        }

    }

The only interesting thing here is the Exclude, where we tell Active Record to not set the Password property. We then manually get the value, check if it is not empty and if it is not, set the password.

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. Production Postmortem (52):
    07 Apr 2025 - The race condition in the interlock
  2. RavenDB (13):
    02 Apr 2025 - .NET Aspire integration
  3. RavenDB 7.1 (6):
    18 Mar 2025 - One IO Ring to rule them all
  4. RavenDB 7.0 Released (4):
    07 Mar 2025 - Moving to NLog
  5. Challenge (77):
    03 Feb 2025 - Giving file system developer ulcer
View all series

RECENT COMMENTS

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats
}