Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Tuesday, April 15, 2014

gpresult /V

When you want to find out what groups you're a member of.

| | # 
# Thursday, April 10, 2014
( Git )

Using Staging to try to only commit important bits

Not using branches yet

pushing to a named shared directory on remote server

| | # 
# Wednesday, April 09, 2014
( BDD )

Refactoring around validation so methods are short.

How to do data access – Repository, UoW etc..

  • UoW – EF uses, nHibernate made it popular
    • create a context or session
    • get objects from or add object to session
    • save changes transactionally when done
  • Repository
    • eg repo.GetUser(ID)
    • problem: lots of methods
  • CQRS (Command Query Separation)
    • problem: confusing
  • IRepo<T> – Mix UoW and Repo
    • not transactional
    • only works with 1 class
    • returns iqueryable which breaks encapsulation
    • needless overhead

SImplest thing – just use EF as it is intended..UoW

namespace Funny.DB {
    public class Session : DbContext {
        public Session()
            : base(nameOrConnectionString: "Funny") {
            // Nice for development
            Database.SetInitializer(new DropCreateDatabaseIfModelChanges<Session>());
        }
        public DbSet<Story> Stories { get; set; }
    }
}

Added EF6 to both projects.  Then add a folder called DB then class called Session are wire up.

Don’t default strings to nvarchar(max)

image
Great test just spiking out a console app and writing to the db.  Using ObjectExplorer in VS is handy too.

<connectionStrings>
  <add name="Funny" providerName="System.Data.SqlClient" connectionString="server=.\sqlexpress;database=Funny;integrated security=true" />
</connectionStrings>

EF Migrations / Testing

// Part 2
public Story ApplicationAccepted() {
    Story story = new Story();
    using (var session = new Session()) {
        // Create Story from CurrentStory
        story.Title = CurrentApplication.Title;
        story.Content = CurrentApplication.Content;
        story.Rating = CurrentApplication.Rating;
        story.CreatedAt = DateTime.Now;
        story.StoryType = CurrentApplication.StoryType;

        session.Stories.Add(story);
        session.SaveChanges();
    }
    return story;
}

Simple way of adding a Story to the session/context then saving the UoW.
image
From the rails world, a _Test database which the tests write to. Data was written by tests.

class Program {
    static void Main(string[] args) {
        var session = new Session();
        var story = new Story { Title = "Stick5" };
        session.Stories.Add(story);

        var vote = new Vote { Story=story, RatingChange = 1, CreatedAt = DateTime.Now };
        session.Votes.Add(vote);
        session.SaveChanges();

        foreach (var sto in session.Stories) {
            Console.WriteLine(sto.Title);
            if (sto.Votes != null) {
                var vot = sto.Votes.FirstOrDefault();
                Console.WriteLine(vot.RatingChange);
            }
        }

        Console.WriteLine("Done!");
        Console.Read();
    }
}

Spiking out a new entity called Vote.  A Story can have many Votes.  Did a pre TDD spike to make sure EF is wired up correctly and DB changes are being written.

3:18 of 4:10

Alt S W T – Test Explorer shortcut

public void Dispose() {
    //new Session().Database.ExecuteSqlCommand("DELETE FROM Votes");
    new Session().Database.ExecuteSqlCommand("DELETE FROM Stories");
}

Implementing IDisposable on ValidStoryReceived tests
private bool TitleAlreadyExists() {
    bool exists;
    using (var session = new Session()) {
        exists = session.Stories.FirstOrDefault(s => s.Title == CurrentApplication.Title) != null;
    }
    return exists;
}

FirstOrDefault and not Where, as then null check gets confused.

Unique constraints add to database?

namespace Funny.DB {
    public class Session : DbContext {
        public Session()
            : base(nameOrConnectionString: "Funny") {
            // Nice for development
            //Database.SetInitializer(new DropCreateDatabaseIfModelChanges<Session>());
        }
        public DbSet<Story> Stories { get; set; }
        public DbSet<Vote> Votes { get; set; }
    }
}

Enable-Migrations –EnableAutomaticMigrations
Update-Database –Force (when want to delete data too)

Moved to localdb (new improved sqlexpress!)

| | # 
# Wednesday, April 02, 2014
( BDD | Funny )

The third iteration of the British Humour / Funny website

image

image

  • First iteration (shown above – Winter)
    • Generic repository
    • EF CodeFirst
    • Fakes
    • Unity
    • IValidatableObject
  • Second iteration (below - Humour)
    • Based on Imar’s work
    • Heavy
    • Never got it into production

image

What Are We Doing?

  • Webapp to display ‘Stories’ ie jokes/video/quote/pictures/animated gif
  • CRUD form for administrator
  • BDD style of development
  • KISS
    • Real code that I intend to use
    • Not overly complicate

Setup

  • xUnit
  • FluentAssertions
  • GitHub put source onto

Features

Category of things that an application does.  A business critical feature ie not engineering eg Mailers.  Something that’s going to deliver some value

  • Authentication
  • Registration
  • Reminders

Funny:

  • Display
  • CRUD

BDD

  • Feature
  • Scenario (context) – class
  • Spec (test) – method

BDD – Gauging how our application behaves

Unit Testing – Isolated functionality of app returns what is expected

image
Unit test.. not BDD..

image
BDD – What happens when I do this approach.  Specify how the application works in the eyes of a user.

Send tests to manager as a screenshot… Is this what you mean?

‘Should’ ambiguous

[Trait("User visits the Homepage", "")]
public class VisitHomepage {
    [Fact(DisplayName="Show list of Stories in rank order")]
    public void ShowListOfStoriesInRankOrder() {
        throw new NotImplementedException();
    }
    [Fact(DisplayName = "Log entry is created")]
    public void LogEntryIsCreated() {
        throw new NotImplementedException();
    }
}

image
First shot at creating BDD tests for my app.

BDD Philosophy

Dan North in March 2006

..”Programmers wanted to know where to start, what to test and what not to test, how much to test in one go, what to call their tests, and how to understand why a test fails.”

Telling stories with our tests.

Given, When, Then…

as a, i want, so that

what value to end user..test should show

BDD is a user-oriented way to structure tests and organise development

Takes great effort and patience to get right.

Test Naming

[Trait("Anon User visits the Homepage", "")]
public class VisitHomepage {
    [Fact(DisplayName="Show list of all Stories in rank order")]
    public void ShowListOfStoriesInRankOrder() {
        throw new NotImplementedException();
    }
    [Fact(DisplayName = "Log entry is created")]
    public void LogEntryIsCreated() {
        throw new NotImplementedException();
    }
}

Spec (test) name - how our code responds to Scenario (class)

2:46 of 6:14.. look for other Rob Conery MVC BDD tests.. others test names

CRUD

namespace Specs.CRUD {
    public class Story {

    }

    [Trait("A Valid Story is submitted", "")]
    public class ValidStoryReceived {
        [Fact(DisplayName = "A Story is added to the system")]
        public void StoryAddedToSystem() {
            throw new NotImplementedException();
        }

        [Fact(DisplayName = "A confirmation message is shown to the administrator")]
        public void ConfirmationMessage() {
            throw new NotImplementedException();
        }
    }
}

Creating classes inline with tests to begin with – Brad Wilson inspired.

Validation

public class StoryCreatorResult {
    public Story NewStory { get; set; }
    public string Message { get; set; }
    public bool Success { get; set; }
    public StoryApplication StoryApplication { get; set; }

    public StoryCreatorResult() {
        this.Success = false;
    }
}

public class StoryCreator {
    public StoryApplication ValidateApplication(StoryApplication app) {
        // do some stuff
        app.HasBeenValidated = true;
        return app;
    }

    public StoryCreatorResult CreateNewStory(StoryApplication app) {
        var result = new StoryCreatorResult();

        // validation
        result.StoryApplication = ValidateApplication(app);

        // Successful creation of story!
        result.NewStory = new Story();
        result.Message = "Successfully created a new story!";
        result.StoryApplication = app;

        return result;
    }
}


StoryCreator (Service layer)

  • takes in a StoryApplication
  • returns a StoryCreatorResult
    • NewStory (if it worked)
    • StoryApplication (with a HasBeenValidated flag)
    • Message
public enum StoryType {
    Joke,
    Video,
    Quote,
    Picture,
    AnimatedGIF
}

public class Story {
    public int ID { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public int Rating { get; set; }
    public DateTime CreatedAt { get; set; }
    public StoryType StoryType { get; set; }

    public Story() {
        this.CreatedAt = DateTime.Now;
        this.StoryType = StoryType.Joke;
    }
}

Story currently has not much in it.
public class StoryApplication {
    public string Title { get; set; }
    public string Content { get; set; }
    public StoryType StoryType { get; set; }
    public bool HasBeenValidated { get; set; }

    public StoryApplication(string title, string content, StoryType storyType) {
        this.Title = title;
        this.Content = content;
        this.StoryType = storyType;
        this.HasBeenValidated = false;

        if (String.IsNullOrWhiteSpace(this.Title)
            || String.IsNullOrWhiteSpace(this.Content))
            throw new InvalidOperationException("Can't have an empty Title or Content");
    }
}

Interesting throwing on the StoryApplication if title or content are empty.  It should be handled gracefully on the client anyway so should never get to here.
[Trait("A StoryApplication is created with an empty Title or Content", "")]
public class EmptyTitle {
       
    [Fact(DisplayName = "An exception is thrown with empty title")]
    public void ApplicationInvalid() {
        Assert.Throws<InvalidOperationException>(
            () => new StoryApplication("", "content here", StoryType.Joke)
            );
    }

Testing the throw

image

2014-04-08 07.28.32
This helps a lot!

| | # 
# Monday, March 24, 2014
( BDD )

Goal: To fully understand the project, and take the ideas forward into my project

Problem: Got to a stage in his BDD Videos where tests not passing.

  • Take his code and get tests passing
  • Add more tests
  • Make overall diagram better
  • Get working within MVC5 – see grist off github link
  • Build out BritishHumour using same ideas
    • Videos again
    • Happy path first

Tests failing from GitHub

Needs data in UserMailerTemplates – had to call Update-Database to get the seed method to run.

| | # 
# Saturday, March 22, 2014
( BDD )

Email

Uses MarkdownSharp for html email.

Using file maildrop in c:\temp\maildrop

API

OpenAuth implemented!

Implement an interface so people can mock out if we want to .

In the past we’d do:

Public static Authenticate(arguments…)

Don’t want static methods as

  • Almost impossible to mock out

If we’re in an MVC app and want to mock out someone being authenticated… then statics wouldn’t work (without hitting the db).

Configuration

Allow us to pass in

  • Reset URL
  • ConfirmationURL
  • MinPasswordLength

Where to go from Here

Mocking videos

Controller/Filter/Integration/OWIN…

Test behaviour

  • Feature eg Registration, Authentication
  • Scenario (Context)  “What happens when I do this..”
  • Spec test

Clarity!

| | # 
# Thursday, March 20, 2014
( BDD )
  • Feature eg Registration, Authentication
  • Scenario (Context) eg ValidLogin, InvalidEmailOrPassword, EmptyEmailOrPassword.
  • Spec – test

image

Added specs to a new playlist

image

Created new: Authenticator, AuthenticationResult, Credentials classes

namespace Specs.Authentication {
    [Trait("Authentication", "Valid login")]
    public class ValidLogin {
        AuthenticationResult _result;
        public ValidLogin() {
            var auth = new Authenticator();
            _result = auth.AuthenticateUser(new Credentials { Email = "dave@dave.com", Password = "password" });
        }

        [Fact(DisplayName = "User authenticated")]
        public void AuthenticateTheUser() {
            Assert.True(_result.Authenticated);
        }

Going for happy path first!

Engineering the solution…

UoW issue in Authenticator when I want to pull something out ie a User, then write a UserSession if all email and password are fine.  Need to keep within the same UoW..or…

28:32 of 30:51

image

Refactor

public AuthenticationResult AuthenticateUser(Credentials creds) {
    _session = new Session();
    var result = new AuthenticationResult();
    User user = null;
    this.CurrentCredentials = creds;

    if (EmailOrPasswordNotPresent()) {
        result = InvalidLogin("Email and Password are required to login");
    } else {
        //find the user
        user = LocateUser();

        //if they're not here we're done
        if (user == null) {
            result = InvalidLogin("Invalid email or password");
            //does the password match?
        } else if (HashedPasswordDoesNotMatch(user)) {
            result = InvalidLogin("Invalid email or password");
            //success!
        } else {
            user.AddLogEntry("Login", "User logged in");
            CreateSession(user);

            result.Authenticated = true;
            result.User = user;
            result.Message = "Welcome! You are logged in";

            _session.SaveChanges();
        }
    }

    _session.Dispose();
    return result;
}

As need to reference the same session in 2 places

