簡體   English   中英

在單元測試中,您是否驗證並斷言?

[英]In a unit test, do you verify and assert?

我在我的單元測試項目中使用Moq。 我在網上看到的大多數單元測試示例以someMock.VerifyAll();結尾someMock.VerifyAll(); 我想知道在VerifyAll()之后斷言是否可以。 所以,例如,

//Arrange
var student = new Student{Id = 0, Name="John Doe", IsRegistered = false};
var studentRepository = new Mock<IStudentRepository>();
var studentService= new StudentService(studentRepository.Object);

//Act
studentService.Register(student);  //<-- student.IsRegistered = true now.

//Verify and assert
studentRepository.VerifyAll();
Assert.IsTrue(student.IsRegistered);

任何想法? 謝謝。

不,在大多數情況下你不應該同時使用它們 (總是有例外)。 原因是你應該只測試測試中的一個問題,即可維護性,可讀性和其他一些原因。 因此,在測試中應該是Verify(VerifyAll)或Assert,並相應地命名測試。

看看Roy Osherove關於它的文章:

http://osherove.com/blog/2005/4/3/a-unit-test-should-test-only-one-thing.html

VerifyAll用於確保調用某些方法以及調用多少次。 你使用mocks

Assert用於驗證從您正在測試的方法返回的結果。 你使用Stubs

Martin fowler有一篇很棒的文章解釋了模擬和存根之間的區別。 如果你了解它,你會更好地了解它們。

http://martinfowler.com/articles/mocksArentStubs.html

更新:使用Moq的模擬vs存根的示例,如下面的注釋所示。 我使用過驗證,但您也可以使用VerifyAll。

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
... 

[TestClass]
public class UnitTest1
{
    /// <summary>
    /// Test using Mock to Verify that GetNameWithPrefix method calls 
    /// Repository GetName method once when Id is greater than Zero
    /// </summary>
    [TestMethod]
    public void GetNameWithPrefix_IdIsTwelve_GetNameCalledOnce()
    {
        // Arrange 
        var mockEntityRepository = new Mock<IEntityRepository>();
        mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));

        var entity = new EntityClass(mockEntityRepository.Object);
        // Act 
        var name = entity.GetNameWithPrefix(12);
        // Assert
        mockEntityRepository.Verify(
            m => m.GetName(It.IsAny<int>()), Times.Once);
    }

    /// <summary>
    /// Test using Mock to Verify that GetNameWithPrefix method 
    /// doesn't calls Repository GetName method when Id is Zero
    /// </summary>
    [TestMethod]
    public void GetNameWithPrefix_IdIsZero_GetNameNeverCalled()
    {
        // Arrange 
        var mockEntityRepository = new Mock<IEntityRepository>();
        mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
        var entity = new EntityClass(mockEntityRepository.Object);
        // Act 
        var name = entity.GetNameWithPrefix(0);
        // Assert
        mockEntityRepository.Verify(
            m => m.GetName(It.IsAny<int>()), Times.Never);
    }

    /// <summary>
    /// Test using Stub to Verify that GetNameWithPrefix method
    /// returns Name with a Prefix
    /// </summary>
    [TestMethod]
    public void GetNameWithPrefix_IdIsTwelve_ReturnsNameWithPrefix()
    {
        // Arrange 
        var stubEntityRepository = new Mock<IEntityRepository>();
        stubEntityRepository.Setup(m => m.GetName(It.IsAny<int>()))
            .Returns("Stub");
        const string EXPECTED_NAME_WITH_PREFIX = "Mr. Stub";
        var entity = new EntityClass(stubEntityRepository.Object);
        // Act 
        var name = entity.GetNameWithPrefix(12);
        // Assert
        Assert.AreEqual(EXPECTED_NAME_WITH_PREFIX, name);
    }
}

public class EntityClass
{
    private IEntityRepository _entityRepository;
    public EntityClass(IEntityRepository entityRepository)
    {
        this._entityRepository = entityRepository;
    }
    public string Name { get; set; }
    public string GetNameWithPrefix(int id)
    {
        string name = string.Empty;
        if (id > 0)
        {
            name = this._entityRepository.GetName(id);
        }
        return "Mr. " + name;
    }
}

public interface IEntityRepository
{
    string GetName(int id);
}

public class EntityRepository:IEntityRepository
{
    public string GetName(int id)
    {
        // Code to connect to DB and get name based on Id
        return "NameFromDb";
    }
}

是的你應該調用斷言。

VerifyAll()將斷言所有SetUp()調用實際上都被調用了。

VerifyAll()不會確認您的學生對象已注冊。 因為在您的測試用例中沒有SetUp()調用,我認為VerifyAll()沒有驗證任何內容。

我絕對希望看到VerifyAssert在單元測試中並排使用。 斷言用於驗證已正確設置被測系統的屬性,而Verify用於確保已正確調用被測系統所接受的任何依賴項。 使用Moq我傾向於明確驗證設置而不是使用VerifyAll catch-all。 這樣你就可以使測試的目的更加清晰。

我在上面的代碼中假設您對學生資料庫的調用返回一個布爾值來表明該學生已注冊? 然后你在student對象上設置該值? 在這種情況下,需要添加一個有價值的設置,您有效地說,當調用學生存儲庫方法時,它將返回true。 然后Assert student.IsRegistered為true以確保您已從存儲庫返回值中正確設置屬性,並Verify使用您期望的輸入調用存儲庫方法。

在模擬測試中斷言和驗證都沒有任何本質上的錯誤,盡管依賴於被調用的實際方法的斷言可能會失敗,因為模擬方法與真實方法沒有相同的效果。

在您的示例中,它可能很好,因為只有存儲庫被模擬,並且學生狀態的更改可能在服務中完成。

驗證和斷言是否應該在同一測試中完成在某種程度上是一種品味問題。 實際上,驗證是檢查是否正確調用了存儲庫,並且斷言正在檢查是否對該實體進行了正確的更改。 由於這些是單獨的問題,我會將它們分開進行測試,但這可能就是我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM