C# 单元测试和模拟域对象

C# 单元测试和模拟域对象,c#,unit-testing,C#,Unit Testing,我有一个域类,如下所示: public class Employee { public Guid EmployeeId { get; private set; } public string Name { get; private set; } public string Surname { get; private set; } ... // other properties public ICollection<Language>

我有一个域类,如下所示:

public class Employee
{
    public Guid EmployeeId { get; private set; }
    public string Name { get; private set; }
    public string Surname { get; private set; }
    ...
    // other properties

    public ICollection<Language> Languages { get; private set; }
        = new List<Language>();

    public ICollection<Skill> Skills { get; private set; }
        = new List<Skill>();

    public void AddLanguage(Language language)
    {
        if (language == null)
            return;

        Languages.Add(language);
    }

    public void DeleteLanguage(Guid languageId)
    {
        var languageToDelete = Languages
            .SingleOrDefault(x => x.LanguageId == languageId);

        if(languageToDelete == null)
            throw new ArgumentException("Language entry doesn't exist.");

        Languages.Remove(languageToDelete);
    }
}
公共类员工
{
公共Guid EmployeeId{get;private set;}
公共字符串名称{get;private set;}
公共字符串姓氏{get;private set;}
...
//其他属性
公共ICollection语言{get;private set;}
=新列表();
公共ICollection技能{get;private set;}
=新列表();
公共语言(语言)
{
if(语言==null)
返回;
语言。添加(语言);
}
公共语言(Guid languageId)
{
var languageToDelete=语言
.SingleOrDefault(x=>x.LanguageId==LanguageId);
if(languageToDelete==null)
抛出新ArgumentException(“语言条目不存在”);
语言。删除(languageToDelete);
}
}
我想测试给定的方法,但我被卡住了

我有:

    [Fact]
    public void AddLanguage_AfterCallWithValidObject_LanguagesCollectionContainsAddedObject()
    {
        var language = new Mock<Language>();
        var employee = new Employee("Name", "Surname", ...);

        employee.AddLanguage(language.Object);

        Assert.Contains(employee.EmployeeLanguages, x => x.Language.Equals(language.Object));
    }

    [Fact]
    public void DeleteLanguage_WhenLanguageWithGivenIdDoesntExist_ThrowArgumentException()
    {
        var languageToDelete = new Language("English");

        var employee = new Mock<Employee>();
        employee.Setup(x => x.Languages).Returns(new List<Languages>
        {
            new Language("Spanish"),
            new Language("German")
        });

        employee.Object.DeleteLanguage(languageToDelete);

        // Asserts here
    }
