Created
July 17, 2020 11:23
-
-
Save dburriss/e91bf1baba51094801ff2268c019a073 to your computer and use it in GitHub Desktop.
Blog | Maintainable unit tests | Snippet 1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// tests | |
// test for invalid name omitted... | |
[Fact] | |
public void CreatingPerson_WithValidPerson_CallsIsValid() | |
{ | |
var name = "Bob"; | |
var people = Substitute.For<IPersonRepository>(); | |
var validator = Substitute.For<IPersonValidator>(); | |
var createPerson = new CreatePerson(people, validator); | |
createPerson.With(name); | |
validator.ReceivedWithAnyArgs(1).IsValid(Arg.Any<Person>()); | |
} | |
// anemic domain entity | |
public class Person | |
{ | |
public Person(Guid id, string name) | |
{ | |
Id = id; | |
Name = name; | |
} | |
public Guid Id { get; set; } | |
public string Name { get; set; } | |
} | |
// use-case | |
public class CreatePerson | |
{ | |
private readonly IPersonRepository personRepository; | |
private readonly IPersonValidator personValidator; | |
public CreatePerson(IPersonRepository personRepository, IPersonValidator personValidator) | |
{ | |
this.personRepository = personRepository; | |
this.personValidator = personValidator; | |
} | |
public void With(string name) | |
{ | |
var person = new Person(Guid.NewGuid(), name); | |
if (personValidator.IsValid(person)) | |
{ | |
personRepository.Create(person); | |
} | |
else | |
{ | |
throw new ArgumentException(nameof(name)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class PersonBuilder | |
{ | |
private Guid id; | |
private string name; | |
public PersonBuilder() | |
{ | |
id = Guid.NewGuid(); | |
name = $"name {Guid.NewGuid()}"; | |
} | |
public PersonBuilder With(Guid id) | |
{ | |
this.id = id; | |
return this; | |
} | |
public PersonBuilder With(string name) | |
{ | |
this.name = name; | |
return this; | |
} | |
public Person Build() | |
{ | |
return new Person(id, name); | |
} | |
public static implicit operator Person(PersonBuilder builder) => builder.Build(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Person | |
{ | |
public Person(Guid id, string name) | |
{ | |
Id = id; | |
Name = name; | |
} | |
public bool IsValid() | |
{ | |
if (Id == Guid.Empty) return false; | |
if (string.IsNullOrEmpty(Name)) return false; | |
return true; | |
} | |
public Guid Id { get; } | |
public string Name { get; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// test for invalid name omitted... | |
[Fact] | |
public void CreatePerson_WithValidName_PersistsPerson() | |
{ | |
var name = "Bob"; | |
InMemoryPersonRepository people = Given.People; | |
var createPerson = new CreatePerson(people); | |
createPerson.With(name); | |
Assert.Equal(name, people.All().First().Name); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class CreatePerson | |
{ | |
private readonly IPersonRepository personRepository; | |
public CreatePerson(IPersonRepository personRepository) | |
{ | |
this.personRepository = personRepository; | |
} | |
public void With(string name) | |
{ | |
var person = new Person(Guid.NewGuid(), name); | |
if (person.IsValid()) | |
{ | |
personRepository.Create(person); | |
} | |
else | |
{ | |
throw new ArgumentException(nameof(name)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// person entity | |
public class Person | |
{ | |
public Person(Guid id, string name) | |
{ | |
if (id == Guid.Empty) throw new ArgumentException(nameof(id)); | |
if (string.IsNullOrEmpty(name)) throw new ArgumentException(nameof(name)); | |
Id = id; | |
Name = name; | |
} | |
public Guid Id { get; } | |
public string Name { get; } | |
} | |
// use-case | |
public class CreatePerson | |
{ | |
private readonly IPersonRepository personRepository; | |
public CreatePerson(IPersonRepository personRepository) | |
{ | |
this.personRepository = personRepository; | |
} | |
public void With(string name) | |
{ | |
var person = new Person(Guid.NewGuid(), name); | |
personRepository.Create(person); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class InMemoryPersonRepository : IPersonRepository | |
{ | |
private IDictionary<Guid, Person> data; | |
public InMemoryPersonRepository(IDictionary<Guid, Person> data) | |
{ | |
this.data = data; | |
} | |
public IReadOnlyCollection<Person> All() | |
{ | |
return new List<Person>(data.Values); | |
} | |
public void Create(Person person) | |
{ | |
data.Add(person.Id, person); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class InMemoryPersonRepositoryBuilder | |
{ | |
IDictionary<Guid, Person> data = new Dictionary<Guid, Person>(); | |
public InMemoryPersonRepositoryBuilder With(params PersonBuilder[] people) | |
{ | |
foreach (Person p in people) | |
{ | |
data.Add(p.Id, p); | |
} | |
return this; | |
} | |
public InMemoryPersonRepository Build() | |
{ | |
return new InMemoryPersonRepository(data); | |
} | |
public static implicit operator InMemoryPersonRepository(InMemoryPersonRepositoryBuilder builder) | |
=> builder.Build(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Handles creation of instances useful to testing like entites, value objects, settings, etc. | |
/// </summary> | |
public static class A | |
{ | |
public static PersonBuilder Person => new PersonBuilder(); | |
} | |
/// <summary> | |
/// Handles the creation of builders that build external services for testing | |
/// </summary> | |
public static class Given | |
{ | |
public static InMemoryPersonRepositoryBuilder People => new InMemoryPersonRepositoryBuilder(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
InMemoryPersonRepository people = Given.People.With(A.Person, A.Person, A.Person); | |
// if i wanted another with a specific name | |
people.Create(A.Person.With(name: "Bob")); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment