Listing 3-1. The C# Auction Domain Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace MvcPattern.Models
{
public class Member
{
public string LoginName { get; set; } // The unique key
public int ReputationPoints { get; set; }
} public class Bid
{
public Member Member { get; set; }
public DateTime DatePlaced { get; set; }
public decimal BidAmount { get; set; }
} public class Item
{
public int ItemID { get; private set; } // The unique key
public string Title { get; set; }
public string Description { get; set; }
public DateTime AuctionEndDate { get; set; }
public IList<Bid> Bids { get; set; }
}
}
Listing 3-2. C# Repository Classes for the Member and Item Domain Classes
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace MvcPattern.Models
{
public class MembersRepository
{
public void AddMember(Member member)
{ } public Member FetchByLoginName(string loginName)
{
return null;
} public void SubmitChanges()
{ }
} public class ItemsRepository
{
public void AddItem(Item item)
{ } public Item FetchById(int itemId)
{
return null;
} public IList<Item> ListItems(int pageSize, int pageIndex)
{
return null;
} public void SubmitChanges()
{ }
}
}
Listing 3-3. Instantiating Concrete Classes to Get an Interface Implementation
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace MvcPattern.Models
{
public class PasswordResetHelper
{
public void ResetPassword()
{
IEmailSender mySender = new MyEmailSender(); // Call interface methods to configure e-mail details mySender.SendEmail();
}
}
} using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace MvcPattern.Models
{
public interface IEmailSender
{
void SendEmail();
} public class MyEmailSender : IEmailSender
{
public void SendEmail()
{
}
}
}
Listing 3-4. Removing Dependencies from the PasswordResetHelper Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace MvcPattern.Models
{
public class PasswordResetHelper
{
private IEmailSender _emailSender; public PasswordResetHelper(IEmailSender emailSender)
{
_emailSender = emailSender;
} public void ResetPassword()
{
// Call interface methods to configure e-mail details
_emailSender.SendEmail();
}
}
}
Listing 3-5. The IMembersRepository Interface
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace MvcPattern.Models
{
public interface IMembersRepository
{
void AddMember(Member member);
Member FetchByLoginName(string loginName);
void SubmitChanges();
} public class MembersRepository : IMembersRepository
{
public void AddMember(Member member)
{ } public Member FetchByLoginName(string loginName)
{
return null;
} public void SubmitChanges()
{ }
}
}
Listing 3-6. The AdminController Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcPattern.Models; namespace MvcPattern.Controllers
{
public class AdminController : Controller
{
IMembersRepository _membersRepository; public AdminController(IMembersRepository membersRepository)
{
_membersRepository = membersRepository;
} public ActionResult ChangeLoginName(string oldLoginName, string newLoginName)
{
Member member = _membersRepository.FetchByLoginName(oldLoginName);
member.LoginName = newLoginName; _membersRepository.SubmitChanges(); return View();
}
}
}
Listing 3-7. An Example Test Fixture
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MvcPattern.Controllers;
using MvcPattern.Models; namespace MvcPattern.Tests
{
[TestClass]
public class AdminControllerTest
{
[TestMethod]
public void CanChangeLoginName()
{
// Arrange (set up a scenario)
Member bob = new Member() { LoginName = "Bob" };
FakeMembersRepository repository = new FakeMembersRepository();
repository.Members.Add(bob); AdminController target = new AdminController(repository); string oldLoginName = bob.LoginName;
string newLoginName = "Anastasia"; // Act (attempt the operation)
target.ChangeLoginName(oldLoginName, newLoginName); // Assert (verify the result)
Assert.AreEqual(newLoginName, bob.LoginName);
Assert.IsTrue(repository.DidSubmitChanges); }
} private class FakeMembersRepository : IMembersRepository
{
public List<Member> Members = new List<Member>();
public bool DidSubmitChanges = false; public void AddMember(Member member)
{
} public Member FetchByLoginName(string loginName)
{
return Members.First(m => m.LoginName == loginName);
} public void SubmitChanges()
{
DidSubmitChanges = true;
}
}
}
Listing 3-8. Adding a Stub Method to the Item Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace MvcPattern.Models
{
public class Item
{
public int ItemID { get; private set; } // The unique key
public string Title { get; set; }
public string Description { get; set; }
public DateTime AuctionEndDate { get; set; }
public IList<Bid> Bids { get; set; } public void AddBid(Member member, Decimal amount)
{
throw new NotImplementedException();
}
}
}
Listing 3-9. Three Test Fixtures
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MvcPattern.Controllers;
using MvcPattern.Models; namespace MvcPattern.Tests
{
[TestClass]
public class AdminControllerTest
{
[TestMethod]
public void CanAddBid()
{
// Arrange - set up the scenario
Item target = new Item();
Member member = new Member();
Decimal amount = 150M; // Act - perform the test
target.AddBid(member, amount); // Assert - check the behavior
Assert.AreEqual(, target.Bids.Count());
Assert.AreEqual(amount, target.Bids[].BidAmount);
} [TestMethod()]
[ExpectedException(typeof(InvalidOperationException))]
public void CannotAddLowerBid()
{
// Arrange
Item target = new Item();
Member member = new Member();
Decimal amountParam = 150M; // Act
target.AddBid(member, amountParam);
target.AddBid(member, amountParam - );
} [TestMethod()]
public void CanAddHigherBid()
{
// Arrange
Item target = new Item();
Member firstMember = new Member();
Member secondMember = new Member();
Decimal amount = 150M; // Act
target.AddBid(firstMember, amount);
target.AddBid(secondMember, amount + ); // Assert
Assert.AreEqual(, target.Bids.Count());
Assert.AreEqual(amount + , target.Bids[].BidAmount);
}
}
}
Listing 3-10. Implementing the AddBid Method
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace MvcPattern.Models
{
public class Item
{
public int ItemID { get; private set; } // The unique key
public string Title { get; set; }
public string Description { get; set; }
public DateTime AuctionEndDate { get; set; }
public IList<Bid> Bids { get; set; } public Item()
{
Bids = new List<Bid>();
} public void AddBid(Member member, Decimal amount)
{
Bids.Add(new Bid() {
BidAmount = amount,
DatePlaced = DateTime.Now,
Member = member
});
}
}
}
Listing 3-11. Improving the Implementation of the AddBid Method
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace MvcPattern.Models
{
public class Item
{
public int ItemID { get; private set; } // The unique key
public string Title { get; set; }
public string Description { get; set; }
public DateTime AuctionEndDate { get; set; }
public IList<Bid> Bids { get; set; } public Item()
{
Bids = new List<Bid>();
} public void AddBid(Member member, Decimal amount)
{
if (Bids.Count == || amount > Bids.Max(e => e.BidAmount))
{
Bids.Add(new Bid()
{
BidAmount = amount,
DatePlaced = DateTime.Now,
Member = member
});
}
else
{
throw new InvalidOperationException("Bid amount too low");
}
}
}
}