考虑在比你使用的工具更高的抽象层次上进行模拟。
也许你的视图模型应该依赖于服务,而不是你使用的工具的细节(例如IIsesServiceChannel)。
以下是一个例子:
我通常使用IServices、Services和MockServices。
- IServices提供了所有业务逻辑必须调用方法的可用操作。
- Services是数据访问层,我的代码后端将其注入到视图模型中(即实际数据库)。
- MockServices是数据访问层,我的单元测试将其注入到视图模型中(即模拟数据)。
IServices:
public interface IServices
{
IEnumerable<Warehouse> LoadSupply(Lookup lookup);
IEnumerable<Demand> LoadDemand(IEnumerable<string> stockCodes, int daysFilter, Lookup lookup);
IEnumerable<Inventory> LoadParts(int daysFilter);
Narration LoadNarration(string stockCode);
IEnumerable<PurchaseHistory> LoadPurchaseHistory(string stockCode);
IEnumerable<StockAlternative> LoadAlternativeStockCodes();
AdditionalInfo GetSupplier(string stockCode);
}
模拟服务:
public class MockServices : IServices
{
#region Constants
const int DEFAULT_TIMELINE = 30;
#endregion
#region Singleton
static MockServices _mockServices = null;
private MockServices()
{
}
public static MockServices Instance
{
get
{
if (_mockServices == null)
{
_mockServices = new MockServices();
}
return _mockServices;
}
}
#endregion
#region Members
IEnumerable<Warehouse> _supply = null;
IEnumerable<Demand> _demand = null;
IEnumerable<StockAlternative> _stockAlternatives = null;
IConfirmationInteraction _refreshConfirmationDialog = null;
IConfirmationInteraction _extendedTimelineConfirmationDialog = null;
#endregion
#region Boot
public MockServices(IEnumerable<Warehouse> supply, IEnumerable<Demand> demand, IEnumerable<StockAlternative> stockAlternatives, IConfirmationInteraction refreshConfirmationDialog, IConfirmationInteraction extendedTimelineConfirmationDialog)
{
_supply = supply;
_demand = demand;
_stockAlternatives = stockAlternatives;
_refreshConfirmationDialog = refreshConfirmationDialog;
_extendedTimelineConfirmationDialog = extendedTimelineConfirmationDialog;
}
public IEnumerable<StockAlternative> LoadAlternativeStockCodes()
{
return _stockAlternatives;
}
public IEnumerable<Warehouse> LoadSupply(Lookup lookup)
{
return _supply;
}
public IEnumerable<Demand> LoadDemand(IEnumerable<string> stockCodes, int daysFilter, Syspro.Business.Lookup lookup)
{
return _demand;
}
public IEnumerable<Inventory> LoadParts(int daysFilter)
{
var job1 = new Job() { Id = Globals.jobId1, AssembledRequiredDate = DateTime.Now, StockCode = Globals.stockCode100 };
var job2 = new Job() { Id = Globals.jobId2, AssembledRequiredDate = DateTime.Now, StockCode = Globals.stockCode200 };
var job3 = new Job() { Id = Globals.jobId3, AssembledRequiredDate = DateTime.Now, StockCode = Globals.stockCode300 };
return new HashSet<Inventory>()
{
new Inventory() { StockCode = Globals.stockCode100, UnitQTYRequired = 1, Category = "Category_1", Details = new PartDetails() { Warehouse = Globals.Instance.warehouse1, Job = job1} },
new Inventory() { StockCode = Globals.stockCode200, UnitQTYRequired = 2, Category = "Category_1", Details = new PartDetails() { Warehouse = Globals.Instance.warehouse1, Job = job2} },
new Inventory() { StockCode = Globals.stockCode300, UnitQTYRequired = 3, Category = "Category_1", Details = new PartDetails() { Warehouse = Globals.Instance.warehouse1, Job = job3} },
};
}
#endregion
#region Selection
public Narration LoadNarration(string stockCode)
{
return new Narration()
{
Text = "Some description"
};
}
public IEnumerable<PurchaseHistory> LoadPurchaseHistory(string stockCode)
{
return new List<PurchaseHistory>();
}
public AdditionalInfo GetSupplier(string stockCode)
{
return new AdditionalInfo()
{
SupplierName = "Some supplier name"
};
}
#endregion
#region Creation
public Inject Dependencies(IEnumerable<Warehouse> supply, IEnumerable<Demand> demand, IEnumerable<StockAlternative> stockAlternatives, IConfirmationInteraction refreshConfirmation = null, IConfirmationInteraction extendedTimelineConfirmation = null)
{
return new Inject()
{
Services = new MockServices(supply, demand, stockAlternatives, refreshConfirmation, extendedTimelineConfirmation),
Lookup = new Lookup()
{
PartKeyToCachedParts = new Dictionary<string, Inventory>(),
PartkeyToStockcode = new Dictionary<string, string>(),
DaysRemainingToCompletedJobs = new Dictionary<int, HashSet<Job>>(),
.
.
.
},
DaysFilterDefault = DEFAULT_TIMELINE,
FilterOnShortage = true,
PartCache = null
};
}
public List<StockAlternative> Alternatives()
{
var stockAlternatives = new List<StockAlternative>() { new StockAlternative() { StockCode = Globals.stockCode100, AlternativeStockcode = Globals.stockCode100Alt1 } };
return stockAlternatives;
}
public List<Demand> Demand()
{
var demand = new List<Demand>()
{
new Demand(){ Job = new Job{ Id = Globals.jobId1, StockCode = Globals.stockCode100, AssembledRequiredDate = DateTime.Now}, StockCode = Globals.stockCode100, RequiredQTY = 1},
new Demand(){ Job = new Job{ Id = Globals.jobId2, StockCode = Globals.stockCode200, AssembledRequiredDate = DateTime.Now}, StockCode = Globals.stockCode200, RequiredQTY = 2},
};
return demand;
}
public List<Warehouse> Supply()
{
var supply = new List<Warehouse>()
{
Globals.Instance.warehouse1,
Globals.Instance.warehouse2,
Globals.Instance.warehouse3,
};
return supply;
}
#endregion
}
服务:
public class Services : IServices
{
#region Singleton
static Services services = null;
private Services()
{
}
public static Services Instance
{
get
{
if (services == null)
{
services = new Services();
}
return services;
}
}
#endregion
public IEnumerable<Inventory> LoadParts(int daysFilter)
{
return InventoryRepository.Instance.Get(daysFilter);
}
public IEnumerable<Warehouse> LoadSupply(Lookup lookup)
{
return SupplyRepository.Instance.Get(lookup);
}
public IEnumerable<StockAlternative> LoadAlternativeStockCodes()
{
return InventoryRepository.Instance.GetAlternatives();
}
public IEnumerable<Demand> LoadDemand(IEnumerable<string> stockCodes, int daysFilter, Lookup lookup)
{
return DemandRepository.Instance.Get(stockCodes, daysFilter, lookup);
}
.
.
.
单元测试:
[TestMethod]
public void shortage_exists()
{
var supply = new List<Warehouse>() { Globals.Instance.warehouse1, Globals.Instance.warehouse2, Globals.Instance.warehouse3 };
Globals.Instance.warehouse1.TotalQty = 1;
Globals.Instance.warehouse2.TotalQty = 2;
Globals.Instance.warehouse3.TotalQty = 3;
var demand = new List<Demand>()
{
new Demand(){ Job = new Job{ Id = Globals.jobId1, StockCode = Globals.stockCode100, AssembledRequiredDate = DateTime.Now}, StockCode = Globals.stockCode100, RequiredQTY = 1},
new Demand(){ Job = new Job{ Id = Globals.jobId2, StockCode = Globals.stockCode200, AssembledRequiredDate = DateTime.Now}, StockCode = Globals.stockCode200, RequiredQTY = 3},
new Demand(){ Job = new Job{ Id = Globals.jobId3, StockCode = Globals.stockCode300, AssembledRequiredDate = DateTime.Now}, StockCode = Globals.stockCode300, RequiredQTY = 4},
};
var alternatives = _mock.Alternatives();
var dependencies = _mock.Dependencies(supply, demand, alternatives);
var viewModel = new MainViewModel();
viewModel.Register(dependencies);
viewModel.Load();
AwaitCompletion(viewModel);
var part100IsNotShort = dependencies.PartCache.Where(p => (p.StockCode == Globals.stockCode100) && (!p.HasShortage)).Single() != null;
var part200IsShort = dependencies.PartCache.Where(p => (p.StockCode == Globals.stockCode200) && (p.HasShortage)).Single() != null;
var part300IsShort = dependencies.PartCache.Where(p => (p.StockCode == Globals.stockCode300) && (p.HasShortage)).Single() != null;
Assert.AreEqual(true, part100IsNotShort &&
part200IsShort &&
part300IsShort);
}
CodeBehind:
public MainWindow()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
this.viewModel = this.DataContext as MainViewModel;
var dependencies = GetDependencies();
this.viewModel.Register(dependencies);
.
.
.
视图模型:
public MyViewModel()
{
.
.
.
public void Register(Inject dependencies)
{
try
{
this.Injected = dependencies;
this.Injected.RefreshConfirmation.RequestConfirmation += (message, caption) =>
{
var result = MessageBox.Show(message, caption, MessageBoxButton.YesNo, MessageBoxImage.Question);
return result;
};
this.Injected.ExtendTimelineConfirmation.RequestConfirmation += (message, caption) =>
{
var result = MessageBox.Show(message, caption, MessageBoxButton.YesNo, MessageBoxImage.Question);
return result;
};
.
.
.
}
catch (Exception ex)
{
Debug.WriteLine(ex.GetBaseException().Message);
}
}