[事实]
public void add language\u AfterCallWithValidObject\u language集合contained addedObject()
{
var language=newmock();
var employee=新员工(“姓名”、“姓氏”、…);
employee.AddLanguage(language.Object);
Assert.Contains(employee.EmployeeLanguages,x=>x.Language.Equals(Language.Object));
}
[事实]
public void delete language_当使用给定的语言时,不能使用文本列表_ThrowArgumentException()
{
var languageToDelete=新语言(“英语”);
var employee=new Mock();
employee.Setup(x=>x.Languages).Returns(新列表
{
新语言(“西班牙语”),
新语言(“德语”)
});
employee.Object.DeleteLanguage(languageToDelete);
//在这里断言
}
在第一次测试中,我还要断言调用了Languages.Add(skill)方法,但我不知道如何执行

  • 这是一种优雅的方式吗?我曾考虑过mockingAdd方法,但我不确定这是否是个好主意
  • 在第二个测试中,我不能简单地模拟Employee对象,因为它不是一个接口。 我曾考虑过让员工曝光,但我读到我不应该仅仅为了测试而这样做

  • 如何模拟语言属性而不将员工暴露为界面?可能吗?做这种事有什么好的习惯吗
  • 我测试这些方法的一般概念可以吗?(我是单元测试新手)

  • 对于行为非常简单且没有依赖关系的域对象,不严格需要模拟。您只需使用以下命令即可测试
    添加

    //Arrange
    var e = new Employee();
    var l = new Mock<Language>(); 
    
    //Act
    e.AddLanguage(l.Object);
    
    //Assert
    Assert.IsTrue(e.Languages.Contains(l.Object));
    
    //排列
    var e=新员工();
    var l=新的Mock();
    //表演
    e、 AddLanguage(l.Object);
    //断言
    IsTrue(e.Languages.Contains(l.Object));
    

    通过这种方式进行测试,您可以获得非常好的代码覆盖率,并对Employee类按设计工作充满信心。

    对于行为非常简单且没有依赖关系的域对象,不需要严格模拟。您只需使用以下命令即可测试
    添加

    //Arrange
    var e = new Employee();
    var l = new Mock<Language>(); 
    
    //Act
    e.AddLanguage(l.Object);
    
    //Assert
    Assert.IsTrue(e.Languages.Contains(l.Object));
    
    //排列
    var e=新员工();
    var l=新的Mock();
    //表演
    e、 AddLanguage(l.Object);
    //断言
    IsTrue(e.Languages.Contains(l.Object));
    

    通过这种方式进行测试,您可以获得非常好的代码覆盖率,并对Employee类按设计工作充满信心。

    您应该将对象作为“黑盒”进行测试,而不依赖于实现细节。在您的例子中,实现细节是
    Employee
    类使用
    ICollection.Add
    方法

    在你的情况下,你完全不需要嘲笑。
    仅模拟依赖项,这将使测试速度变慢或测试配置非常复杂。

    [Fact]
    public void AddLanguage_ShouldSaveGivenLanguage()
    {
        var language = new Language();
        var employee = new Employee("Name", "Surname");
    
        employee.AddLanguage(language);
    
        var expectedLanguages = new[] { language };
        employee.EmployeeLanguages.Should().BeEquivalentTo(expectedLanguages);
    }
    
    使用公共API
    Employee
    classprovide为测试(后台)设置它。
    用于测试
    DeleteLanguage
    通过类的公共API添加虚拟语言

    [Fact]
    public void DeleteLanguage_WhenLanguageExists_Remove()
    {
        var language1 = new Language("German");
        var language2 = new Language("French");
        var languageToDelete = new Language("English");
    
        var employee = new Employee("Name", "Surname");
        employee.AddLanguage(language1);
        employee.AddLanguage(language2);
        employee.AddLanguage(languageToDelete);
    
        employee.DeleteLanguage(languageToDelete);
    
        var expectedLanguages = new[] { language1, language2 };
        employee.EmployeeLanguages.Should().BeEquivalentTo(expectedLanguages);
    }
    
    [Fact]
    public void DeleteLanguage_WhenLanguageNotExists_ThrowException()
    {
        var language1 = new Language("German");
        var language2 = new Language("French");
        var notExistedLanguage = new Language("English");
    
        var employee = new Employee("Name", "Surname");
        employee.AddLanguage(language1);
        employee.AddLanguage(language2);
    
        Action delete = () => employee.DeleteLanguage(languageToDelete);
    
        delete.Should()
              .Throw<ArgumentException>()
              .WithMessage("Language entry doesn't exist.");
    }
    
    [事实]
    public void DeleteLanguage_当语言存在时_Remove()
    {
    var language1=新语言(“德语”);
    var language2=新语言(“法语”);
    var languageToDelete=新语言(“英语”);
    var员工=新员工(“姓名”);
    employee.AddLanguage(语言1);
    employee.AddLanguage(语言2);
    employee.AddLanguage(languageToDelete);
    employee.DeleteLanguage(languageToDelete);
    var expectedLanguages=new[]{language1,language2};
    employee.EmployeeLanguages.Should().BeEquivalentTo(expectedLanguages);
    }
    [事实]
    public void DeleteLanguage_,当Languagenote通过Exception()存在时
    {
    var language1=新语言(“德语”);
    var language2=新语言(“法语”);
    var notExistedLanguage=新语言(“英语”);
    var员工=新员工(“姓名”);
    employee.AddLanguage(语言1);
    employee.AddLanguage(语言2);
    Action delete=()=>employee.DeleteLanguage(languageToDelete);
    删除.Should()
    
    .Throw

    您应该将对象作为“黑盒”进行测试,而不依赖于实现细节。在您的示例中,实现细节是
    Employee
    类使用
    ICollection.Add
    方法

    在你的情况下,你完全不需要嘲笑。
    仅模拟依赖项,这将使测试速度变慢或测试配置非常复杂。

    [Fact]
    public void AddLanguage_ShouldSaveGivenLanguage()
    {
        var language = new Language();
        var employee = new Employee("Name", "Surname");
    
        employee.AddLanguage(language);
    
        var expectedLanguages = new[] { language };
        employee.EmployeeLanguages.Should().BeEquivalentTo(expectedLanguages);
    }
    
    使用公共API
    Employee
    classprovide为测试(后台)设置它。
    用于测试
    DeleteLanguage
    通过类的公共API添加虚拟语言

    [Fact]
    public void DeleteLanguage_WhenLanguageExists_Remove()
    {
        var language1 = new Language("German");
        var language2 = new Language("French");
        var languageToDelete = new Language("English");
    
        var employee = new Employee("Name", "Surname");
        employee.AddLanguage(language1);
        employee.AddLanguage(language2);
        employee.AddLanguage(languageToDelete);
    
        employee.DeleteLanguage(languageToDelete);
    
        var expectedLanguages = new[] { language1, language2 };
        employee.EmployeeLanguages.Should().BeEquivalentTo(expectedLanguages);
    }
    
    [Fact]
    public void DeleteLanguage_WhenLanguageNotExists_ThrowException()
    {
        var language1 = new Language("German");
        var language2 = new Language("French");
        var notExistedLanguage = new Language("English");
    
        var employee = new Employee("Name", "Surname");
        employee.AddLanguage(language1);
        employee.AddLanguage(language2);
    
        Action delete = () => employee.DeleteLanguage(languageToDelete);
    
        delete.Should()
              .Throw<ArgumentException>()
              .WithMessage("Language entry doesn't exist.");
    }
    
    [事实]
    public void DeleteLanguage_当语言存在时_Remove()
    {
    var language1=新语言(“德语”);
    var language2=新语言(“法语”);
    var languageToDelete=新语言(“英语”);
    var员工=新员工(“姓名”);
    employee.AddLanguage(语言1);
    employee.AddLanguage(语言2