C# 单元测试中的测试方法

C# 单元测试中的测试方法,c#,unit-testing,nunit,C#,Unit Testing,Nunit,我有如下服务。假设我想测试Create()方法。我在单元测试中读到,我应该通过比较、计数等方式进行测试。然后如何测试我的Create()方法。将返回类型从void Create更改为bool Create仅仅是为了能够检查方法输出以进行测试,这是丑陋的还是不理想的想法?你能提出一些建议吗 public class CreateCarService : ICreateCarService { private readonly ICarQuery _carQuery; private

我有如下
服务
。假设我想测试
Create()
方法。我在
单元测试
中读到,我应该通过
比较、计数等方式进行测试。然后如何测试我的
Create()
方法。将返回类型从
void Create
更改为
bool Create
仅仅是为了能够检查方法输出以进行测试,这是丑陋的还是不理想的想法?你能提出一些建议吗

public class CreateCarService : ICreateCarService
{
    private readonly ICarQuery _carQuery;
    private readonly ICarRepository _carRepository;

    public CreateCarService(ICarQuery carQuery, ICarRepository carRepository)
    {
        _carQuery = carQuery;
        _carRepository = carRepository;
    }

    public void Create(Car car)
    {
        if (car == null) throw new CusException(Error, "Car object cannot be null");

        if (_carQuery.IsLoginExist(car.Login))
            throw new CusException(Error, "Message1");

        if (_carQuery.IsEmailExist(car.Email))
            throw new CusException(Error, "Message1");

        _carRepository.Add(car);
    }
}
您希望测试被测试成员的“预期行为”。由于被测成员不返回任何可验证的输出,并且依赖于外部抽象,因此您应该能够监视被测成员与该外部抽象的交互,并验证预期行为

一个这样的例子

public void CreateCarService_Create_Should_Add_Car() {    
    //Arrange
    Car car = new Car {
        Login = "Login",
        Email = "Email"
    };

    ICarQuery carQuery = Mock.Of<ICarQuery>();
    ICarRepository carRepository = Mock.Of<ICarRepository>();

    ICreateCarService subject = new CreateCarService(carQuery, carRepository);

    //Act
    subject.Create(car);

    //Assert
    Mock.Get(carRepository).Verify(_ => _.Add(car), Times.Once);
}
public void CreateCarService\u Create\u应该\u Add\u Car(){
//安排
汽车{
Login=“Login”,
Email=“电子邮件”
};
ICarQuery carQuery=Mock.Of();
ICarRepository carRepository=Mock.Of();
ICreateCarService subject=新创建的CarService(carQuery、carRepository);
//表演
主题。创建(汽车);
//断言
Mock.Get(carRepository).Verify(=>u.Add(car),Times.one);
}
上面的示例安全地导航到被测试成员的末尾,但假设您想要测试针对null参数情况引发的异常

public void CreateCarService_Create_Should_Throw_CusException_For_Null_Car() {    
    //Arrange        
    ICreateCarService subject = new CreateCarService(null, null);

    //Act
    Action act = ()=> subject.Create(null);

    //Assert
    var ex = Assert.Throws<CusException>(act);        
}
public void CreateCarService\u Create\u应该为\u Null\u Car()抛出\u CusException\u{
//安排
ICreateCarService subject=newcreatecarservice(null,null);
//表演
Action act=()=>subject.Create(空);
//断言
var ex=Assert.Throws(act);
}
您希望为通过被测成员的所有可能路径创建测试。因此,花些时间复习测试中的主题,并制定出可能的测试用例。安排受试者满足这些案例,并练习这些案例以验证预期行为

参考以更好地了解如何使用Moq模拟框架。

您希望测试被测成员的“预期行为”。由于被测成员不返回任何可验证的输出,并且依赖于外部抽象,因此您应该能够监视被测成员与该外部抽象的交互,并验证预期行为

一个这样的例子

