[英]Unit testing and mocking domain objects
I have a domain class, which looks like this: 我有一个域类,如下所示:
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);
}
}
I would like to test given methods but I'm stuck. 我想测试给定的方法,但遇到了麻烦。
I have: 我有:
[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
}
In the first test I would like also assert that Languages.Add(skill) method was called but I have no idea how to do it. 在第一个测试中,我还想断言Languages.Add(skill)方法已被调用,但我不知道该怎么做。
In the second test I cannot simply mock Employee object as it is not an interface. 在第二个测试中,我不能简单地模拟Employee对象,因为它不是接口。 I thought about exposing Employee but I read I should not do that just for testing purpose.
我曾考虑过要公开Employee,但我读到我不应该仅仅出于测试目的。
You should tests objects as "black box" without relying on implementation details. 您应该在不依赖实现细节的情况下将对象测试为“黑匣子”。 In your case implementation details is that
Employee
class uses ICollection.Add
method. 在您的情况下,实现细节是
Employee
类使用ICollection.Add
方法。
And you definitely don't need to mock at all in your case. 而且您绝对不需要在您的情况下嘲笑。
Mock only dependencies which will make tests slow or very very very very complex to configure for the test. 仅模拟将使测试变慢或非常非常非常非常复杂的依赖关系,以配置测试。
[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);
}
Use public API Employee
class provide to setup it for the test (back box). 使用公共API
Employee
类提供的为测试进行设置(后框)。
For testing DeleteLanguage
add dummy languages through public API of the class. 为了测试
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.");
}
Did you notice how cumbersome employee.EmployeeLanguages
reads, you can rename property to just employee.Languages
. 您是否注意到了
employee.EmployeeLanguages
读取的麻烦,您可以将属性重命名为employee.Languages
。
For readable assertions I used FluentAssertions library 对于可读的断言,我使用了FluentAssertions库
For domain objects with very simple behavior and no dependencies, mocking isn't strictly needed. 对于行为非常简单且没有依赖关系的域对象,严格不需要模拟。 You can test
Add
simply with this: 您可以使用以下命令测试“
Add
:
//Arrange
var e = new Employee();
var l = new Mock<Language>();
//Act
e.AddLanguage(l.Object);
//Assert
Assert.IsTrue(e.Languages.Contains(l.Object));
Testing in this fashion you can achieve perfectly good code coverage and plenty of confidence that the Employee class works as designed. 以这种方式进行测试,您可以获得完全好的代码覆盖率,并且对Employee类按设计方式工作充满了信心。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.