Причина сбоя при запросе actual.Orders.Count()
состоит в том, что actual
возвращает этот объект, который вы создали ранее:
new Customer() { Id = 1, Name = "Jeff" }
И этот конкретный Customer
объект имеет null
для своего свойства Orders
, потому что вы никогда не устанавливали его.
Посмотри на это так. Что касается вашего кода, хранилище - это просто механизм хранения объектов. Не имеет значения, поддерживается ли хранилище таблицами в базе данных со связями между этими таблицами, или же оно просто где-то помещается в список. Важно то, что когда вы вызываете repo.GetCustomer(1)
, он возвращает вам объект Customer
с идентификатором 1 и все остальные данные, которые должны быть заполнены, как и должно быть. В этом случае вам необходимо, чтобы соответствующие ордера действительно находились в объекте Customer
!
Так что вы можете сделать что-то вроде:
private void FillData()
{
var customer1 = new Customer() { Id = 1, Name = "Jeff" };
var customer2 = new Customer() { Id = 2, Name = "Brian" };
var order1 = new Order() { Id = 1, Customer = 1, Value = 100 };
var order2 = new Order() { Id = 2, Customer = 2, Value = 200 };
var order3 = new Order() { Id = 3, Customer = 1, Value = 300 };
customer1.Orders = new List<Order> {order1, order3};
customer2.Orders = new List<Order> {order2};
this.Customers = new FakeObjectSet<Customer>(new[] {customer1, customer2});
this.Orders = new FakeObjectSet<Order>(new[] {order1, order2, order3});
}
Но с вашим полным набором заказов.
ВСЕ, ЧТО СКАЗАНО, я настоятельно рекомендую не использовать подобные рулонные бетонные насадки Изучите использование среды Moq: http://code.google.com/p/moq/ Это сделает вашу жизнь намного проще.
Редактировать : Вот кое-что, что вы могли бы найти полезным при построении контекстов для своих юнит-тестов. Прежде всего, если вы не знаете, что такое метод расширения, это всего лишь пример:
namespace Foo
{
public static class StringExtensions
{
public static bool IsNullOrEmpty(this string input)
{
return string.IsNullOrEmpty(input);
}
}
}
Тогда потребитель мог бы сделать ...
using Foo;
string a = null, b = "hello";
a.IsNullOrEmpty(); // returns true
b.IsNullOrEmpty(); // returns false
Синтаксис позволяет создавать методы, которые можно вызывать , как если бы они были методами экземпляра объекта, но на самом деле это статические методы, которые определены в другом месте.
СЕЙЧАС, как говорится. Вы могли бы потенциально создать некоторые методы расширения и вспомогательные классы для создания контекстов для ваших модульных тестов. Как пример.
public static class UnitTestHelper
{
private static int _nextCustomerId = 0;
private static int _nextOrderId = 0;
public static Customer MockCustomer(string name)
{
if (string.IsNullOrEmpty(name)) throw new ArgumentException("name");
var id = _nextCustomerId;
_nextCustomerId += 1;
return new Customer
{
Id = id,
Name = name,
Orders = new List<Order>()
};
}
public static Customer WithOrder(this Customer customer, int value)
{
if (customer == null) throw new ArgumentNullException("customer");
var order = new Order
{
Id = _nextOrderId,
Customer = customer.Id,
Value = value
};
customer.Orders.Add(order);
_nextOrderId += 1;
return customer;
}
public static Mock<Repository> HavingCustomers(this Mock<Repository> repository,
params Customer[] customers)
{
if (repository == null) throw new ArgumentNullException("repository");
var allOrders = customers.SelectMany(c => c.Orders);
repository.Setup(r => r.Customers)
.Returns(new FakeObjectSet<Customer>(customers));
repository.Setup(r => r.Orders)
.Returns(new FakeObjectSet<Order>(allOrders));
return repository;
}
}
Как только вы это получите, вместо того, чтобы делать много кропотливого ручного создания материала, вы можете сделать что-то вроде ...
[Test]
public void ShouldReturnAllCustomersWithoutOrders()
{
var john = UnitTestHelper.MockCustomer("John").WithOrder(100).WithOrder(200);
var paul = UnitTestHelper.MockCustomer("Paul");
var george = UnitTestHelper.MockCustomer("George").WithOrder(15);
var ringo = UnitTestHelper.MockCustomer("Ringo");
var mockRepository = new Mock<Repository()
.HavingCustomers(john, paul, george, ringo);
var custServ = new CustomerService(mockRepository.Object);
var customersWithoutOrders = custServ.GetCustomersWithoutOrders();
Assert.That(customersWithoutOrders.Count(), Is.EqualTo(2));
Assert.That(customersWithoutOrders, Has.Member(paul));
Assert.That(customersWithoutOrders, Has.Member(ringo));
}
И эту настройку можно извлечь в метод с присоединенным SetUpAttribute
, если он будет использоваться в нескольких тестах.
Вам понадобится как можно больше гибкости при определении контекста для ваших модульных тестов, вы не хотите предполагать, что для каждого модульного теста вы пишете вам всегда хотят двух одинаковых клиентов с одинаковыми восемью заказами. Но это не значит, что вы не можете написать несколько быстрых вспомогательных методов или классов, чтобы упростить настройку и сделать ее гораздо менее многословной.
Надеюсь, это поможет!