C# +模拟服务层?

发布于 2024-09-27 19:04:32 字数 2458 浏览 4 评论 0 原文

我刚刚开始使用 Moq 进行单元测试/模拟,并遇到了问题。

我有一个名为“CustomerService”的服务层,其中包含以下代码:

public interface ICustomerService
{
    Customer GetCustomerById(int id);
}

public class CustomerService : ICustomerService
{
    private IRepository<Customer> customerRepository;

    public CustomerService(IRepository<Customer> rep)
    {
        customerRepository = rep;
    }
    public Customer GetCustomerById(int id)
    {
        var customer = customerRepository.Get(x => x.CustomerId == id);

        if (customer == null)
            return null;

        return customer;
    }
}

我的存储库类是通用的,并且如下:

public interface IRepository<T> : IDisposable where T : class
    {
        T Get(Expression<Func<T, bool>> predicate);
    }

    public class Repository<T> : IRepository<T> where T : class
    {
        private ObjectContext context;
        private IObjectSet<T> objectSet;

        public Repository()
            : this(new demonEntities())
        {
        }

        public Repository(ObjectContext ctx)
        {
            context = ctx;
            objectSet = context.CreateObjectSet<T>();
        }

        public T Get(Expression<Func<T, bool>> predicate)
        {
            T entity = objectSet.Where<T>(predicate).FirstOrDefault();

            if (entity == null)
                return null;

            return objectSet.Where<T>(predicate).FirstOrDefault();
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (context != null)
                {
                    context.Dispose();
                    context = null;
                }
            }
        }
    }

现在是我的问题..如何进行单元测试来检查我的 GetCustomerById 是否返回 null?

已经尝试过:

[TestMethod]
public void GetCustomerTest()
{
    const int customerId = 5;

    var mock = new Mock<IRepository<Customer>>();
    mock.Setup(x => x.Get(z => z.CustomerId == customerId))
        .Returns(new Customer());

    var repository = mock.Object;
    var service = new CustomerService(repository);
    var result = service.GetCustomerById(customerId);

    Assert.IsNotNull(result);
}

没有运气......

I have just started playing with unit testing / mocks using Moq, and ran into a problem..

I have a Service layer named "CustomerService" which have following code:

public interface ICustomerService
{
    Customer GetCustomerById(int id);
}

public class CustomerService : ICustomerService
{
    private IRepository<Customer> customerRepository;

    public CustomerService(IRepository<Customer> rep)
    {
        customerRepository = rep;
    }
    public Customer GetCustomerById(int id)
    {
        var customer = customerRepository.Get(x => x.CustomerId == id);

        if (customer == null)
            return null;

        return customer;
    }
}

My repository class is generic, and are following:

public interface IRepository<T> : IDisposable where T : class
    {
        T Get(Expression<Func<T, bool>> predicate);
    }

    public class Repository<T> : IRepository<T> where T : class
    {
        private ObjectContext context;
        private IObjectSet<T> objectSet;

        public Repository()
            : this(new demonEntities())
        {
        }

        public Repository(ObjectContext ctx)
        {
            context = ctx;
            objectSet = context.CreateObjectSet<T>();
        }

        public T Get(Expression<Func<T, bool>> predicate)
        {
            T entity = objectSet.Where<T>(predicate).FirstOrDefault();

            if (entity == null)
                return null;

            return objectSet.Where<T>(predicate).FirstOrDefault();
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (context != null)
                {
                    context.Dispose();
                    context = null;
                }
            }
        }
    }

Now is my question.. How can I make unit test to check whether my GetCustomerById returns null or not?

Already tried:

[TestMethod]
public void GetCustomerTest()
{
    const int customerId = 5;

    var mock = new Mock<IRepository<Customer>>();
    mock.Setup(x => x.Get(z => z.CustomerId == customerId))
        .Returns(new Customer());

    var repository = mock.Object;
    var service = new CustomerService(repository);
    var result = service.GetCustomerById(customerId);

    Assert.IsNotNull(result);
}

without luck...

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

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

发布评论

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

评论(3

夜血缘 2024-10-04 19:04:32

您需要将 Repository.Get 方法设为虚拟,以便 Moq 可以覆盖它并返回您设置的值:

public virtual T Get(Expression<Func<T, bool>> predicate)

并在测试中更改

mock.Setup(x => x.Get(z => z.CustomerId == customerId))
        .Returns(new Customer());

mock.Setup(x => x.Get(It.IsAny<Expression<Func<Customer, bool>>>()))
        .Returns(new Customer());

返回新的 Customer< /code> 对于任何传入的 Expression> 。理想情况下,您会测试特定表达式,但根据对此 SO问题,Moq无法做到这一点。

如果您想测试您的服务层是否没有对存储库返回的 Customer 执行任何意外操作,而不是测试以查看任何 Customer > 返回后,您可以设置一个模拟 Customer (确保将 CustomerId 属性设置为虚拟)并断言返回的 Customer服务层具有预期的属性。

[TestMethod]
public void GetCustomerTest()
{
    const int customerId = 5;

    var mockCustomer = new Mock<Customer>();

    mockCustomer.SetupGet(x => x.CustomerId)
        .Returns(customerId);

    var mock = new Mock<IRepository<Customer>>();

    mock.Setup(x => x.Get(It.IsAny<Expression<Func<Customer, bool>>>()))
        .Returns(mockCustomer.Object);

    var repository = mock.Object;
    var service = new CustomerService(repository);
    var result = service.GetCustomerById(customerId);

    Assert.AreEqual(customerId, result.CustomerId);
}

华泰

You need to make the Repository<T>.Get method virtual so Moq can override it and return the value you set up:

public virtual T Get(Expression<Func<T, bool>> predicate)

and in your test, change

mock.Setup(x => x.Get(z => z.CustomerId == customerId))
        .Returns(new Customer());

to

mock.Setup(x => x.Get(It.IsAny<Expression<Func<Customer, bool>>>()))
        .Returns(new Customer());

which says return a new Customer for any Expression<Func<Customer, bool>> passed in. Ideally you would test a specific expression, but per the accepted answer to this SO question, Moq cannot do this.

If you wanted to test that your service layer was not doing anything unexpected to the Customer returned by the repository, instead of testing to see that any Customer was returned, you could set up a mock Customer (being sure to make the CustomerId property virtual) and assert that the Customer returned by the service layer had the expected properties.

[TestMethod]
public void GetCustomerTest()
{
    const int customerId = 5;

    var mockCustomer = new Mock<Customer>();

    mockCustomer.SetupGet(x => x.CustomerId)
        .Returns(customerId);

    var mock = new Mock<IRepository<Customer>>();

    mock.Setup(x => x.Get(It.IsAny<Expression<Func<Customer, bool>>>()))
        .Returns(mockCustomer.Object);

    var repository = mock.Object;
    var service = new CustomerService(repository);
    var result = service.GetCustomerById(customerId);

    Assert.AreEqual(customerId, result.CustomerId);
}

HTH

極樂鬼 2024-10-04 19:04:32

您必须针对界面创建模拟。

然后,您需要将类中的成员设置为模拟而不是实现者

,然后您需要在为方法创建的模拟上调用 .Setup .. rememver 以使用方法链接使其可验证。

在测试中运行该方法,然后调用mock.Verify()

如果需要,明天将添加代码。我在工作中有大量可以使用的代码示例。

you have to create the mock against the interface.

you then need to set the member in the class to the mock instead of the implementor

you then need to call .Setup on the mock you created for the method .. rememver to use method chaining to make it verifiable.

run the method in your test and then call mock.Verify()

Will add code tomorrow if you require. I have loads of code samples at work i can use.

挽袖吟 2024-10-04 19:04:32

你不能这样做的原因是因为一个 lambda x =>; x.CustomerId == id 不等于另一个 lambda x =>; x.CustomerId == id 因此 Moq 无法匹配它们。

但是,如果您有一个类,其常见操作为:

public class CustomerQueries {
    public static Predicate<Customer> ById(int id) = x => x.CustomerId == id;
}

并在代码和测试中重用此 lambda,那么您的 Moq 应该可以顺利通过。您也可以在类的已知实例上使用一种方法,只要它是完全相同的委托而不仅仅是相似的一个或另一个副本。

PS:您是否考虑过使用 Predicate 代替 Expression> ?更容易阅读和理解其目的。

The reason you can't do this is because one lambda x => x.CustomerId == id is not equal to another lambda x => x.CustomerId == id so Moq can't match them.

However, if you had, say, a class with your common operations on:

public class CustomerQueries {
    public static Predicate<Customer> ById(int id) = x => x.CustomerId == id;
}

and reused this lambda in both code and test, then your Moq should pass without any problem. You could alternatively use a method on a known instance of a class, as long as it's exactly the same delegate and not merely a similar one or another copy.

PS: Have you considered using Predicate<T> instead of Expression<Func<T, bool>>? It's easier to read and to understand the purpose.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文