繁体   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