我已经看过很多关于“嘲弄私人方法”的帖子和问题,但仍然无法使其发挥作用而且找不到真正的答案。
让我们忘记代码的味道,你不应该这样做....
根据我的理解,我做了以下事情:
1)创建了一个类库“MyMoqSamples”
2)添加了对Moq和NUnit的引用
3)编辑AssemblyInfo文件并添加
[assembly:InternalsVisibleTo(“DynamicProxyGenAssembly2”)]
[assembly:InternalsVisibleTo(“MyMoqSamples”)]
4)现在需要测试私有方法。因为它是私有方法,所以它不是接口的一部分。
5)添加以下代码
[TestFixture]
public class Can_test_my_private_method
{
[Test]
public void Should_be_able_to_test_my_private_method()
{
// TODO how do I test my DoSomthing method?
}
}
public class CustomerInfo
{
public string Name { get; set; }
public string Surname { get; set; }
}
public interface ICustomerService
{
List<CustomerInfo> GetCustomers();
}
public class CustomerService : ICustomerService
{
public List<CustomerInfo> GetCustomers()
{
return new List<CustomerInfo> { new CustomerInfo { Surname = "Bloggs", Name = "Jo" } };
}
protected virtual void DoSomething()
{
}
}
你能举例说明你如何测试我的私有方法吗?
非常感谢
您描述的步骤将Moq设置为测试内部类和成员,因此与测试受保护或私有方法没有任何关系
测试私有方法有点气味,你应该真正测试公共API。如果您认为该方法非常重要并且需要单独进行测试,那么它是否应该属于自己的类,然后可以自行测试?
如果您的心脏已经开始测试上面的受保护方法,那么您可以在测试程序集中滚动自己的Mock:
public class CustomerServiceMock : CustomerService {
public void DoSomethingTester() {
// Set up state or whatever you need
DoSomething();
}
}
[TestMethod]
public void DoSomething_WhenCalled_DoesSomething() {
CustomerServiceMock serviceMock = new CustomerServiceMock(...);
serviceMock.DoSomethingTester();
}
如果它是私人的,你可能会做一些狡猾的反思,但走这条路是测试地狱的方法。
更新
虽然你已经在你的问题中给出了示例代码,但我并没有真正看到你想要如何“测试”受保护的方法,所以我会想出一些人为的...
假设您的客户服务如下: -
public CustomerService : ICustomerService {
private readonly ICustomerRepository _repository;
public CustomerService(ICustomerRepository repository) {
_repository = repository;
}
public void MakeCustomerPreferred(Customer preferred) {
MakePreferred(customer);
_repository.Save(customer);
}
protected virtual void MakePreferred(Customer customer) {
// Or more than likely some grungy logic
customer.IsPreferred = true;
}
}
如果您想测试受保护的方法,您可以执行以下操作: -
[TestClass]
public class CustomerServiceTests {
CustomerServiceTester customerService;
Mock<ICustomerRepository> customerRepositoryMock;
[TestInitialize]
public void Setup() {
customerRepoMock = new Mock<ICustomerRepository>();
customerService = new CustomerServiceTester(customerRepoMock.Object);
}
public class CustomerServiceTester : CustomerService {
public void MakePreferredTest(Customer customer) {
MakePreferred(customer);
}
// You could also add in test specific instrumentation
// by overriding MakePreferred here like so...
protected override void MakePreferred(Customer customer) {
CustomerArgument = customer;
WasCalled = true;
base.MakePreferred(customer);
}
public Customer CustomerArgument { get; set; }
public bool WasCalled { get; set; }
}
[TestMethod]
public void MakePreferred_WithValidCustomer_MakesCustomerPreferred() {
Customer customer = new Customer();
customerService.MakePreferredTest(customer);
Assert.AreEqual(true, customer.IsPreferred);
}
// Rest of your tests
}
这个“模式”的名称是Test特定的子类(基于xUnit测试模式术语),您可能希望在此处看到更多信息: -
http://xunitpatterns.com/Test-Specific%20Subclass.html
根据您的评论和之前的问题,您似乎已经承担了对某些遗留代码进行单元测试(或自己做出决定)的任务。在这种情况下,所有遗留代码的圣经都是Michael Feathers的书。它涵盖了这样的技术以及重构和技术,以处理将“不可测试的”类和方法分解为更易于管理的东西,我强烈推荐它。
您描述的步骤将Moq设置为测试内部类和成员,因此与测试受保护或私有方法没有任何关系
测试私有方法有点气味,你应该真正测试公共API。如果您认为该方法非常重要并且需要单独进行测试,那么它是否应该属于自己的类,然后可以自行测试?
如果您的心脏已经开始测试上面的受保护方法,那么您可以在测试程序集中滚动自己的Mock:
public class CustomerServiceMock : CustomerService {
public void DoSomethingTester() {
// Set up state or whatever you need
DoSomething();
}
}
[TestMethod]
public void DoSomething_WhenCalled_DoesSomething() {
CustomerServiceMock serviceMock = new CustomerServiceMock(...);
serviceMock.DoSomethingTester();
}
如果它是私人的,你可能会做一些狡猾的反思,但走这条路是测试地狱的方法。
更新
虽然你已经在你的问题中给出了示例代码,但我并没有真正看到你想要如何“测试”受保护的方法,所以我会想出一些人为的...
假设您的客户服务如下: -
public CustomerService : ICustomerService {
private readonly ICustomerRepository _repository;
public CustomerService(ICustomerRepository repository) {
_repository = repository;
}
public void MakeCustomerPreferred(Customer preferred) {
MakePreferred(customer);
_repository.Save(customer);
}
protected virtual void MakePreferred(Customer customer) {
// Or more than likely some grungy logic
customer.IsPreferred = true;
}
}
如果您想测试受保护的方法,您可以执行以下操作: -
[TestClass]
public class CustomerServiceTests {
CustomerServiceTester customerService;
Mock<ICustomerRepository> customerRepositoryMock;
[TestInitialize]
public void Setup() {
customerRepoMock = new Mock<ICustomerRepository>();
customerService = new CustomerServiceTester(customerRepoMock.Object);
}
public class CustomerServiceTester : CustomerService {
public void MakePreferredTest(Customer customer) {
MakePreferred(customer);
}
// You could also add in test specific instrumentation
// by overriding MakePreferred here like so...
protected override void MakePreferred(Customer customer) {
CustomerArgument = customer;
WasCalled = true;
base.MakePreferred(customer);
}
public Customer CustomerArgument { get; set; }
public bool WasCalled { get; set; }
}
[TestMethod]
public void MakePreferred_WithValidCustomer_MakesCustomerPreferred() {
Customer customer = new Customer();
customerService.MakePreferredTest(customer);
Assert.AreEqual(true, customer.IsPreferred);
}
// Rest of your tests
}
这个“模式”的名称是Test特定的子类(基于xUnit测试模式术语),您可能希望在此处看到更多信息: -
http://xunitpatterns.com/Test-Specific%20Subclass.html
根据您的评论和之前的问题,您似乎已经承担了对某些遗留代码进行单元测试(或自己做出决定)的任务。在这种情况下,所有遗留代码的圣经都是Michael Feathers的书。它涵盖了这样的技术以及重构和技术,以处理将“不可测试的”类和方法分解为更易于管理的东西,我强烈推荐它。
您的问题似乎有两个部分。
如何模拟受保护的方法:
http://blogs.clariusconsulting.net/kzu/mocking-protected-members-with-moq/
如何在测试中触发此受保护/私有行为的调用
这里的答案是你通过公开的东西触发它。如果你想强制它直接发生(即,实际上调用一些东西 protected
直接没有中间帮助器,你需要使用反射。这是设计 - 语言提供保护机制作为强制封装的一种方式。
我建议你考虑一下你在试验中试图展示/证明的内容。如果你发现自己编写了一个复杂的测试,那么你就是在做错了。也许您想要做的事情可以分解为独立测试?也许您正在编写集成测试,而不是单元测试?但是有很多关于不良测试的文章,当你学会编写好的测试时,更多的文章对你有意义。我最喜欢的两个是 http://www.codethinked.com/post/2009/06/30/What-is-Unit-Testing.aspx 和 http://www.codethinked.com/post/2009/11/05/Ite28099s-Okay-To-Write-Unit-Tests.aspx