  • LocateUser (read from db)
  • CreateSession (write to db)

Have refactored it to be instantiated and disposed in the same controlled method.  So no early returns.

AuthenticateUserByToken – used for API stuff..just take a token and find the User.

Also put in more stuff here on Credentials…am leaving as hoping not to need it yet.

| | # 
# Monday, March 17, 2014
( BDD )

Registrator refactoring was missed before the start of this video.  Great example of refactoring a class to make it more readable.

UoW

  • Create a “Context” or “Session” – UoW container
  • Get objects from, or add objects to the Session
  • Save the changes to the Session transactionally when you’re done

nHibernate made it popular.

problems

  • Needs access to a single instance of the session
    • in large app need to make sure correct instance of session is passed to each object that needs it
  • Accidentally flushing changes
  • Transactional boundaries

Repository

  • An object that contains explicit, transactional methods which return data or execute a command
  • repo.GetUser(ID) for example

problems

  • Spiraling methods
  • Confusing redundant method names
  • Functional overlap

Query/Command Objects (CQRS)

Udi Dahan and Greg Young

  • Queries return specific, custom data for reads
  • Commands execute explicit, transactional changes to the database
  • Focus on simplicity and clarity

problems:

  • Confusing jargon
  • A  bit verbose
  • Still need an ORM

Mixing UoW and IRepository<T>

  • Defeats the purpose of both
  • Doubles the abstration headache

problems

  • only works with a single class
  • not transactional
  • uses SubmitChanges which flushes UoW
  • returns IQueryable which breaks encapsulation
  • Needless overhead to UoW

Building

namespace MonkeyFist.DB {
    public class Session : DbContext {
        public Session() : base(nameOrConnectionString: "MonkeyFist") { }
        public DbSet<User> Users { get; set; }
        public DbSet<UserActivityLog> ActivityLogs{ get; set; }
        public DbSet<User> MailerLogs { get; set; }
    }
}

image
nHibernate and Raven use same convention as Session.cs

nVarchar(MAX) – don’t want so put DataAnnotations on some fields

public class User {
    public User() {
        this.ID = Guid.NewGuid();
        this.Status = UserStatus.Pending;
        this.ActivityLogs = new List<UserActivityLog>();
        this.MailerLogs = new List<UserMailerLog>();
        this.CreatedAt = DateTime.Now;
    }

    public Guid ID { get; set; }
    [MaxLength(255)]
    public string Email { get; set; }
    public UserStatus Status;
    public ICollection<UserActivityLog> ActivityLogs { get; set; }
    public ICollection<UserMailerLog> MailerLogs { get; set; }
    public DateTime CreatedAt { get; set; }
    
}

Console App – Tasks

A task runner to try stuff out as opposed to using unit tests.

namespace Tasks {
    class Program {
        static void Main(string[] args) {
            var session = new Session();
            var user = new User { Email = "dave@dave.com" };
            Console.WriteLine(user.ID);

            session.Users.Add(user);
            session.SaveChanges();
            Console.WriteLine("Done!");
            Console.Read();
        }
    }
}

image

It worked!

image

DB so far

EF Migrations

enable-migrations in Package Manager console

AutomaticMigrationsEnabled = true;

delete migrations scripts to blow everything away and start again

update-database

MonkeyFist_Test database…

add-migration “adding required to HashedPassword"

update-database

public virtual RegistrationResult ApplicationAccepted() {
    var result = new RegistrationResult();

    using (var session = new Session()) {
        _app.Status = ApplicationStatus.Accepted;
        result.Application = _app;

        result.Application.UserMessage = "Welcome!";
        result.NewUser = new User();
        result.NewUser.ActivityLogs.Add(new UserActivityLog { Subject = "Registration", Entry = "User " + result.NewUser.Email + " successfully registered ", UserID = result.NewUser.ID });

        result.NewUser.MailerLogs.Add(new UserMailerLog { Subject = "Please confirm your email", Body = "Lorem Ipsum", UserID = result.NewUser.ID });
                
        result.Application.IsValid = true;

        session.Users.Add(result.NewUser);
        session.SaveChanges();
    }
    return result;
}

image

Tests writing to the database!  Contentious.

Keeping Test Data Clean

image

For xUnit if want startup called, then put in ctor (which gets run for every single test which is execuated)

Implement IDisposable

public void Dispose() {
    new Session().Database.ExecuteSqlCommand("DELETE FROM Users");
}

Simplest possible thing to delete.

Also EF by default does a cascade delete, so all UserMailerLogs are deleted too when User is deleted!

Adding a Migration

add-migration "Adding Unique constraint to User Email"

public partial class AddingUniqueconstrainttoUserEmail : DbMigration
{
    public override void Up()
    {
        CreateIndex(table: "Users", column: "Email", unique: true, name: "UniqueEmailIdx");
    }
        
