2014-09-10 98 views
8

單元測試可能嗎?EF Entry.State上的單元測試失敗

public class MyRepository<T> where T : IdentityUser, new() 
{ 
    public async Task UpdateAsync(T user) 
    { 
     _context.Entry(user).State = EntityState.Modified; 
     _context.Entry(user).Property("UserName").IsModified = false; 
     await _context.SaveChangesAsync(); 
    } 
} 

的[TestInitialize]加1個用戶到庫

_user = new IdentityUser { Id = "70a038cdde40" }; 

IDbSet<IdentityUser> users = new FakeDbSet<IdentityUser> { _user }; 

var dbContext = new Mock<MyDbContext<IdentityUser>>(); 
dbContext.Setup(x => x.Users).Returns(() => users); 

_repository = new MyRepository<IdentityUser>(dbContext.Object); 

,我想這個

private MyRepository<IdentityUser> _repository; 

[TestMethod] 
public async Task UpdateUser_Success2() 
{ 
    var user = await _repository.FindByIdAsync("70a038cdde40"); 
    Assert.IsFalse(user.EmailConfirmed, "User.EmailConfirmed is True"); 

    user.EmailConfirmed = true; 

    await _repository.UpdateAsync(user); 

    (...) 
} 

測試,但它死在UpdateAsync的一號線。測試是錯誤的還是UpdateAsync實現?有什麼方法可以測試它嗎?

編輯

我加入由Belogix

dbContext.Setup(x => x.Entry(It.IsAny<IdentityUser>())) 
         .Returns(() => dbContext.Object.Entry(_user)); 

這讓我接近,我想,但仍然有非虛錯誤的建議:非虛擬成員上無效的設置: x => x.Entry(It.IsAny())

+0

什麼是'_context'在點你運行你的測試?模擬一些東西或真實的東西(指向數據庫)?你有沒有把相關的零件釘住,這樣就可以調用它了? – Belogix 2014-09-10 14:50:59

+0

@Belogix我已經更新了我的問題希望它回答你的問題 – kooshka 2014-09-10 15:02:30

+0

不,關閉但是如果你看我的答案,你需要模擬'Entry'來返回'user'而不是'State',因爲這是你的類的屬性。因此,使用我的嘲諷'Entry'的例子,然後再試一次......這樣你就可以在該模擬對象上設置狀態等。希望是有道理的! – Belogix 2014-09-11 07:56:19

回答

5

著名言論不斷: - 巴特勒·蘭普森「在計算機科學中的所有問題都可以通過間接的另一個層面解決了」。

看起來像這個不能直接測試,沒有添加一些額外的抽象。我不得不修改我的UpdateAsync方法這樣

public async Task UpdateAsync(T user) 
{ 
    SetEntityStateModified(user); 
    SetPropertyIsModified(user); 
    await _context.SaveChangesAsync(); 
} 

public virtual void SetPropertyIsModified(T user) 
{ 
    _context.Entry(user).Property("UserName").IsModified = false; 
} 

public virtual void SetEntityStateModified(T user) 
{ 
    _context.Entry(user).State = EntityState.Modified; 
} 

,然後更新我的測試代碼在初始化

_repository = new Mock<MyRepository<IdentityUser>>(dbContext.Object); 
_repository.Setup(x => x.SetEntityStateModified(It.IsAny<IdentityUser>())); 
_repository.Setup(x => x.SetPropertyIsModified(It.IsAny<IdentityUser>())); 

我的測試,然後最後通過

[TestMethod] 
public async Task can_update_user_details() 
{ 
    //Arrange 
    var user = await _repository.Object.FindByIdAsync("70a038cdde40"); 
    Assert.IsFalse(user.EmailConfirmed, "User.EmailConfirmed is True"); 

    //Act    
    user.EmailConfirmed = true; 

    await _repository.Object.UpdateAsync(user); 
    var newUser = await _repository.Object.FindByIdAsync("70a038cdde40"); 

    //Assert 
    Assert.IsTrue(newUser.EmailConfirmed, "User.EmailConfirmed is False"); 
} 
-1

它看起來像你沒有正確地鑿你的context ...我不是在與Visual Studio的計算機,所以這裏是一些僞代碼,應該證明我什麼意思。將IsAnything替換爲您的模擬框架忽略參數的方式,或者如果您想要處理不同的響應,則實際上是用戶。

// Existing context initialisation... 
var dbContext = new Mock<MyDbContext<IdentityUser>>(); 
dbContext.Setup(x => x.Users).Returns(() => users); 

// NEW: Mock what/how Entry is going to return when called (i.e. return a user) 
dbContext.Setup(x => x.Entry(IsAnything)).Returns(() => users[0]); 
+3

由於Entry在EntityFramework中不是虛擬的,因此這不起作用,因此不可嘲弄 – tocqueville 2016-06-07 19:47:46

0

的ChangeTracker中的DbContext跟蹤變化並保存更改的實體。所以你可以斷言其中的變化的實體。

Assert.IsTrue(dbContext.Object.ChangeTracker.Entries().Any(entry => 
      entry.State == EntityState.Modified && 
      entry.Entity is IdentityUser && 
      (entry.Entity as IdentityUser).Id == users[0].Id // Here you can check if it is actually the same user 
     )); 

因它會是這樣的屬性:

Assert.IsTrue(_context.Object.ChangeTracker.Entries().Any(entry => 
      entry.Property("UserName").IsModified == false && 
      entry.Entity is IdentityUser && 
      (entry.Entity as IdentityUser).Id == users[0].Id // Here you can check if it is actually the same user 
     ));