Skip to content

Instantly share code, notes, and snippets.

@dburriss
Created July 17, 2020 11:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dburriss/e91bf1baba51094801ff2268c019a073 to your computer and use it in GitHub Desktop.
Save dburriss/e91bf1baba51094801ff2268c019a073 to your computer and use it in GitHub Desktop.
Blog | Maintainable unit tests | Snippet 1
// 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));
}
}
}
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();
}
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; }
}
// 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);
}
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));
}
}
}
// 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);
}
}
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);
}
}
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();
}
/// <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();
}
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