I have been teaching myself how to unit test various parts of my application. I am trying to do a unit test of my UserService. I seem to be getting the following error:
I am not sure what the error means. Could someone explain what is going on? I know the service itself works already I tested it separately. I have never created unit tests before so if someone could please give me a detailed explanation as to why this is happening?
Thanks in advance!
User Service Test
public class UserServiceTest
{
public readonly IUserService MockUserService;
public List<UserDTO> userDTOList = new List<UserDTO>
{
new UserDTO(new User{UserId = 1, FirstName = "Eric"}),
new UserDTO(new User{UserId = 1, FirstName = "Dave"}),
new UserDTO(new User{UserId = 1, FirstName = "Jim"})
};
public UserServiceTest()
{
Mock<IUserService> mockUserService = new Mock<IUserService>();
mockUserService.Setup(mr => mr.GetAllUsers()).Returns(userDTOList);
this.MockUserService = mockUserService.Object;
}
[TestMethod]
public void TestServiceGetUsers()
{
List<UserDTO> testUserList = this.MockUserService.GetAllUsers();
Assert.IsNotNull(testUserList);
Assert.AreEqual(userDTOList, testUserList);
}
}
WCF Interface
[ServiceContract]
public interface IUserService
{
[OperationContract]
List<UserDTO> GetAllUsers();
}
[DataContract]
public class UserDTO
{
public UserDTO(User user)
{
this.FirstName = user.FirstName;
this.LastName = user.LastName;
this.NumberOfItemsSold = user.NumberOfItemsSold;
this.UserId = user.UserId;
this.UserName = user.UserName;
}
[DataMember]
public int UserId { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
public string UserName { get; set; }
[DataMember]
public int NumberOfItemsSold { get; set; }
}
WCF Interface implementation
public class UserService : IUserService
{
public List<UserDTO> GetAllUsers()
{
using (UnitOfWork work = new UnitOfWork(new ProductContext()))
{
return work.Users.GetAll().Select(a => new UserDTO(a)).ToList();
}
}
}
Edit after suggestion from Mel Gerats
Okay so I figured I would place the updated code for my unit test that actually tests the service now. Here it is! Thanks again for the help.
[TestClass]
public class UserServiceTest
{
public List<UserDTO> userDTOList = new List<UserDTO>
{
new UserDTO(new User{UserId = 1, FirstName = "Eric"}),
new UserDTO(new User{UserId = 2, FirstName = "Dave"}),
new UserDTO(new User{UserId = 3, FirstName = "Jim"})
};
public IEnumerable<User> users = new List<User>
{
new User{UserId = 1, FirstName = "Eric"},
new User{UserId = 2, FirstName = "Dave"},
new User{UserId = 3, FirstName = "Jim"}
};
[TestMethod]
public void TestServiceGetUsers()
{
//Setup
var unitOfWorkMock = new Mock<IUnitOfWork>();
unitOfWorkMock.Setup(m => m.Users.GetAll()).Returns(users);
var serviceUnderTest = new UserService(unitOfWorkMock.Object);
var result = serviceUnderTest.GetAllUsers();
Assert.IsNotNull(result);
Assert.AreEqual(userDTOList.Count, result.Count);
Assert.AreEqual(userDTOList[0].FirstName, result[0].FirstName);
Assert.AreEqual(userDTOList[1].FirstName, result[1].FirstName);
}
}
For some reason I could not directly test if the two lists were equal. I think it is because the Capacity of the lists were different on my lists. So I opted for testing each position in the list.
Looks like you are missing a reference to System.ServiceModel
But your test doesn't actually test anything, except maybe that mocking works, as you are only calling the mocked method on the mock.
The goal of mocking is to test your class under test while mocking the dependencies. If your UserService had a dependency on a UserRepository for example, you would mock the repository in order to test the UserService.
The following example illustrates this:
public class UserService : IUserService
{
private IUserRepository _userRepository;
private IUserNotification _userNotification;
public UserService(IUserRepository userRepository, IUserNotification userNotification)
{
_userRepository = userRepository;
_userNotification = userNotification;
}
public void NotifyInactiveUsers(DateTime activeBefore)
{
var users = _userRepository.GetAllUsers();
foreach(var user in users)
{
if(user.LastActivityDate > DateTime.Now)
{
_userNotification.SendRemovalNotification(user)
_userRepository.MarkUserForRemoval(user)
}
}
}
}
The UserService provides an operation to notify all users that are inactive since a given date.
public void TestUserService_NotifyInactiveUsers_BeforeActivityDate()
{
var userRepository = new Mock<IUserRepository>();
var userNotification = new Mock<IUserNotification>();
var userService = new UserService(userRepository.Object, userNotification.Object);
var testUser = new User { LastActivityDate = DateTime.Now}
userRepository.Setup(r => r.GetAllUsers()).Returns(Enumerable.Repeat(testUser, 1));
userService.NotifyInactiveUsers(DateTime.Now.AddYears(-1));
userNotification.Verify(n => n.SendRemovalNotification(It.IsAny<User>), Times.Never);
}
The test sets up the UserRepository to return one user that was active today.
It then calls the operation on the UserService, and verifies that SendRemovalNotification was never called. This tests whether or not the logic to determine if a user is inactive is indeed correct.
Related
I m testing an app that my teacher made. I added the driver class, so every order has a driver associated with it, and a put the Driver as a foreign key in my Order class (So I have one to one relationship)
public class Order {
[ForeignKey("Driver")]
public int DriverId { get; set; }
public int Id { get; set; }
public List<Item> Items { get; set; }
}
public class Driver {
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Range(1, 5)]
public int Rating { get; set; }
}
Then I went ahead and created the add, update and delete driver, and did the migration and update to the database.
Now I am tryin to test all this functionality, so I made some unit-test but the only ones that are passing are Add_customner and Add_drver everything else fails
So I am hoping to see if anyone can share some light
This are my test
[Test]
public void AddCustomer_ValidCustomer_ReturnsTrue() {
// Arrange
Customer customer = new Customer() {
Id = 1,
FirstName = "James",
LastName = "Cameron",
StreetAddress = "123 Sesame Street",
City = "New York City",
ZIP = "11111",
State = "NY",
PhoneNumber = "1111111111",
Email = "jc#terminator.com"
};
// Act
var result = uow.AddCustomer(customer);
// Assert
Assert.IsTrue(result);
var qCust = _context.Customers.Where(q => q.Id == 1).FirstOrDefault();
Assert.AreSame(qCust, customer);
}
[Test]
public void AddDriver_ValidDriver_ReturnsTrue() {
// Arrange
Driver driver = new Driver() {
ID = 1,
FirstName = "James",
LastName = "Cameron",
Rating = 4
};
// Act
var result = uow.AddDriver(driver);
// Assert
Assert.IsTrue(result);
var qDrive = _context.Drivers.Where(q => q.ID == 1).FirstOrDefault();
Assert.AreSame(qDrive, driver);
}
[Test]
public void RemoveDriver_ValidDriver_ReturnsTrue() {
// Arrange
Driver driver = new Driver() {
ID = 1,
FirstName = "James",
LastName = "Cameron",
Rating = 4
};
// Act
var result = uow.RemoveDriver(driver);
// Assert
var Drivera = _context.Drivers;
Assert.IsTrue(result);
var qDrive = _context.Drivers.Remove(driver);
Assert.AreNotEqual(Drivera, qDrive);
}
[Test]
public void UpdateDriver_ValidDriver_ReturnsTrue() {
Driver driver = new Driver() {
ID = 1,
FirstName = "James",
LastName = "Cameron",
Rating = 4
};
var result = uow.UpdateDriver(driver);
var qDrive = _context.Drivers.Where(q => q.ID == 1).FirstOrDefault();
Assert.AreSame(qDrive, driver);
}
}
}
For it to work it must return true
Make sure your UOW using the same object as _context? Make sure when setting up mocks your database context it is injected and assigned same object to UOW and _context. This might be the reason your read-only operations are passing but others are failing.
Since you name it _context, I assume it is an instance variable of your test class. So whenever a test run, a new test object create and thus _context. To test your operations other than add, you should assume the _context is empty. That means you should first add the driver and then test removing it.
I am failing on the BeUniqueEmail with the error message (below). How can I mock this correctly? I have included the validator and the test below.
The tests passed when I removed the BeUnique email validation
Message:
System.NotImplementedException : The method or operation is not implemented.
public class CreateStudentCommandValidator : AbstractValidator<CreateStudentCommand>
{
private readonly IApplicationDbContext _context;
public CreateStudentCommandValidator(IApplicationDbContext context)
{
_context = context;
RuleFor(v => v.Email)
.NotEmpty().WithMessage("Email is required.")
.MaximumLength(30).WithMessage("Email must not exceed 30 characters.")
.MustAsync(BeUniqueEmail).WithMessage("The specified email already exists.");
}
public async Task<bool> BeUniqueEmail(CreateStudentCommand model, string email, CancellationToken cancellationToken)
{
bool emailExists = await _context.Students
.Where(x => x.Email == email)
.Where(x => !x.IsDeleted)
.CountAsync() > 0;
return !emailExists;
}
}
Testing
[Test]
public async Task CreateStudentCommand_Success()
{
var mockSet = new Mock<DbSet<Student>>();
var context = new Mock<IApplicationDbContext>();
context.Setup(m => m.Student).Returns(mockSet.Object);
var handler = new CreateStudentCommandHandler(context.Object);
var validator = new CreateStudentCommandValidator(context.Object);
//var mockedValidator = new Mock<IValidator<CreateStudentCommandValidator>>();
//var mock1 = new Mock<AbstractValidator<CreateStudentCommandValidator>>();
var command = new CreateStudentCommand
{
StudentType = "Test1",
Email = "Test1#email.com",
FirstName = "Test1",
LastName = "Test1",
IsActive = true
};
var result = await handler.Handle(command, new CancellationToken());
// Act
var validationResult = await validator.ValidateAsync(command);
// Assert
Assert.True(validationResult.IsValid);
Assert.IsInstanceOf<Guid>(result);
}
Using Moq, a solution to this could be as follows.
[Test]
public async Task TestValidation()
{
var context = Mock.Of<ApplicationDbContext>();
var validator = new CreateStudentCommandValidator(context);
var command = new CreateStudentCommand
{
StudentType = "Test1",
Email = "Test1#email.com",
FirstName = "Test1",
LastName = "Test1",
IsActive = true
};
var validationResult = await validator.ValidateAsync(command);
Assert.True(validationResult.IsValid);
}
Not knowing your Context, I implemented a simple dummy.
public interface IApplicationDbContext
{
List<Student> Students { get; set; }
}
public class ApplicationDbContext : IApplicationDbContext
{
public ApplicationDbContext()
{
Students = new List<Student>();
}
public List<Student> Students { get; set; }
}
This all depends on the use of Moq however, If you are using some other Mocking Service the Implementation will change.
*My Previous answer was intended to explain the implementation of Mocking Method Results. But in this case you do not need to mock the CreateStudentCommandValidator
I am trying to write a unit test for My Processor class I have two problems
I do not know how to test my Methods Only.
this is my processor
OrderProcessor class
public class OrderProcessor
{
public void Process(CustomersOrder order)
{
var oldOrder = _repository.GetOldorderId(order.Code.Value);
if (oldOrder != 0)
{
updateOrder(order);
}
else
{
SaveOrder(order);
}
}
private void updateOrder(CustomersOrder order)
{
_repository.UpdateOrder(order);
}
private void SaveOrder(CustomersOrder order)
{
_repository.SaveOrder(order);
}
}
}
Repository class
public class Repository : IRepository
{
private static PracticeEntities4 _context;
public Repository(PracticeEntities4 context)
{
_context = context;
}
public int GetOldCustomerId( int customerCode)
{
var CuID= _context.Customers.First(e => e.Code == customerCode);
return CuID.Id;
}
public int GetOldorderId(int orderCode)
{
var oldOrder = _context.CustomersOrders.FirstOrDefault(e => e.Code == orderCode);
return oldOrder.Id;
}
public void SaveCustomer(Customer customer)
{
_context.Customers.Add(customer);
_context.SaveChanges();
}
public void SaveOrder(CustomersOrder order)
{
_context.CustomersOrders.Add(order);
_context.SaveChanges();
}
public void UpdateOrder(CustomersOrder order)
{
_context.CustomersOrders.AddOrUpdate(order);
_context.SaveChanges();
}
}
and this is My unit test I don't know how to fix it and where is the problem exactly and also I want to test the Methods too.
UnitTests Class
[TestClass]
public class OrderProcessorTest
{
[ClassInitialize]
{...}
[TestInitialize]
public void TestInitialize()
{
....
}
[TestMethod]
public void Customer_OrderProcess()
{
//Arange
Mock<IRepository> mock= new Mock<IRepository>();
//Act
mock.Setup(e => e.GetOldCustomerId(1001)).Returns(3);
mock.Setup(e => e.GetOldStoreId(200)).Returns(3);
var dtos = OrderDeserializer.Deserialize(path);
var dto = dtos.First(e => e.Code == 300);
OrderBuilder builder = new OrderBuilder(mock.Object);
builder.OrderBuild(dto);
//Asset
Assert.AreEqual(0, _orders.Count);
}
}
Order Builder Class
public class OrderBuilder
{
public IRepository _repository { get; set; }
public OrderBuilder(IRepository repository)
{
_repository = repository;
}
public CustomersOrder OrderBuild(OrderDto dto)
{
var oldStoreId = _repository.GetOldStoreId(dto.StoreCode);
var oldCustomerId = _repository.GetOldCustomerId(dto.CustomerCode);
return new CustomersOrder()
{
OrderDate = Convert.ToDateTime(dto.OrderDate),
OrderStatus = dto.OrderStatus,
DeliveryDate = Convert.ToDateTime(dto.DeliveryDate),
CustomerId = oldCustomerId,
StoreId = oldStoreId,
Code = dto.Code
};
}
}
In your code I see that there are all sorts of Mocking and Initial test setups that is taking place without a clear intention on what to test.
Unit Test: What ?
Tests a unit of an application without its external dependencies
Unit Test: Why ?
Makes refactoring faster and ensures you don't break existing portion of your code
Unit Test: Steps ?
We first need to re-factor the code before we do unit tests. Modularity is the key
By using Interfaces remove the tight couplings in the code
Inject the dependency via method parameters, constructor, properties or use Dependency Injection
Consider using Mock objects, as a good practice, only when dealing with external dependency.
In the [TestMethod] we organize the tests into 3 categories Arrange -> Act -> Assert
Example:
//Arrange
var res = new Reservation();
//Act
var op = res.Method(new User{IsAdmin=true});
// Assert
Assert.IsTrue(op);
Naming Conventions in UnitTests:
TestProjectName: [InserProjectName].UnitTests
TestClasses: [InsertClassName]Tests
TestMethod: [MethodYourTesting]_[Scenario]_[ExpectedBehavior]
I have created a Console app as close as possible to your problem
(minus the DBContext) that you can replicate on your PC to understand
the various portions.
All the domain classes are part of one single
file for the sake of testability to reproduce faster.
Console App Project
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StackOrderProcessor
{
public class CustomersOrder
{
public OrderDto Order { get; set; }
public List<CustomersOrder> CustomersOrders = new List<CustomersOrder>();
public DateTime OrderDate { get; set; }
public string OrderStatus { get; set; }
public int CustomerID { get; set; }
public int Code { get; set; }
public int ID { get; set; }
}
public class Customer
{
public OrderDto Order { get; set; }
public List<Customer> Customers = new List<Customer>();
public int Code { get; set; }
public int ID { get; set; }
}
public class OrderDto
{
public DateTime OrderDate { get; set; }
public int CustomerCode { get; set; }
public string OrderStatus { get; set; }
public int Code { get; set; }
}
public interface IRepository
{
int GetOldCustomerId(int customerCode);
int GetOldOrderId(int orderCode);
void SaveCustomer(Customer customer);
void SaveOrder(CustomersOrder order);
}
public class Repository : IRepository
{
private readonly Customer _cust;
private readonly CustomersOrder _custOrder;
public Repository(Customer cust, CustomersOrder custOrder )
{
_cust = cust;
_custOrder = custOrder;
}
public int GetOldCustomerId(int customerCode)
{
var cuId = _cust.Customers.First(e => e.Code == customerCode);
return cuId.ID;
}
public int GetOldOrderId(int orderCode)
{
var oId = _custOrder.CustomersOrders.FirstOrDefault(e => e.Code == orderCode);
return oId.ID;
}
public void SaveCustomer(Customer customer)
{
_cust.Customers.Add(customer);
}
public void SaveOrder(CustomersOrder order)
{
_custOrder.CustomersOrders.Add(order);
}
}
public class OrderProcess
{
private readonly IRepository _repository;
public OrderProcess(IRepository repository)
{
_repository = repository;
}
public void Process(CustomersOrder order)
{
var oldOrder = _repository.GetOldOrderId(order.Code);
if (oldOrder == 0)
_repository.SaveOrder(order);
}
}
public class OrderBuilder
{
private readonly IRepository _repository;
public OrderBuilder(IRepository repository)
{
_repository = repository;
}
public CustomersOrder OrderBuild(OrderDto dto)
{
var oldCustomerId = _repository.GetOldCustomerId(dto.CustomerCode);
return new CustomersOrder()
{
Order = dto,
OrderDate = Convert.ToDateTime(dto.OrderDate),
OrderStatus = dto.OrderStatus,
ID = oldCustomerId,
CustomerID = oldCustomerId,
Code = dto.Code
};
}
}
class Program
{
static void Main(string[] args)
{
var cust = new Customer();
var custOrder = new CustomersOrder();
#region PopulatingCustomer
//Populating OrderDto
var dto1 = new OrderDto { Code = 1, CustomerCode = 1, OrderDate = DateTime.Now.Date, OrderStatus = "OK" };
//Populating Customer
var customerList = cust.Customers = new List<Customer>();
var customerOrderList = custOrder.CustomersOrders = new List<CustomersOrder>();
var customer1 = new Customer
{
Code = 1,
ID = 1, Order=dto1
};
var customer2 = new Customer
{
Code = 2,
ID = 2,
};
customerList.Add(customer1);
customerList.Add(customer2);
#endregion
#region PopulatingCustomerOrder
var customersOrder1 = new CustomersOrder { Code = 1, CustomerID = 1, ID = 1, Order = dto1, OrderDate = dto1.OrderDate, OrderStatus = dto1.OrderStatus };
customerOrderList.Add(customersOrder1);
#endregion
#region InvokingMethods
//IRepository
IRepository IRepo = new Repository(cust,custOrder);
//OrderProcessor
var orderProcesor = new OrderProcess(IRepo);
//OrderBuilder
var dto2 = new OrderDto { Code = 2, CustomerCode = 2, OrderDate = DateTime.Now.Date, OrderStatus = "OK" };
var oBuilder = new OrderBuilder(IRepo);
var newCustOrder = oBuilder.OrderBuild(dto2);
customerOrderList.Add(newCustOrder);
#endregion
Console.Read();
}
}
}
UnitTest Project
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using StackOrderProcessor;
namespace StackOrderProcessor.UnitTests
{
[TestClass]
public class RepositoryTests
{
[TestMethod]
public void GetOldCustomerId_WhenCalled_ReturnsOId()
{
//Arrange
var cust = new Customer();
var custOrder = new CustomersOrder();
IRepository repo = new Repository(cust,custOrder);
var customerList = cust.Customers = new List<Customer>();
var dto1 = new OrderDto { Code = 1, CustomerCode = 1, OrderDate = DateTime.Now.Date, OrderStatus = "OK" };
var customer1 = new Customer
{
Code = 1,
ID = 1,
Order = dto1
};
var customer2 = new Customer
{
Code = 2,
ID = 2,
};
customerList.Add(customer1);
customerList.Add(customer2);
//Act
repo.GetOldCustomerId(1);
//Assert
Assert.AreEqual(1, 1); //Test will Pass as we have a customer of Code 1
}
[TestMethod]
//MethodName_Scenario_Expectedbehavior
public void SaveCustomer_WhenCalled_AddsNewCustomer()
{
var cust = new Customer();
var custOrder = new CustomersOrder();
IRepository repo = new Repository(cust, custOrder);
var customerList = cust.Customers = new List<Customer>();
var dto1 = new OrderDto { Code = 1, CustomerCode = 1, OrderDate = DateTime.Now.Date, OrderStatus = "OK" };
var customer1 = new Customer
{
Code = 1,
ID = 1,
Order = dto1
};
var customer2 = new Customer
{
Code = 2,
ID = 2,
};
customerList.Add(customer1);
customerList.Add(customer2);
//Act
var custToSave = new Customer
{
Code = 3,
ID = 3,
Order = null
};
repo.SaveCustomer(custToSave);
//Assert
Assert.AreEqual(3, customerList.Count);
}
}
[TestClass]
public class OrderProcessor1Tests
{
[TestMethod]
public void Process_WhenOrderIsZero_AddsNewCustomerOrder()
{
//Arrange
var cust = new Customer();
var custOrder = new CustomersOrder();
var customerOrderList = custOrder.CustomersOrders = new List<CustomersOrder>();
IRepository repo = new Repository(cust, custOrder);
var orderProcessor = new OrderProcess(repo);
var dto1 = new OrderDto { Code = 1, CustomerCode = 1, OrderDate = DateTime.Now.Date, OrderStatus = "OK" };
var custOrder1 = new CustomersOrder { ID = 1, Code = 1, CustomerID = 1, Order = dto1, OrderDate = dto1.OrderDate, OrderStatus = dto1.OrderStatus };
customerOrderList.Add(custOrder1);
//Act
orderProcessor.Process(custOrder1);
//Assert
Assert.AreEqual(1, customerOrderList.Count);
}
}
}
Note: Make sure to add reference of StackOrderProcessor in StackOrderProcessor.UnitTests
You will still need to better organize the Unit Test Methods, this was just for demonstration purposes, I hope concepts are much more clear now
Your question doesn't have enough information, you don't need your repository code for this question, but OrderBuiler class and _orders field needs. I'm sorry for this comment in answers location.
I've come across a problem with Entity framework Core 2.0.2 and I wonder if someone can enlighten me.
What I have is a person class with a collection of phone numbers.
Person
public class Person : BaseEntity
{
[Required]
public string FirstName { get; set; }
public string MiddleName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Context { get; set; }
public string Email { get; set; }
public ICollection<PhoneNumber> PhoneNumbers { get; set; } = new List<PhoneNumber>();
}
PhoneNumber
public class PhoneNumber : BaseEntity
{
public string Usage { get; set; }
public int PersonId { get; set; }
public Person Person { get; set; }
}
BaseEntity is basically just a property with an id.
Then I have a generic repo
Repository
public class Repository<T> where T : BaseEntity
{
private readonly Dab2_2RdbContext _context;
public Repository(Dab2_2RdbContext context)
{
_context = context;
}
public void Create(T t)
{
_context.Entry<T>(t).State = EntityState.Added;
_context.SaveChanges();
}
public T Read(int id)
{
return _context.Find<T>(id);
}
public void Update(int id, T t)
{
_context.Entry<T>(t).State = EntityState.Modified;
_context.SaveChanges();
}
public void Delete(T t)
{
_context.Entry<T>(t).State = EntityState.Deleted;
_context.SaveChanges();
}
}
All of this code is very simple and works fine.
The situation happens when I test the code like this:
var context = new Dab2_2RdbContext();
var personRepo = new Repository<Person>(context);
var phoneNumberRepo = new Repository<PhoneNumber>(context);
var person = new Person()
{
FirstName = "Kasper",
LastName = "Lastname",
Email = "Something#gmail.com",
Context = "Myself",
};
person.PhoneNumbers = new List<PhoneNumber>()
{
new PhoneNumber() {Usage = "Work"},
new PhoneNumber() {Usage = "School"}
};
// Create
personRepo.Create(person);
This code generates a person with proper values, however, it doesn't include the Phone numbers.
var context = new Dab2_2RdbContext();
var personRepo = new Repository<Person>(context);
var phoneNumberRepo = new Repository<PhoneNumber>(context);
var person = new Person()
{
FirstName = "Kasper",
LastName = "Lastname",
Email = "Something#gmail.com",
Context = "Myself",
};
// Create
personRepo.Create(person);
person.PhoneNumbers = new List<PhoneNumber>()
{
new PhoneNumber() {Usage = "Work"},
new PhoneNumber() {Usage = "School"}
};
personRepo.Update(person.Id, person);
This code makes the proper relationship (the person.The phonenumber relation has been made. Notice the // Create person, shifted above person.Phone number
I wonder if someone can enlighten me, I really can figure out why this is.
The answer is that setting the state of the entry. Will only track the entity, and none of it's properties. While add will track all the navigation properties.
I just finished James Kovac's article on Inversion of Control and Dependency Injection and then used what I learned to make my own IoC/DI example below.
I'm quite satisified with this example since it:
satisfies the testability aspect of IoC in that it instantiates Customers by passing both a Repository and a MockRepository
also the authorization service is decoupled which would allow you to e.g. write another authorization service with different rules, and then you could easily swap them out based on other conditions
However, looking toward progressing from here, some things seem odd:
I don't seem to have a "container". Is my Customer class "acting as a container" in this sense?
if I were to port this to WPF, where would Modules (in terms of Prism) fit into this example (e.g. would AuthorizationService and Repository be modules?)
if I were to port this to WPF, where would MVVM fit in? Do have have parts of MVVM through having the dependency injection or is MVVM something separate altogether.
Thanks for any direction you can provide on this.
NEW CODE BASED ON COMMENTS:
using System;
using System.Linq;
using System.Collections.Generic;
namespace TestSimpleDependencyInjection1
{
class Program
{
static void Main(string[] args)
{
AuthorizationService authorizationService = new AuthorizationService();
//real example
Repository repository = new Repository(authorizationService);
for (int id = 1; id <= 3; id++)
{
Customer customer = repository.GetCustomer(id);
customer.Display();
}
Console.WriteLine();
//mock test example
MockRepository mockRepository = new MockRepository(authorizationService);
Customer mockCustomerAdministrator = repository.GetCustomer(1);
Customer mockCustomerSalesperson = repository.GetCustomer(2);
UnitTester.Assert("Administrators have access", mockCustomerAdministrator.GetAuthorizationMessage(), "Access Granted");
UnitTester.Assert("Salespeople do not have access", mockCustomerAdministrator.GetAuthorizationMessage(), "Access Granted");
Console.ReadLine();
}
}
public static class UnitTester
{
public static void Assert(string title, string value, string expectedResult)
{
Console.WriteLine(value == expectedResult ? String.Format("{0}: test succeeded", title) : String.Format("{0}: TEST FAILED!", title));
}
}
public class Customer
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public AccessGroup AccessGroup { get; set; }
public AuthorizationService AuthorizationService { get; set; }
public string GetAuthorizationMessage()
{
return this.AuthorizationService.GetAccessMessage(this);
}
public Customer()
{
}
public void Display()
{
Console.WriteLine("Customer: {1}, {0} ({2}): {3}", this.FirstName, this.LastName, this.AccessGroup, this.GetAuthorizationMessage());
}
}
public class AuthorizationService
{
public string GetAccessMessage(Customer customer)
{
return customer.AccessGroup == AccessGroup.Administrator ? "Access Granted" : "Access Denied";
}
}
public class Repository : IRepository
{
private List<Customer> _customerSet = new List<Customer>();
private AuthorizationService _authorizationService;
public Repository(AuthorizationService authorizationService)
{
_authorizationService = authorizationService;
_customerSet.Add(new Customer {AuthorizationService = _authorizationService, ID = 1, FirstName = "Jim", LastName = "Smith", AccessGroup = AccessGroup.Administrator });
_customerSet.Add(new Customer {AuthorizationService = _authorizationService, ID = 2, FirstName = "John", LastName = "Johnson", AccessGroup = AccessGroup.Administrator });
_customerSet.Add(new Customer {AuthorizationService = _authorizationService, ID = 3, FirstName = "Hank", LastName = "Rivers", AccessGroup = AccessGroup.Salesperson });
}
public Customer GetCustomer(int id)
{
return (from c in _customerSet
where c.ID == id
select c).SingleOrDefault();
}
}
public class MockRepository : IRepository
{
private List<Customer> _customerSet = new List<Customer>();
private AuthorizationService _authorizationService;
public MockRepository(AuthorizationService authorizationService)
{
_authorizationService = authorizationService;
_customerSet.Add(new Customer { AuthorizationService = _authorizationService, ID = 1, FirstName = "Test1AdministratorFirstName", LastName = "Test1AdministratorLastName", AccessGroup = AccessGroup.Administrator });
_customerSet.Add(new Customer { AuthorizationService = _authorizationService, ID = 2, FirstName = "Test2SalespersonFirstName", LastName = "Test2SalesPersonLastName", AccessGroup = AccessGroup.Salesperson });
}
public Customer GetCustomer(int id)
{
return (from c in _customerSet
where c.ID == id
select c).SingleOrDefault();
}
}
public interface IRepository
{
Customer GetCustomer(int id);
}
public enum AccessGroup
{
Administrator,
Salesperson
}
}
AND PER REQUEST, HERE IS THE ORIGINAL CODE:
using System;
using System.Collections.Generic;
namespace TestSimpleDependencyInjection1
{
class Program
{
static void Main(string[] args)
{
AuthorizationService authorizationService = new AuthorizationService();
//real example
Repository repository = new Repository();
for (int id = 1; id <= 3; id++)
{
Customer customer = new Customer(id, authorizationService, repository);
customer.Display();
}
Console.WriteLine();
//mock test example
MockRepository mockRepository = new MockRepository();
Customer mockCustomerAdministrator = new Customer(1, authorizationService, mockRepository);
Customer mockCustomerSalesperson = new Customer(2, authorizationService, mockRepository);
UnitTester.Assert("Administrators have access", mockCustomerAdministrator.GetAuthorizationMessage(), "Access Granted");
UnitTester.Assert("Salespeople do not have access", mockCustomerAdministrator.GetAuthorizationMessage(), "Access Granted");
Console.ReadLine();
}
}
public static class UnitTester
{
public static void Assert(string title, string value, string expectedResult)
{
Console.WriteLine(value == expectedResult ? String.Format("{0}: test succeeded", title) : String.Format("{0}: TEST FAILED!", title));
}
}
public class Customer
{
private AuthorizationService authorizationService;
private IRepository repository;
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public AccessGroup AccessGroup { get; set; }
public string GetAuthorizationMessage()
{
return authorizationService.GetAccessMessage(this);
}
public Customer(int id, AuthorizationService authorizationService, IRepository repository)
{
this.authorizationService = authorizationService;
this.repository = repository;
CustomerDB customerDB = repository.GetCustomerDB(id);
this.ID = customerDB.ID;
this.FirstName = customerDB.FirstName;
this.LastName = customerDB.LastName;
this.AccessGroup = customerDB.AccessGroup;
}
public void Display()
{
Console.WriteLine("Customer: {1}, {0} ({2}): {3}", this.FirstName, this.LastName, this.AccessGroup, this.GetAuthorizationMessage());
}
}
public class AuthorizationService
{
public string GetAccessMessage(Customer customer)
{
return customer.AccessGroup == AccessGroup.Administrator ? "Access Granted" : "Access Denied";
}
}
public class Repository : IRepository
{
private List<CustomerDB> _customerDBSet = new List<CustomerDB>();
public Repository()
{
_customerDBSet.Add(new CustomerDB { ID = 1, FirstName = "Jim", LastName = "Smith", AccessGroup = AccessGroup.Administrator });
_customerDBSet.Add(new CustomerDB { ID = 2, FirstName = "John", LastName = "Johnson", AccessGroup = AccessGroup.Administrator });
_customerDBSet.Add(new CustomerDB { ID = 3, FirstName = "Hank", LastName = "Rivers", AccessGroup = AccessGroup.Salesperson });
}
public CustomerDB GetCustomerDB(int id)
{
CustomerDB customerDBchosen = null;
//this should be done with LINQ (couldn't get it CustomerDB to implement IEnumerable correctly)
foreach (CustomerDB customerDB in _customerDBSet)
{
if (customerDB.ID == id)
{
customerDBchosen = customerDB;
break;
}
}
return customerDBchosen;
}
}
public class MockRepository : IRepository
{
public CustomerDB GetCustomerDB(int id)
{
switch (id)
{
case 1:
return new CustomerDB { ID = 1, FirstName = "Test1AdministratorFirstName", LastName = "Test1AdministratorLastName", AccessGroup = AccessGroup.Administrator };
case 2:
return new CustomerDB { ID = 2, FirstName = "Test2SalespersonFirstName", LastName = "Test2SalesPersonLastName", AccessGroup = AccessGroup.Salesperson };
default:
return null;
}
}
}
public interface IRepository
{
CustomerDB GetCustomerDB(int id);
}
public class CustomerDB
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public AccessGroup AccessGroup { get; set; }
}
public enum AccessGroup
{
Administrator,
Salesperson
}
}
Your code violates the single responsibility principal in a pretty serious way.
http://en.wikipedia.org/wiki/Single_responsibility_principle
Your Customer object shouldn't be loading itself from a repository, the repository should be loading the Customer object and handing it back to the caller.
I would expect something more like:
Customer customer = repository.GetCustomer(3);
usage of Switch statements in most cases violate SRP. Do check this out for ways to eliminate switch statements in your code. The challenge & beauty of writing loosely coupled code is to eliminate if, if else & switch statements.