Test Driven AutoMapper with .NET Core
Time to read: 3 minutes
If you use data in a project then you have most likely had to map one model to another. Whether you've done that in the constructor, a dedicated method, or used a mapper of some sort, it can be repetitive and tedious.
AutoMapper, a library for .NET written by Jimmy Bogard, has been around for a while. It had a revamp to work with .NET Core and Dependency Injection but can still feel a little bit like magic.
I use AutoMapper for an internal Twilio tool. I struggled to create valid maps that work without requiring me to map everything manually.
I found the best way to configure AutoMapper is by writing tests and lettingAutoMapper tell me exactly what I need to configure. Let me show you how that works.
I have created a solution in this repository that has the AutoMapper NuGet package already installed and all the basic plumbing included using Dependency Injection. You can follow along with the code in the complete
branch or actively code along using the incomplete code in the master
branch.
If you do wish to code along, the project is using .NET Core SDK version 2.2.104. But anything above version 2.2 should work, even .NET Core 3 previews. You can change the version of .NET used by updating the global.json
file in the route of the solution.
You can check which versions of the .NET Core SDK you have installed using the following command in the console:
If you would like to see a full integration of Twilio APIs in a .NET Core application then checkout this free 5-part video series I created. It's separate from this blog post tutorial but will give you a full run down of many APIs at once.
Start with the tests
The solution has a test project. In the file named TestDrivenAutoMapping.Tests/MappingsTests.cs
you should see the following code:
I use NUnit as my testing framework, but you can use one of your choosing. The key line in the above code is the Mapper.AssertConfigurationIsValid();
method call. This is what will help us to create the correct mappings.
The file we will complete our mappings in is the TestDrivenAutoMapping.Common/Mappings/Mappings.cs
file and should have the following content in the default constructor of the Mappings
class:
AutoMapper follows conventions and therefore, this class must inherit from Profile
. The two private methods found in the same class are custom Type Convertors that convert a string
to DateTime
and vice versa. We won't need to worry about these as they are just another type of mapping.
Our main goal is to map Person
to PersonViewModel
.
If the two models contained the same fields and properties, the code in Mapping.cs
would be sufficient and our Mapper.AssertConfigurationIsValid();
test method call in would pass.
We could simplify it even further by consolidating the two mappings into one line of code: CreateMap<PersonViewModel, Person>().ReverseMap();
However, our models are more complicated. We have some nested models which make it slightly more interesting, and also have some view specific content, public IList<string> Jobs { get; set; }
, in the ViewModel that is of no relevance to the domain model.
If we run the tests now, either from within your IDE or in the console using dotnet test`, we will receive a load of errors. However, it's these errors that will assist us with our correct mappings.
The first two errors we receive are:
Mapping from the errors
The errors tell us we have four unmapped properties when mapping from PersonViewModel to Person and vice versa.
We can add these mappings directly to the TestDrivenAutoMapping.Common/Mappings/Mappings.cs
file as shown below:
We have either mapped or chosen to ignore the properties that were mentioned in the test error. You may notice that, for some of the properties, such as .ForMember(e => e.Job, opts => opts.MapFrom(src => src.JobViewModel))
, we have specified that this is mapping between two models. Therefore we will need to declare this mapping like so:
Now, when we run the tests again, we have the following error:
As JobViewModel
doesn't have a property called Id
we will want to just ignore this. Update the code like so:
This time it's a fairly simple mapping and we can use the ReverseMap()
feature.
We also have the following mapping: .ForMember(e => e.Address, opts => opts.MapFrom(model => model))
on the CreateMap<PersonViewModel, Person>()
map. This requires us to define a mapping between Person
and Address
. We also need to define a mapping between the PersonViewModel
and Address
to complete the reverse mapping. Add the following mapping to the Mappings
class:
If we run the tests now, all should pass and we have only the custom mappings that we required in our Mappings
class.
AutoMapper is a fantastic tool for speeding up development and reducing the amount of boilerplate code you need to write. I hope that approaching it in this manner helps speed up your development even more.
If you have any questions or further ideas on this approach please reach out to me on one of the following channels:
- Email: lporter@twilio.com
- Twitter: @LaylaCodesIt
- GitHub: layla-p
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.