Home > c#, Unit Testing > Creating entities in unit tests

Creating entities in unit tests

An interesting question came up on twitter a moment ago about creating entities for unit tests, what follows is the approach that I use to enable me to create entities with the minimum possible code yet the maximum possible flexibility.

I have two entities Account and User, the code for them is shown below.


    public class Account : Entity<Account>
    {
        public Account( string accountName )
        {
            Name = accountName;
            Users = new List<User>();
            Questionnaires = new List<Questionnaire>();
        }

// ReSharper disable UnusedMember.Local
        private Account()
        {
            // Required by NHibernate
        }
// ReSharper restore UnusedMember.Local

        public string Name { get; private set; }
        public User Owner { get; private set; }
        public IList<User> Users { get; private set; }
        public IList<Questionnaire> Questionnaires { get; private set; }

        public User CreateUser( string username, string passphrase, string name, string emailAddress, string country, string timezone, bool asOwner )
        {
            var user = new User( username, passphrase, name, emailAddress, country, timezone, this );
            Users.Add( user );
            if( asOwner )
            {
                Owner = user;
            }

            return user;
        }

        public Questionnaire CreateQuestionnaire( string name )
        {
            Contract.Requires( () => name );
            var questionnare = new Questionnaire( name, this );
            Questionnaires.Add( questionnare );
            return questionnare;
        }
    }

    public class User : Entity<User>
    {
        public const int MaxAuthenticationRetryCount = 5;

        public User( string username, string passphrase, string name, string emailAddress, string country, string timezone, Account account )
        {
            Contract.Requires( () => username );
            Contract.Requires( () => passphrase );
            Contract.Requires( () => name );
            Contract.Requires( () => emailAddress );
            Contract.Requires( () => country );
            Contract.Requires( () => timezone );
            Contract.Requires( () => account );
            Username = username;
            Passphrase = passphrase;
            Name = name;
            EmailAddress = emailAddress;
            Country = country;
            Timezone = timezone;
            Account = account;
            EmailAddressConfirmed = false;
        }

// ReSharper disable UnusedMember.Local
        private User()
        {
            // Required by NHibernate
        }
// ReSharper restore UnusedMember.Local

        public string Username { get; private set; }
        public string Passphrase { get; private set; }
        public string Name { get; private set; }
        public string EmailAddress { get; private set; }
        public string Country { get; private set; }
        public string Timezone { get; private set; }
        public Account Account { get; private set; }
        public bool EmailAddressConfirmed { get; private set; }
        public int AuthenticationRetryCount { get; private set; }

        public void ConfirmEmailAddress()
        {
            EmailAddressConfirmed = true;
        }

        public bool Authenticate( string passphrase )
        {
            Contract.Requires( () => passphrase );
            if (IsAccountLocked())
            {
                return false;
            }

            var isAuthenticated = String.Compare( Passphrase, passphrase, StringComparison.InvariantCulture ) == 0;
            if (isAuthenticated)
            {
                AuthenticationRetryCount = 0;
            }
            else
            {
                AuthenticationRetryCount += 1;
            }

            return isAuthenticated;
        }

        public bool IsAccountLocked()
        {
            return AuthenticationRetryCount >= MaxAuthenticationRetryCount;
        }

        public void ResetPassphrase( string newPassphrase )
        {
            Contract.Requires( () => newPassphrase );
            Passphrase = newPassphrase;
        }

        public void UnlockAccount()
        {
            AuthenticationRetryCount = 0;
        }
    }

To manage creation of these entities during testing I have a static factory class for each that manages creating the entities and optionally populating any properties ( often using reflection ). The source for these is as follows:

    public static class AccountFactory
    {
        public static Account Create()
        {
            var account = new Account( "Account Name" );
            UserFactory.Create( account, true );
            return account;
        }
    }

    public static class UserFactory
    {
        public static User Create()
        {
            var account = AccountFactory.Create();
            return Create( account, true );
        }

        public static User Create( Account account, bool asOwner )
        {
            var user = account.CreateUser( "Username", "a valid passphrase", "Name", "email@address.com", "New Zealand", "GMT+12", asOwner );
            return user;
        }

        public static User WithUsername( this User user, string username )
        {
            user.SetPropertyValue( u => u.Username, username );
            return user;
        }

        public static User WithPassphrase( this User user, string passphrase )
        {
            user.SetPropertyValue( u => u.Passphrase, passphrase );
            return user;
        }

        public static User WithEmailAddress( this User user, string emailAddress )
        {
            user.SetPropertyValue( u => u.EmailAddress, emailAddress );
            return user;
        }

        public static User MimicFailedAuthenticationAttempts( this User user, int failedAttempts )
        {
            user.SetPropertyValue( u => u.AuthenticationRetryCount, failedAttempts );
            return user;
        }

        public static User MimicLockout( this User user)
        {
            user.SetPropertyValue( u => u.AuthenticationRetryCount, User.MaxAuthenticationRetryCount );
            return user;
        }
    }

The create methods return a valid entity initialised with a default set of values that can be used for testing any scenario you like. If you need to customise the data in the entities to fit a specific test scenario, the extension methods allow additional tweaking of the entity data ( often via reflection ).

This approach allows me to build entities quickly for testing purposes while ensuring that I don’t have to maintain a God class that manages building many different entities and their relationships, it is essentially convention over configuration for my entities.

Advertisement
Categories: c#, Unit Testing
  1. No comments yet.
  1. August 25, 2009 at 12:12 pm | #1

Leave a Reply

Fill in your details below or click an icon to log in:

Gravatar
WordPress.com Logo

Please log in to WordPress.com to post a comment to your blog.

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.