單元測試時,您應該提供所有您明確測試的類的所有依賴關係。 即依賴注入;沒有服務構建它自己的依賴關係,而是依賴外部組件來提供它們。當你在一個依賴注入容器之外,並且在一個單元測試中你正在手動創建你正在測試的類時,它的你的責任提供依賴關係。
實際上,這意味着您要麼向構造函數提供模擬對象或實際對象。例如,您可能想要提供一個真實的記錄器,但沒有目標,一個連接內存數據庫的真實數據庫上下文或一些模擬服務。
假設在這個例子中,該服務正在測試這個樣子的:
public class ExampleService
{
public ExampleService(ILogger<ExampleService> logger,
MyDbContext databaseContext,
UtilityService utilityService)
{
// …
}
// …
}
所以爲了測試ExampleService
,我們需要提供這三個對象。在這種情況下,我們會做以下每個:
ILogger<ExampleService>
- 我們將用一個真實的記錄,沒有任何連接的目標。因此,任何對記錄器的調用都能正常工作,而我們不需要提供一些模擬,但我們不需要測試日誌輸出,因此我們不需要實際的目標。
MyDbContext
- 在這裏,我們將使用真實數據庫上下文與附加的內存數據庫
UtilityService
- 爲此,我們將創建一個模擬,它只是在我們想要測試的方法內設置我們需要的實用方法。
所以單元測試看起來是這樣的:
[Fact]
public async Task TestExampleMethod()
{
var logger = new LoggerFactory().CreateLogger<ExampleService>();
var dbOptionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase();
// using Moq as the mocking library
var utilityServiceMock = new Mock<UtilityService>();
utilityServiceMock.Setup(u => u.GetRandomNumber()).Returns(4);
// arrange
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// fix up some data
db.Set<Customer>().Add(new Customer()
{
Id = 2,
Name = "Foo bar"
});
await db.SaveChangesAsync();
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// create the service
var service = new ExampleService(logger, db, utilityServiceMock.Object);
// act
var result = service.DoSomethingWithCustomer(2);
// assert
Assert.NotNull(result);
Assert.Equal(2, result.CustomerId);
Assert.Equal("Foo bar", result.CustomerName);
Assert.Equal(4, result.SomeRandomNumber);
}
}
在您的具體Cancel
情況下,要避免使用你是不是目前正在測試該服務的任何方法。所以如果你想測試Cancel
,你應該從你的服務調用的唯一方法是Cancel
。測試可能看起來像這樣(只是猜測這裏的依賴關係):
[Fact]
public async Task Cancel_StatusShouldBeN()
{
var logger = new LoggerFactory().CreateLogger<ExampleService>();
var dbOptionsBuilder = new DbContextOptionsBuilder().UseInMemoryDatabase();
// arrange
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// fix up some data
db.Set<SomeItem>().Add(new SomeItem()
{
Id = 5,
Status = "Not N"
});
await db.SaveChangesAsync();
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
// create the service
var service = new YourService(logger, db);
// act
var result = service.Cancel(5);
// assert
Assert.Equal(1, result);
}
using (var db = new MyDbContext(dbOptionsBuilder.Options))
{
var item = db.Set<SomeItem>().Find(5);
Assert.Equal(5, item.Id);
Assert.Equal("n", item.Status);
}
}
Btw。請注意,我始終打開一個新的數據庫上下文,以避免從緩存的實體獲取結果。通過打開一個新的上下文,我可以驗證這些更改實際上是否完全將其導入到數據庫中。
對於這種測試,您不應該使用術語「單元測試」。這是一個集成測試,因爲對於測試,您依賴於具體的'DbContext'實現。單元測試可以在沒有外部依賴的情況下進行測試(即通過模擬接口)。這是當你在你的服務中使用EF Core時的缺點之一,而不是將其抽象到存儲庫或通過CQRS – Tseng