如何对仅由 NHibernate 设置的受保护属性进行单元测试?

发布于 2024-09-24 13:55:04 字数 731 浏览 3 评论 0原文

我正在使用 NHibernate 来持久化此实体:

public class Store
{
    public int Id { get; protected set; }
    public int Name { get; set; }
}

请注意 Id 属性如何具有受保护的 setter。这是为了防止用户更改 Id,同时仍允许 NHibernate 在将其保存到数据库时为其分配 Id

在我的一个单元测试中,我使用 Moq 和以下代码来模拟我的存储库:

var mock = new Mock<IRepository>();
mock.Setup(x => x.GetById<Store>(It.IsAny<int>()))
    .Returns(new Store { Value = "Walmart" }); // can't set the ID here

var store = Repository.GetById<Store>(5);
Assert.That(store.Id == 5);

当我告诉 Moq 返回一个新的 Store 实例时,我无法分配 ID,并且单元测试失败。我如何对该属性进行单元测试?我不想更改属性的访问级别,因为我不希望用户手动更改它,但这正是我在这里必须做的事情才能测试它。

I'm using NHibernate to persist this entity:

public class Store
{
    public int Id { get; protected set; }
    public int Name { get; set; }
}

Note how the Id property has a protected setter. This is to prevent users from changing the Id while still allowing NHibernate to assign it an Id when it saves it to the database.

In one of my unit tests, I'm using Moq with the following code to mock out my repository:

var mock = new Mock<IRepository>();
mock.Setup(x => x.GetById<Store>(It.IsAny<int>()))
    .Returns(new Store { Value = "Walmart" }); // can't set the ID here

var store = Repository.GetById<Store>(5);
Assert.That(store.Id == 5);

When I tell Moq to return a new Store instance, I can't assign the ID, and the unit test fails. How do I unit test this property? I don't want to change the access level of the property because I don't want users to change it manually, and yet that's exactly what I have to do here in order to test it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

一个人的夜不怕黑 2024-10-01 13:55:04

只是为了将其作为另一种方法,您可以将setter设置为受保护的内部:

public class Store
{
    public int Id { get; protected internal set; }
    public int Name { get; set; }
}

并使用InternalsVisibleTo属性:

[assembly: InternalsVisibleTo("StoreUnitTests")]

Just to throw this out there as another approach, you could make the setter protected internal:

public class Store
{
    public int Id { get; protected internal set; }
    public int Name { get; set; }
}

and use the InternalsVisibleTo attribute:

[assembly: InternalsVisibleTo("StoreUnitTests")]
喜你已久 2024-10-01 13:55:04

如果您实际上没有测试 Store 类,请模拟它,并使用 SetupGet 方法:

var mock = new Mock<IRepository>();
var mockStore = new Mock<Store>();
mock.Setup(x => x.GetById<Store>(It.IsAny<int>())).Returns(mockStore.Object);

mockStore.SetupGet(s => s.Id).Returns(5);
mockStore.SetupGet(s => s.Value).Returns("Walmart");

var store = Repository.GetById<Store>(5);
Assert.That(store.Id == 5);

If you're not actually testing the Store class, then mock it, and use the SetupGet method:

var mock = new Mock<IRepository>();
var mockStore = new Mock<Store>();
mock.Setup(x => x.GetById<Store>(It.IsAny<int>())).Returns(mockStore.Object);

mockStore.SetupGet(s => s.Id).Returns(5);
mockStore.SetupGet(s => s.Value).Returns("Walmart");

var store = Repository.GetById<Store>(5);
Assert.That(store.Id == 5);
对风讲故事 2024-10-01 13:55:04

在测试项目中创建一个 Store 的子类,允许自定义受保护的属性。

class TestableStore : Store { 
  public int TestableId { 
    get { return Id; }
    set { Id = value; }
  }
}

然后设置单元测试以在需要构造 Store 对象时使用 return this 实例。

mock
  .Setup(x => x.GetById<Store>(It.IsAny<int>()))
  .Returns(new TestableStore { Value = "Walmart", TestableId=42 }); 

In the test project create a child class of Store that allows for customization of the protected properties.

class TestableStore : Store { 
  public int TestableId { 
    get { return Id; }
    set { Id = value; }
  }
}

Then setup your unit tests to use return this instance when you need to construct a Store object.

mock
  .Setup(x => x.GetById<Store>(It.IsAny<int>()))
  .Returns(new TestableStore { Value = "Walmart", TestableId=42 }); 
诗酒趁年少 2024-10-01 13:55:04

当然不是最好的方法,但您也可以使用反射来设置属性,如 Sharp Architecture 的 测试:

namespace SharpArch.Testing.NUnit.Helpers
{
    using System.Reflection;

    using SharpArch.Domain;
    using SharpArch.Domain.DomainModel;

    /// <summary>
    ///     For better data integrity, it is imperitive that the <see cref = "Entity.Id" />
    ///     property is read-only and set only by the ORM.  With that said, some unit tests need 
    ///     Id set to a particular value; therefore, this utility enables that ability.  This class should 
    ///     never be used outside of the testing project; instead, implement <see cref = "IHasAssignedId{IdT}" /> to 
    ///     expose a public setter.
    /// </summary>
    public static class EntityIdSetter
    {
        /// <summary>
        ///     Uses reflection to set the Id of a <see cref = "EntityWithTypedId{IdT}" />.
        /// </summary>
        public static void SetIdOf<TId>(IEntityWithTypedId<TId> entity, TId id)
        {
            // Set the data property reflectively
            var idProperty = entity.GetType().GetProperty("Id", BindingFlags.Public | BindingFlags.Instance);

            Check.Ensure(idProperty != null, "idProperty could not be found");

            idProperty.SetValue(entity, id, null);
        }

        /// <summary>
        ///     Uses reflection to set the Id of a <see cref = "EntityWithTypedId{IdT}" />.
        /// </summary>
        public static IEntityWithTypedId<TId> SetIdTo<TId>(this IEntityWithTypedId<TId> entity, TId id)
        {
            SetIdOf(entity, id);
            return entity;
        }
    }
}

Certainly not the best approach, but you could also use reflection to set the property as is done in Sharp Architecture's tests:

namespace SharpArch.Testing.NUnit.Helpers
{
    using System.Reflection;

    using SharpArch.Domain;
    using SharpArch.Domain.DomainModel;

    /// <summary>
    ///     For better data integrity, it is imperitive that the <see cref = "Entity.Id" />
    ///     property is read-only and set only by the ORM.  With that said, some unit tests need 
    ///     Id set to a particular value; therefore, this utility enables that ability.  This class should 
    ///     never be used outside of the testing project; instead, implement <see cref = "IHasAssignedId{IdT}" /> to 
    ///     expose a public setter.
    /// </summary>
    public static class EntityIdSetter
    {
        /// <summary>
        ///     Uses reflection to set the Id of a <see cref = "EntityWithTypedId{IdT}" />.
        /// </summary>
        public static void SetIdOf<TId>(IEntityWithTypedId<TId> entity, TId id)
        {
            // Set the data property reflectively
            var idProperty = entity.GetType().GetProperty("Id", BindingFlags.Public | BindingFlags.Instance);

            Check.Ensure(idProperty != null, "idProperty could not be found");

            idProperty.SetValue(entity, id, null);
        }

        /// <summary>
        ///     Uses reflection to set the Id of a <see cref = "EntityWithTypedId{IdT}" />.
        /// </summary>
        public static IEntityWithTypedId<TId> SetIdTo<TId>(this IEntityWithTypedId<TId> entity, TId id)
        {
            SetIdOf(entity, id);
            return entity;
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文