    public override void Down()
    {
        DropIndex(table: "Users", name: "UniqueEmailIdx");
    }
}

update-database –verbose

TestBase

public class TestBase : IDisposable {
    public TestBase() {
        new Session().Database.ExecuteSqlCommand("DELETE FROM Users");
    }
    public void Dispose() {
        new Session().Database.ExecuteSqlCommand("DELETE FROM Users");
    }
}
public class ExistingEmail : TestBase{
    RegistrationResult _result;
    public ExistingEmail() : base() {
        var app1 = new Application("existing@dave.com", "password", "password");
        _result = new Registrator().ApplyForMembership(app1);
    }

Forcing a delete of users before each test is run

Hashing Password

Need a way of transferring information from Application to a new User

bcrypt

public virtual string HashPassword() {
    return BCryptHelper.HashPassword(_app.Password, BCryptHelper.GenerateSalt(10));
}

Refactoring

Making it simpler and splitting methods into smaller ones.

Naming of Tests / Playlists

image

Added a playlist for Registration, and will have one for Authentication. 

Also naming

[Trait("Registration", "Empty email or password")]

Gives us denotation.

| | # 
# Friday, March 14, 2014
( BDD )

22:59 section

public class Application {
    public string Email { get; set; }
    public string Password { get; set; }
    public string Confirmation { get; set; }
    public bool HasBeenValidated { get; set; }

    public Application(string email, string password, string confirm) {
        this.Email = email;
        this.Password = password;
        this.Confirmation = confirm;
        this.HasBeenValidated = false;
    }
}

Email, Password and Confirm cannot be null – this is business logic and validation!

Going through and making failing tests, then getting them passing.

Lots of refactoring and building out objects – very composed system.  8 distinct objects.

Resources

As Rob wants to internationlise his validator, opens up a resources file.  Instead of using a UserMessage within the Application object.

image

image

Clarity here is critical when system starts to get bigger than what you can hold in your head. 

| | # 
# Thursday, March 13, 2014
( BDD )

a spec is about how our code responds to a scenario

eg a spec will not be..email should be 5 or more characters

What is a valid application

define implicitly by saying what is invalid

Saying what the application does:

image

Snippets

Want to be able to do fact tab tab and get it going

http://visualstudiogallery.msdn.microsoft.com/B08B0375-139E-41D7-AF9B-FAEE50F68392

http://snippetdesigner.codeplex.com/wikipage?title=createFromScratch&referringTitle=Documentation

Ctrl K X to insert snippet

**todo: figure out how to tab tab

image
image

  • Feature
  • Scenario(Context)
  • Spec

NCrunch

Because test runner takes 4 or 5 secs to run.  Would like to do it more quickly

2:25 out of 5:31

Long names okay.

namespace Specs.Registration {

    public class User {

    }

    public class Registrator {
        public User ApplyForMembership() {
            return new User();
        }
    }

    [Trait("A Valid Application is Submitted", "")]
    public class ValidApplicationReceived {

        [Fact(DisplayName = "A user is added to the system")]
        public void User_Is_Added_To_System() {
            throw new NotImplementedException();
        }

Doing in same file.. nice!

A scenario (eg ValidApplicationReceived) should work only against 1 instance of a thing under test.  Helps things be faster and focus on specs.

[Trait("A Valid Application is Submitted", "")]
public class ValidApplicationReceived {
    Registrator _reg;
    User _user;

    public ValidApplicationReceived() {
        _reg = new Registrator();
        _user = _reg.ApplyForMembership();
    }

    [Fact(DisplayName = "A user is added to the system")]
    public void User_Is_Added_To_System() {
        Assert.NotNull(_user);
    }

Spec is now passing!

Guids – using as wants to log from multiple apps.  So wants users to be uniquely identified.  Otherwise Integer ID’s preferable!

Expose ICollection<UserLogs> as know going to be using EF

namespace Specs.Registration {
    public class UserLog {
        public Guid UserID { get; set; }
        public string Subject { get; set; }
        public string Entry { get; set; }
    }
    public enum UserStatus {
        Pending
    }
    public class User {
        public Guid ID { get; set; }
        public UserStatus Status;
        public ICollection<UserLog> Logs { get; set; }
        public User() {
            this.ID = Guid.NewGuid();
            this.Status = UserStatus.Pending;
            this.Logs = new List<UserLog>();
        }
    }
    public class Registrator {
        public User ApplyForMembership() {
            return new User();
        }
    }

Got our data structures in place here.  Composition at work

Log Entry is Created

public class Registrator {
    public User ApplyForMembership() {
        //successful registration!
        var user = new User();
        user.Logs.Add(new UserLog { Subject = "Registration", Entry = "User " + user.Email + " successfully registered ", UserID = user.ID });
        return user;
    }
}
[Fact(DisplayName = "Log entry created for event")]
public void Log_Entry_Is_Created_For_Event() {
    Assert.Equal(1, _user.Logs.Count());
}

Email Is Sent

Log is more of a system log entry or data tracking

Want to be able to track all communications with user.

public class UserActivityLog {
    public UserActivityLog() {
        this.ID = Guid.NewGuid();
        this.CreatedAt = DateTime.Now;
    }
    public Guid ID { get; set; }
    public Guid UserID { get; set; }
    public string Subject { get; set; }
    public string Entry { get; set; }
    public DateTime CreatedAt { get; set; }
}
public class MailerLog {
    public MailerLog() {
        this.ID = Guid.NewGuid();
        this.CreatedAt = DateTime.Now;
    }
    public Guid ID { get; set; }
    public Guid UserID { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
    public DateTime CreatedAt { get; set; }
}
public enum UserStatus {
    Pending
}
public class User {
    public Guid ID { get; set; }
    public UserStatus Status;
    public ICollection<UserActivityLog> ActivityLogs { get; set; }
    public ICollection<MailerLog> MailerLogs { get; set; }
    public string Email { get; set; }
    public DateTime CreatedAt { get; set; }
    public User() {
        this.ID = Guid.NewGuid();
        this.Status = UserStatus.Pending;
        this.ActivityLogs = new List<UserActivityLog>();
        this.MailerLogs = new List<MailerLog>();
        this.CreatedAt = DateTime.Now;
    }
}
public class RegistrationResult {
    public User NewUser { get; set; }
    public string Message { get; set; }
    public bool Success { get; set; }
    public RegistrationResult() {
        this.Success = false;
    }
}
public class Registrator {
    public RegistrationResult ApplyForMembership() {
        var result = new RegistrationResult();

        //successful registration!
        result.Message = "Welcome!";
        result.NewUser = new User();
        result.NewUser.ActivityLogs.Add(new UserActivityLog { Subject = "Registration", Entry = "User " + result.NewUser.Email + " successfully registered ", UserID = result.NewUser.ID });
        result.NewUser.MailerLogs.Add(new MailerLog { Subject = "Please confirm your email", Body = "Lorem Ipsum", UserID = result.NewUser.ID });
        return result;
    }
}
[Trait("A Valid Application is Submitted", "")]
public class ValidApplicationReceived {
    Registrator _reg;
    RegistrationResult _result;
    User _user;

    public ValidApplicationReceived() {
        _reg = new Registrator();
        _result = _reg.ApplyForMembership();
        _user = _result.NewUser;
    }

    [Fact(DisplayName = "A user is added to the system")]
    public void User_Is_Added_To_System() {
        Assert.NotNull(_user);
    }
    [Fact(DisplayName = "User status set to Pending")]
    public void User_Status_Set_to_Pending() {
        Assert.Equal(UserStatus.Pending, _user.Status);
    }
    [Fact(DisplayName = "Log entry created for event")]
    public void Log_Entry_Is_Created_For_Event() {
        Assert.Equal(1, _user.ActivityLogs.Count);
    }
    [Fact(DisplayName = "Email sent to confirm address")]
    public void Email_Sent_to_Confirm_Address() {
        Assert.Equal(1, _user.MailerLogs.Count);
    }
    [Fact(DisplayName = "A confirmation message is provided to show to the user")]
    public void A_Message_is_Provided_for_User() {
        Assert.Equal("Welcome!", _result.Message);
    }
}

Objects taking shape.

Registrator.ApplyForMembership() doesn’t take any parameters!

image

Good to see happy path Scenario (Context) of Valid Application is submitted Specs passing

| | #