管理员返回null,但为什么?

时间:2011-02-19 17:20:47

标签: c# unit-testing moq

我有以下测试。

[TestFixture]
    public class AdministratorRepositoryTests
    {
        private IAdministrateurRepository repository;

        public static IAdministrateurRepository MockAdministrateurRepository(params Administrateur[] items)
        {
            var mockRepos = new Mock<IAdministrateurRepository>();
            mockRepos.Setup(x => x.Select()).Returns(items.AsQueryable());
            return (IAdministrateurRepository)mockRepos.Object;
        }

        [SetUp]
        public void SetupContext()
        {
            Guid gId1 = new Guid("a05fd3de-9ae4-4b0b-b560-fd96678d3019");
            Administrateur a1 = new Administrateur(gId1);

            Guid gId2 = new Guid("e0724d12-d856-4677-89aa-d12611c15a4c");
            Administrateur a2 = new Administrateur(gId2);

            Guid gId3 = new Guid("30a69d49-84e5-42fc-a643-9e42c1350aa8");
            Administrateur a3 = new Administrateur(gId3);

            Guid gId4 = new Guid("b6444711-baee-4da6-87a8-a839c438bdff");
            Administrateur a4 = new Administrateur(gId4);

            Guid gId5 = new Guid("9d805acd-9d59-44ac-892c-438b189bbf94");
            Administrateur a5 = new Administrateur(gId5);

            repository = MockAdministrateurRepository(a1, a2, a3, a4, a5);

        }

        [Test]
        public void peut_recuperer_un_administrateur_dun_repository_par_son_id()
        {
            Guid monGuid = new Guid("a05fd3de-9ae4-4b0b-b560-fd96678d3019");

            var administrateur = repository.SelectById(monGuid);

            administrateur.ShouldNotBeNull();
            administrateur.Id.ShouldEqual(monGuid);
        }
    }

我很难理解为什么管理员返回NULL。

var administrateur = repository.SelectById(monGuid);

我甚至对存储库进行了计数测试,它有5个项目。

任何帮助?

4 个答案:

答案 0 :(得分:7)

我经常看到这个问题 - 你从根本上误解了嘲弄/伪装/抄袭的目的是什么。也就是说,你正在编写一个虚假实现的测试,因此,测试是无用的。想一想 - 你的测试试图证明什么?您正在尝试验证是否可以从存储库中获取结果,但是:

// this is a mock/fake/stub/whatever, it's NOT the real class
var repository = new Mock<IAdministrateurRepository>(); 

// and so.. what is the point in this when your 
// IAministratorRepository is not the production version?
var administrateur = repository.SelectById(monGuid);

// The repository is meant to be the focus of the test, 
// yet you're not testing a result of using a real class
administrateur.ShouldNotBeNull();
administrateur.Id.ShouldEqual(monGuid);

以上一行是从您的存储库中提取数据,但存储库不是真正的实现。这不是生产代码。如果是这样,那么为什么然后断言从假存储库中取出的Administrator对象呢?您正在测试的行为是ID功能搜索,但它是在您的测试夹具中定义的!您已经创建了一个存根/伪实现,然后对其进行了测试 - 仅此而已。 Mocks / Stubs / Fakes意味着有预期或提供预制结果来推动真实类型的测试(阅读:生产中使用的类型)。测试模拟本身没有任何效果。

在编写任何测试代码之前,您应该问自己此测试的目的是什么?如果您无法回答该问题,请在编写测试代码之前解决它。如果您要实际测试AdministratorRepository,那么您将实例化存储库的生产代码版本,然后对其进行操作,然后在其行为的某些方面进行断言。

blog post可能会对您有所帮助。我主张放弃模拟框架并使用基于状态的测试,直到你完全清楚模拟/存根适合的位置,因为它很容易被基于交互的测试弄糊涂。

*编辑 - 好的,这里(大致)代码应该是什么样子 忽略实现细节,只关注每种类型的角色,并注意它很简单。

// interface
public interface IAdministratorRepository
{
    Administrator SelectById(Guid _id);
    void Add(Administrator _admin);
}

// minimal implementation of admin.  
public class Administrator
{
    public Guid Id { get; set; }

    public Administrator(Guid _id)
    {
        Id = _id;
    }
}

/// <summary>
/// For argument's sakes, this is the class under test.  
/// It's not a mock/fake/stub/whatever; it's the real deal!
/// </summary>
public class RealAdministratorRepository : IAdministratorRepository
{
    private Dictionary<Guid, Administrator> m_items = new Dictionary<Guid, Administrator>();

    public Administrator SelectById(Guid _id)
    {
        // no error handling here; keeping it simple
        if(m_items.ContainsKey(_id))
            return m_items[_id];

        return null;
    }

    public void Add(Administrator _admin)
    {
        // No error handling for brevity's sakes
        m_items.Add(_admin.Id, _admin);
    }
}

// now, here's a very, very simple happy path test for SelectById using
// the real implementation of AdministratorRepository
[TestFixture]
public class AdministratorRepositoryTests
{
    private const string AdminId = "a05fd3de-9ae4-4b0b-b560-fd96678d3019";
    private IAdministratorRepository m_repository;

    [SetUp]
    public void PerTestSetUp()
    {
        // no mocks/stubs required.  m_repository is a RealAdministratorRepository
        // because that's our production class and that's what we want to test!
        m_repository = new RealAdministratorRepository();
        m_repository.Add(new Administrator(new Guid(AdminId)));
    }

    [Test]
    public void SelectById_WithItemsInRepository_ReturnsCorrectItems()
    {
        // ignore the fact that I'm repeating the same string 3 times; brevity again
        var item = m_repository.SelectById(new Guid(AdminId));

        Assert.That(item, Is.Not.Null);
        Assert.That(item.Id, Is.EqualTo(new Guid(AdminId)));
    }
}

请注意,我没有使用模拟框架,因为我不需要。生产代码没有任何需要模拟/存根/伪造的有问题的依赖。

答案 1 :(得分:4)

模拟和伪造是两个不同的概念。您似乎认为您通过向其添加数据并期望数据存在来设置存储库。这就是存储库的工作方式;它就像真正的存储库,但更简单,只包含您设置的数据。另一方面, mock 存储库实际上并不包含数据,但包含一组对具有特定签名的方法调用的响应。您设置了对没有参数的Select()调用的响应,但实际上您正在使用特定参数调用SelectById()调用。因为事先没有为此调用设置响应,所以它返回一个空结果,即当没有找到匹配的调用时的默认结果。

答案 2 :(得分:2)

您设置了Select,但在测试中使用了SelectById

答案 3 :(得分:2)

您必须在MockAdministrateurRepository方法中设置模拟存储库才能返回所需的Administarateur。

mockRepos
    .Setup(x => x.SelectById(new Guid("a05fd3de-9ae4-4b0b-b560-fd96678d3019")))
    .Returns(a1);