public void CreateCarService_Create_Should_Add_Car() {    
    //Arrange
    Car car = new Car {
        Login = "Login",
        Email = "Email"
    };

    ICarQuery carQuery = Mock.Of<ICarQuery>();
    ICarRepository carRepository = Mock.Of<ICarRepository>();

    ICreateCarService subject = new CreateCarService(carQuery, carRepository);

    //Act
    subject.Create(car);

    //Assert
    Mock.Get(carRepository).Verify(_ => _.Add(car), Times.Once);
}
public void CreateCarService\u Create\u应该\u Add\u Car(){
//安排
汽车{
Login=“Login”,
Email=“电子邮件”
};
ICarQuery carQuery=Mock.Of();
ICarRepository carRepository=Mock.Of();
ICreateCarService subject=新创建的CarService(carQuery、carRepository);
//表演
主题。创建(汽车);
//断言
Mock.Get(carRepository).Verify(=>u.Add(car),Times.one);
}
上面的示例安全地导航到被测试成员的末尾,但假设您想要测试针对null参数情况引发的异常

public void CreateCarService_Create_Should_Throw_CusException_For_Null_Car() {    
    //Arrange        
    ICreateCarService subject = new CreateCarService(null, null);

    //Act
    Action act = ()=> subject.Create(null);

    //Assert
    var ex = Assert.Throws<CusException>(act);        
}
public void CreateCarService\u Create\u应该为\u Null\u Car()抛出\u CusException\u{
//安排
ICreateCarService subject=newcreatecarservice(null,null);
//表演
Action act=()=>subject.Create(空);
//断言
var ex=Assert.Throws(act);
}
您希望为通过被测成员的所有可能路径创建测试。因此,花些时间复习测试中的主题,并制定出可能的测试用例。安排受试者满足这些案例,并练习这些案例以验证预期行为


参考以更好地理解如何使用Moq模拟框架。

您可以通过使用方法设置
IsLoginExist
IsEmailExist
方法的
Moq
行为来验证对于任何有效的
Car
实例
Add
方法只调用了一次

[TestFixture]
公开课考试
{
[测试]
public void CreateCarServiceTest()
{
var carQueryMock=new Mock();
var carRepositoryMock=新Mock();
var createCarService=new createCarService(carQueryMock.Object,carRepositoryMock.Object);
Setup(c=>c.IsLoginExist(It.IsAny())。返回(false);
Setup(c=>c.IsEmailExist(It.IsAny())。返回(false);
createCarService.Create(新车());
carRepositoryMock.Verify(c=>c.Add(It.IsAny()),Times.Once);
}
}
Create
方法抛出异常时,检查否定情况也是有意义的

[测试]
公共无效CreateCarNegativeTest()
{
var carQueryMock=new Mock();
var carRepositoryMock=新Mock();
var createCarService=new createCarService(carQueryMock.Object,carRepositoryMock.Object);
抛出(()=>createCarService.Create(null));
Setup(c=>c.IsLoginExist(It.IsAny())。返回(true);
Assert.Throws(()=>createCarService.Create(newcar());
Setup(c=>c.IsLoginExist(It.IsAny())。返回(false);
Setup(c=>c.IsEmailExist(It.IsAny())。返回(true);
Assert.Throws(()=>createCarService.Create(newcar());
}

您可以将此方法拆分为不同的测试,以便每个测试都有一个
断言
,或者将参数传递给它。

您可以验证对于任何有效的
Car
实例
Add
方法只调用了一次,通过使用方法设置
IsLoginExist
IsEmailExist
方法的
Moq
行为

[TestFixture]
公开课考试
{
[测试]
public void CreateCarServiceTest()
{
var carQueryMock=new Mock();
var carRepositoryMock=新Mock();
var createCarService=new createCarService(carQueryMock.Object,carRepositoryMock.Object);
Setup(c=>c.IsLoginExist(It.IsAny())。返回