[英]Unit testing database using EF
一個單元如何測試連接到數據庫的服務?
我在數據訪問層中有一個playerRepository
類,它直接與數據庫進行交互;在業務層中,它有一個playerService
類,它創建一個playerRepository
實例,並為諸如刪除播放器,保存播放器,獲取所有播放器, ID /名稱yadda yadda。
我想在不使用真實數據庫的情況下對playerService
進行單元測試,而是使用EF隨附的內存數據庫。
問題是,我在弄清楚如何設置它時遇到了麻煩。
我有一個PlayerContext : DbContext
類,它用作代碼優先的模型(通過EF教程來完成此部分)。 而且我必須向構造函數DbContextOptionsBuilder<PlayerContext>
添加一個參數,但是我不知道從哪里開始。 我不知道如何和在哪里設置連接字符串,“默認”數據庫將在哪里存儲自身。
我想通過使用不帶NSubstitute或Moq的EF來做到這一點,我這樣做是為了了解如何在不使用EF之外的其他框架的情況下完成它。
public class PlayerContext : DbContext
{
public PlayerContext() : base()
{
}
public DbSet<Player> Players { get; set; }
}
public class Player
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int DciNumber { get; set; }
}
我正在使用Visual Studio提供的單元測試
[TestClass]
public class PlayerServiceTest
{
// Should get all the players from database
[TestMethod]
public void GetAllPlayers()
{
// arrange - act
// TODO: Return all the players from the database
// assert
// TODO: Should return empty list
}
而且PlayerService類看起來像這樣
public class PlayerService
{
private PlayerRepository _playerRepository = new PlayerRepository();
public List<Player> GetAllPlayers()
{
var players = _playerRepository.GetAllPlayers();
return players;
}
PlayerRepository
public class PlayerRepository
{
public List<Player> GetAllPlayers()
{
using (var context = new PlayerContext())
{
var players = context.Players.ToList();
return players;
}
}
通常我的問題是:
在單元測試的情況下,如何使PlayerContext
具有另一個連接到內存數據庫的連接字符串,以及在不通過單元測試運行時如何為它提供正確的連接字符串
如何更改數據庫的位置,因為它使用C:\\Users
的默認路徑。
我不是在尋找與DAL的PlayerRepository
進行集成測試,我只是想測試業務層,而我所需要的就是,當我運行PlayerService要使用連接到內存數據庫的PlayerRepository的測試時,就是這樣。 否則,它應連接到與應用程序主exe所在文件夾中存儲的本地數據庫
需要幫助!
缺少的部分是依賴注入/ IoC。 此處的原理是使用可以模擬的協定接口定義依賴項(存儲庫)。 將該依賴項注入依賴它的類中。 這樣,您可以用返回已知狀態,拋出預期異常等的模擬對象來替代具體的實現(例如數據庫,文件處理等),以測試業務邏輯對這些情況的處理。
public class PlayerService
{
private readonly IPlayerRepository _playerRepository = null;
public PlayerService(IPlayerRepository playerRepository)
{
_playerRepository = playerRepository ?? throw new ArgumentNullException("playerRepository");
}
public List<Player> GetAllPlayers()
{
var players = _playerRepository.GetAllPlayers();
return players;
}
}
看一下IoC容器,例如Autofac,Unity,Ninject等,以獲取如何構建容器以在構造它們時自動識別具體類實例並將其注入到您的服務中的示例。
當您去編寫單元測試時,您將創建IPlayerRepository類的模擬(例如:Moq)並將其傳遞給您的被測服務。 即:
[Test]
public void TestService()
{
var mockRepository = new Mock<IPlayerRepository>();
mockRepository.Setup(x => x.GetPlayers()).Returns(buildTestPlayerList());
var serviceUnderTest = new PlayerService(mockRepository.Object);
// continue with test...
}
在最壞的情況下:如果您要放棄容器,這也可以工作:
public class PlayerService
{
private IPlayerRepository _playerRepository = null;
public IPlayerRepository PlayerRepository
{
get { return _playerRepository ?? (_playerRepository = new PlayerRepository()); }
set { _playerRepository = value; }
}
// ...
}
...然后進行測試...
[Test]
public void TestService()
{
var mockRepository = new Mock<IPlayerRepository>();
mockRepository.Setup(x => x.GetPlayers()).Returns(buildTestPlayerList());
var serviceUnderTest = new PlayerService { PlayerRepository = mockRepository.Object };
// continue with test...
}
我將這種模式稱為“惰性屬性注入”,您可以在其中選擇發送依賴關系,但是默認情況下,它將僅創建默認依賴關系。 當在舊代碼中引入依賴替換和單元測試時,這可能是一個有用的模式,而舊代碼很大程度上依賴於更新中間代碼類。 從某種意義上講,這是懶惰的,因為依賴項只是在第一次訪問時才“更新”。 如果您在服務中調用不需要依賴關系的方法,則不會初始化每個依賴關系,只有使用的依賴關系才會初始化。 我強烈建議您閱讀IoC容器,因為它們可以幫助自動關聯依賴關系。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.