Skip to content

Instantly share code, notes, and snippets.

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 hlaueriksson/61bbc512efaf3ecd9dd6622306843c13 to your computer and use it in GitHub Desktop.
Save hlaueriksson/61bbc512efaf3ecd9dd6622306843c13 to your computer and use it in GitHub Desktop.
2016-11-30-entities-and-value-objects-in-csharp-for-ddd
using ConductOfCode.Domain;
using Machine.Specifications;
using Ploeh.AutoFixture;
namespace ConductOfCode.Specs
{
public class AutoFixtureSpecs
{
It AutoFixture_can_not_create_an_instance_if_validation_fails = () =>
{
var fixture = new Fixture();
var exception = Catch.Exception(() =>
fixture.Create<Customer>()
);
exception.ShouldContainErrorMessage("Exception has been thrown by the target of an invocation.");
exception.InnerException.ShouldContainErrorMessage("Email is invalid");
};
It AutoFixture_can_not_create_an_instance_using__With__if_the_property_is_read_only = () =>
{
var fixture = new Fixture();
var exception = Catch.Exception(() =>
fixture.Build<Customer>().With(x => x.Email, new Email("a@b.c")).Create()
);
exception.ShouldContainErrorMessage("The property \"Email\" is read-only.");
};
It AutoFixture_can_create_an_instance_using__Register__and_a_creation_function = () =>
{
var fixture = new Fixture();
fixture.Register(() => new Email("a@b.c"));
var subject = fixture.Create<Customer>();
subject.ShouldNotBeNull();
};
}
}
using System;
using AutoMapper;
using ConductOfCode.Data;
using ConductOfCode.Domain;
using Machine.Specifications;
namespace ConductOfCode.Specs
{
public class AutoMapperSpecs
{
Establish context = () =>
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<CustomerDto, Customer>().ReverseMap();
cfg.CreateMap<NameDto, Name>().ReverseMap();
cfg.CreateMap<EmailDto, Email>().ReverseMap();
});
_mapper = config.CreateMapper();
};
It AutoMapper_can_map_to_the_entity = () =>
{
var dto = new CustomerDto
{
Id = Guid.NewGuid(),
Name = new NameDto { First = "Sherlock", Last = "Holmes" },
Email = new EmailDto { Value = "sherlock@holmes.co.uk" }
};
var result = _mapper.Map<Customer>(dto);
result.ShouldNotBeNull();
};
It AutoMapper_can_not_map_to_the_entity_if_validation_fails = () =>
{
var dto = new CustomerDto
{
Id = Guid.NewGuid(),
Name = new NameDto { First = "Sherlock", Last = "Holmes" },
Email = new EmailDto { Value = "invalid" }
};
var exception = Catch.Exception(() =>
_mapper.Map<Customer>(dto)
);
exception.ShouldContainErrorMessage("Email is invalid");
};
It AutoMapper_can_map_from_the_entity = () =>
{
var entity = new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), "sherlock@holmes.co.uk");
var result = _mapper.Map<CustomerDto>(entity);
result.ShouldNotBeNull();
};
static IMapper _mapper;
}
}
[DebuggerDisplay("{Email}")]
public class Customer
{
public Guid Id { get; }
public Name Name { get; }
public Email Email { get; }
public Customer(Guid id, Name name, Email email)
{
if (id == Guid.Empty) throw new Exception("Id is invalid");
if (name == null) throw new Exception("Name is required");
if (email == null) throw new Exception("Email is required");
Id = id;
Name = name;
Email = email;
}
}
using System;
using ConductOfCode.Domain;
using Machine.Specifications;
namespace ConductOfCode.Specs
{
[Subject(typeof(Customer))]
public class CustomerSpecs
{
public class Ctor
{
It should_create_an_instance_with_valid_state_via_ctor_parameters = () =>
{
var subject = new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), new Email("sherlock@holmes.co.uk"));
subject.ShouldNotBeNull();
};
It should_be_able_to_use_conversion_operators = () =>
{
var subject = new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), "sherlock@holmes.co.uk");
subject.ShouldNotBeNull();
};
It should_validate__Id__ = () => Catch.Exception(() => new Customer(Guid.Empty, new Name("Sherlock", "Holmes"), "sherlock@holmes.co.uk")).ShouldContainErrorMessage("Id is invalid");
It should_validate__Name__ = () => Catch.Exception(() => new Customer(Guid.NewGuid(), null, "sherlock@holmes.co.uk")).ShouldContainErrorMessage("Name is required");
It should_validate__Email__ = () => Catch.Exception(() => new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), null)).ShouldContainErrorMessage("Email is required");
}
}
}
[DebuggerDisplay("{Value}")]
public class Email : IEquatable<Email>, IEquatable<string>
{
public string Value { get; }
public Email(string value)
{
if (!value.Contains("@")) throw new Exception("Email is invalid");
Value = value;
}
#region Conversion
public static implicit operator string(Email value)
{
return value.Value;
}
public static implicit operator Email(string value)
{
return new Email(value);
}
#endregion
#region Equality
public override bool Equals(object obj)
{
var other = obj as Email;
return other != null ? Equals(other) : Equals(obj as string);
}
public bool Equals(Email other) => other != null && Value == other.Value;
public bool Equals(string other) => Value == other;
public static bool operator ==(Email a, Email b)
{
if (ReferenceEquals(a, b)) return true;
if (((object)a == null) || ((object)b == null)) return false;
return a.Value == b.Value;
}
public static bool operator !=(Email a, Email b) => !(a == b);
#endregion
public override int GetHashCode() => Value.GetHashCode();
public override string ToString() => Value;
}
using ConductOfCode.Domain;
using Machine.Specifications;
namespace ConductOfCode.Specs
{
[Subject(typeof(Email))]
public class EmailSpecs
{
public class Ctor
{
It should_create_an_instance_with_valid_state_via_ctor_parameter = () => new Email("a@b.c").ShouldNotBeNull();
It should_validate_the_email = () => Catch.Exception(() => new Email("invalid")).ShouldContainErrorMessage("Email is invalid");
}
public class Conversion
{
It should_convert_Email_to_string = () =>
{
string result = new Email("a@b.c");
result.ShouldEqual("a@b.c");
};
It should_convert_string_to_Email = () =>
{
Email result = "a@b.c";
result.Value.ShouldEqual("a@b.c");
};
}
public class Equality
{
It should_be_equatable_with_another_Email = () =>
{
new Email("a@b.c").Equals(new Email("a@b.c")).ShouldBeTrue();
new Email("a@b.c").Equals((object)new Email("a@b.c")).ShouldBeTrue();
new Email("a@b.c").Equals(new Email("c@b.a")).ShouldBeFalse();
new Email("a@b.c").Equals((Email)null).ShouldBeFalse();
};
It should_be_equatable_with_string = () =>
{
new Email("a@b.c").Equals("a@b.c").ShouldBeTrue();
new Email("a@b.c").Equals((object)"a@b.c").ShouldBeTrue();
new Email("a@b.c").Equals("c@b.a").ShouldBeFalse();
new Email("a@b.c").Equals((string)null).ShouldBeFalse();
};
It should_support_equality_operator = () =>
{
(new Email("a@b.c") == new Email("a@b.c")).ShouldBeTrue();
(new Email("a@b.c") == "a@b.c").ShouldBeTrue();
(new Email("a@b.c") == new Email("c@b.a")).ShouldBeFalse();
(new Email("a@b.c") == "c@b.a").ShouldBeFalse();
};
It should_support_inequality_operator = () =>
{
(new Email("a@b.c") != new Email("a@b.c")).ShouldBeFalse();
(new Email("a@b.c") != "a@b.c").ShouldBeFalse();
(new Email("a@b.c") != new Email("c@b.a")).ShouldBeTrue();
(new Email("a@b.c") != "c@b.a").ShouldBeTrue();
};
}
}
}
using System;
using ConductOfCode.Domain;
using Machine.Specifications;
using Newtonsoft.Json;
namespace ConductOfCode.Specs
{
public class JsonSpecs
{
It JsonConvert_can_serialize_the_entity = () =>
{
var entity = new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), "sherlock@holmes.co.uk");
var result = JsonConvert.SerializeObject(entity);
result.ShouldNotBeEmpty();
};
It JsonConvert_can_deserialize_the_entity = () =>
{
var json = JsonConvert.SerializeObject(new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), "sherlock@holmes.co.uk"));
var result = JsonConvert.DeserializeObject<Customer>(json);
result.ShouldNotBeNull();
};
It JsonConvert_can_not_deserialize_the_entity_if_validation_fails = () =>
{
var json = JsonConvert.SerializeObject(new Customer(Guid.NewGuid(), new Name("Sherlock", "Holmes"), "sherlock@holmes.co.uk"));
json = json.Replace("sherlock@holmes.co.uk", "invalid");
var exception = Catch.Exception(() =>
JsonConvert.DeserializeObject<Customer>(json)
);
exception.ShouldContainErrorMessage("Email is invalid");
};
}
}
[DebuggerDisplay("{First,nq} {Last,nq}")]
public class Name
{
public string First { get; }
public string Last { get; }
public Name(string first, string last)
{
if (string.IsNullOrWhiteSpace(first)) throw new Exception("First name is invalid");
if (string.IsNullOrWhiteSpace(last)) throw new Exception("Last name is invalid");
First = first;
Last = last;
}
}
using ConductOfCode.Domain;
using Machine.Specifications;
namespace ConductOfCode.Specs
{
[Subject(typeof(Name))]
public class NameSpecs
{
public class Ctor
{
It should_create_an_instance_with_valid_state_via_ctor_parameters = () => new Name("Sherlock", "Holmes").ShouldNotBeNull();
It should_validate_the_first_name = () => Catch.Exception(() => new Name("", "Holmes")).ShouldContainErrorMessage("First name is invalid");
It should_validate_the_last_name = () => Catch.Exception(() => new Name("Sherlock", "")).ShouldContainErrorMessage("Last name is invalid");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment