C# 如何在单元测试中使用 Moq 调用同一类中的另一个方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16538192/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to use Moq in unit test that calls another method in same class
提问by user2227138
Hi I am new to Moq framework and have some questions about how to use it. I will give an example and hope for answers.
嗨,我是 Moq 框架的新手,对如何使用它有一些疑问。我会举一个例子,希望得到答案。
I have two classes, an interface and and an implementation:
我有两个类,一个接口和一个实现:
public class Vehicle{
public string RegistrationNumber {get; set;}
public long VehicleIdentifier { get; set; }
public Tyre TyreSpecification { get; set; }
}
public class Tyre {
public long NumberOfTyres {get; set;}
public long TyreSize { get; set;}
}
public interface ISelecter {
Vehicle GetVehicleByRegistrationNumber(string registrationNumber);
Tyre GetTyreSpecification(long vehicleIdentifier);
}
public class Selecter : ISelecter
{
public Vehicle GetVehicleByRegistrationNumber(string registrationNumber)
{
var vehicle = 'Database will give us the vehicle specification';
//Then we do things with the vehicle object
//Get the tyre specification
vehicle.TyreSpecification = GetTyreSpecification(vehicle.VehicleIdentifier);
return vehicle;
}
public Tyre GetTyreSpecification(long vehicleIdentifier)
{
var tyre = 'external manufacture system gets the tyre specification';
//Then do thing with the tyre before returning the object
return tyre;
}
}
I want to write two tests for those methods. The problem is when I write the test for GetVehicleByRegistrationNumberI do not know how to mock the method call to GetTyreSpecification.
我想为这些方法编写两个测试。问题是当我编写测试时,GetVehicleByRegistrationNumber我不知道如何模拟对GetTyreSpecification.
The test methods look like this:
测试方法如下所示:
[TestClass]
public class SelecterTest
{
[TestMethod]
public void GetTyreSpecification_test()
{
//Arrange
var tyre = new Tyre { NumberOfTyres = 4, TyreSize = 18 };
var mockSelecter = new Mock<ISelecter>();
mockSelecter.SetUp(s=>s.GetTyreSpecification(It.IsAny<long>())).Returns(tyre);
//Act
var tyreSpec = mockSelecter.Object.GetTyreSpecification(123456);
//Assert
Assert.IsTrue(tyreSpec.NumberOfTyres == 4 && tyreSpec.TyreSize == 18);
}
[TestMethod]
public void GetVehicleByRegistrationNumber_test()
{
//Arrange
var vehicle= new Vehicle { VehicleIdentifier = 123456, RegistrationNumber = ABC123, TyreSpecification = new Tyre { Tyresize = 18, NumberOfTyres = 4 }};
var mockSelecter = new Mock<ISelecter>();
mockSelecter.SetUp(s=>s.GetVehicleByRegistrationNumber(It.IsAny<string> ())).Returns(vehicle);
//Act
var vehicle = mockSelecter.Object.GetVehicleByregistrationNumber(123456);
//Assert
Assert.IsTrue(vehicle.Registrationnumber == "ABC123";
}
}
In the test method GetVehicleByRegistrationNumber_testhow do I mock the call to getTyreSpecification?
在测试方法中GetVehicleByRegistrationNumber_test,我如何模拟对 的调用getTyreSpecification?
采纳答案by levelnis
You shouldn't be trying to mock a method on the class you're trying to test. Mocking frameworks are used to replace the actual calls made to dependencies that your class takes in with fake calls so that you can focus on testing the behaviour of your class without being distracted by external dependencies that it has.
您不应该试图在要测试的类上模拟方法。模拟框架用于用假调用替换对您的类所接受的依赖项的实际调用,以便您可以专注于测试您的类的行为,而不会被它具有的外部依赖项分心。
There are no external dependencies taken in by your Selecterclass so you don't need to mock anything. I would always advocate not mocking if you don't have to and testing the actual code itself. Obviously, to keep your test atomic, you would need to mock calls to external dependencies if there were any.
您的Selecter类没有采用任何外部依赖项,因此您无需模拟任何内容。如果您不需要并测试实际代码本身,我将始终主张不要嘲笑。显然,为了保持测试的原子性,您需要模拟对外部依赖项的调用(如果有的话)。
回答by Sathish
var mockSelecter = new Mock<ISelecter>{ CallBase = true };
mockSelecter.SetUp(s=>s.GetTyreSpecification(It.IsAny<long>())).Returns(tyre);
回答by Nkosi
The focus on mocking the class under test has blinded you to the actual problem.
对模拟被测类的关注使您对实际问题视而不见。
From the comments in the class under test...
从被测班级的评论...
- 'Database will give us the vehicle specification'
- 'external manufacture system gets the tyre specification'
- '数据库将为我们提供车辆规格'
- '外部制造系统获取轮胎规格'
you actually expose two dependencies that should be injected into the class.
您实际上公开了两个应该注入到类中的依赖项。
For the purpose of explaining this answer lets say those dependencies looked like this.
为了解释这个答案,让我们说这些依赖关系看起来像这样。
public interface IDatabase {
Vehicle GetVehicleByRegistrationNumber(string registrationNumber);
}
public interface IExternalManufactureSystem {
Tyre GetTyreSpecification(long vehicleIdentifier);
}
That would mean that the Selecterwould need to be refactored to expect those dependencies.
这意味着Selecter需要重构以期望这些依赖项。
public class Selecter : ISelecter {
private IDatabase database;
private IExternalManufactureSystem externalManufactureSystem;
public Selecter(IDatabase database, IExternalManufactureSystem externalManufactureSystem) {
this.database = database;
this.externalManufactureSystem = externalManufactureSystem;
}
public Vehicle GetVehicleByRegistrationNumber(string registrationNumber) {
//'Database will give us the vehicle specification'
var vehicle = database.GetVehicleByRegistrationNumber(registrationNumber);
//Then we do things with the vehicle object
//Get the tyre specification
vehicle.TyreSpecification = GetTyreSpecification(vehicle.VehicleIdentifier);
return vehicle;
}
public Tyre GetTyreSpecification(long vehicleIdentifier) {
//'external manufacture system gets the tyre specification'
var tyre = externalManufactureSystem.GetTyreSpecification(vehicleIdentifier);
//Then do thing with the tyre before returning the object
return tyre;
}
}
From there it would then be a matter of mocking only the dependencies explicitly needed to test the behavior of the method under test.
从那里开始,只模拟测试被测方法的行为所需的依赖项。
selecter.GetTyreSpecificationhas no need to access the database so there is no reason to mock and inject it for the test.
selecter.GetTyreSpecification无需访问数据库,因此没有理由模拟并注入它进行测试。
[TestMethod]
public void GetTyreSpecification_test() {
//Arrange
var vehicleIdentifier = 123456;
var expected = new Tyre { NumberOfTyres = 4, TyreSize = 18 };
var mockSystem = new Mock<IExternalManufactureSystem>();
mockSystem.Setup(s => s.GetTyreSpecification(vehicleIdentifier)).Returns(expected);
var selecter = new Selecter(null, mockSystem.Object);
//Act
var actual = selecter.GetTyreSpecification(vehicleIdentifier);
//Assert
Assert.AreEqual(expected, actual);
}
selecter.GetVehicleByRegistrationNumberhowever needs to be able to get the tyre specification from the other method so this test would need both dependencies mocked in order for it to be exercised to completion.
selecter.GetVehicleByRegistrationNumber但是需要能够从另一种方法中获取轮胎规格,因此该测试需要模拟两个依赖项,以便完成它。
[TestMethod]
public void GetVehicleByRegistrationNumber_test() {
//Arrange
var vehicleIdentifier = 123456;
var registrationNumber = "ABC123";
var tyre = new Tyre { TyreSize = 18, NumberOfTyres = 4 };
var expected = new Vehicle {
VehicleIdentifier = vehicleIdentifier,
RegistrationNumber = registrationNumber,
TyreSpecification = tyre
};
var mockSystem = new Mock<IExternalManufactureSystem>();
mockSystem.Setup(s => s.GetTyreSpecification(vehicleIdentifier)).Returns(tyre);
var mockDatabase = new Mock<IDatabase>();
mockDatabase.Setup(s => s.GetVehicleByRegistrationNumber(registrationNumber)).Returns(expected);
var selecter = new Selecter(mockDatabase.Object, mockSystem.Object);
//Act
var actual = selecter.GetVehicleByRegistrationNumber(registrationNumber);
//Assert
Assert.IsTrue(actual.RegistrationNumber == registrationNumber);
}
Now with that out of the way, if for example the Selecterclass had the GetVehicleByRegistrationNumberas a virtualmethod,
现在有了这样的方式,例如,如果Selecter该类具有GetVehicleByRegistrationNumber作为virtual方法,
public virtual Tyre GetTyreSpecification(long vehicleIdentifier) {
//...code removed for brevity.
}
There is a way you can use moq to stub the subject under test and mock that method for testing. This is not always the best design and is considered a code smell. However there are situations where you will end up in this particular scenario.
有一种方法可以使用 moq 来存根测试对象并模拟该方法进行测试。这并不总是最好的设计,被认为是一种代码异味。但是,在某些情况下,您最终会遇到这种特殊情况。
[TestMethod]
public void GetVehicleByRegistrationNumber_test2() {
//Arrange
var vehicleIdentifier = 123456;
var registrationNumber = "ABC123";
var tyre = new Tyre { TyreSize = 18, NumberOfTyres = 4 };
var expected = new Vehicle {
VehicleIdentifier = vehicleIdentifier,
RegistrationNumber = registrationNumber,
TyreSpecification = tyre
};
var mockDatabase = new Mock<IDatabase>();
mockDatabase.Setup(s => s.GetVehicleByRegistrationNumber(registrationNumber)).Returns(expected);
var selecter = new Mock<Selecter>(mockDatabase.Object, null) {
CallBase = true //So that base methods that are not setup can be called.
}
selecter.Setup(s => s.GetTyreSpecification(vehicleIdentifier)).Returns(tyre);
//Act
var actual = selecter.Object.GetVehicleByRegistrationNumber(registrationNumber);
//Assert
Assert.IsTrue(actual.RegistrationNumber == registrationNumber);
}
In the above example, when selecter.Object.GetVehicleByRegistrationNumber(registrationNumber)is called, the base Selecterwrapped by the mock will be called, which in turn will then call the mocked GetTyreSpecificationthat was overridden by the setup on the mocked subject under test.
在上面的示例中,当selecter.Object.GetVehicleByRegistrationNumber(registrationNumber)被调用时,Selecter将调用由模拟包装的基础,然后它将调用GetTyreSpecification被测试对象上的设置覆盖的模拟。
You tend to see this when testing abstract classes with implemented members that have dependencies on abstract members.
在测试具有依赖于抽象成员的已实现成员的抽象类时,您往往会看到这一点。
回答by Maddy
In general we use mocks for external dependencies/other object/interface calls being used within our class for which we will write unit tests. So when you are writing test for one of your function which internally makes a call to another function within the same class you don't have to mock that function call. However in the internal function if you are making call to an external interface then you will have to mock the external interface instance and write your unit test with expected result
通常,我们将模拟用于我们将为其编写单元测试的类中使用的外部依赖项/其他对象/接口调用。因此,当您为某个函数编写测试时,该函数在内部调用同一类中的另一个函数,您不必模拟该函数调用。但是,在内部函数中,如果您要调用外部接口,则必须模拟外部接口实例并使用预期结果编写单元测试

