Object forgery

In the previous article, we've looked at options of providing of the testing code with data. Herein, let's look at the solution of this problem in terms of automatization with the AutoFixture library.

Without going into details of how the library itself works, let's confine ourselves to the fact that reflection is used to create fake instances. Obviously, this approach is much slower in practice than defining of values at compile time; but in most cases it saves so much time while writing tests, that slowing down of execution becomes totally insignificant. Moreover, we don't talk about any catastrophic drop in performance - you will see no changes while execution of a few hundreds of tests.

Circular dependencies

Where to start from? Foremost, I want you to take into account the ability of existing of circular dependencies inside your models. Even a trivial one-to-many relationship implemented in models contains a limitless loop already, if we approach it from the perspective of creation of an object through reflection. For example, we can write a field Role inside a class User, and a collection of Users inside the Role next. In such a situation, the creation of Role would lead us to the necessity of creation of a collection of users, where everyone should have an own role, which is also has to be created...

And even if such dependencies do not exist in the project yet, they may appear tomorrow. In addition, the thorough exploring of all possible relationships between classes... doesn't look like something you would be happy to spent time to; especially, at the moment when we need only to implement a testing project.

Therefore, I usually implement the recursion prohibition setting immediately, without diving into any detail, concentrating, at the same time, of all the faking mechanisms into one static class.

public static class FakeFactory
 {
    public static readonly Fixture Fixture = new Fixture();

    static FakeFactory()
    {
       Fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
          .ForEach(b => Fixture.Behaviors.Remove(b));
       Fixture.Behaviors.Add(new OmitOnRecursionBehavior());
    }
 }

If your subtle mental organization suffers from static objects with public fields, you are able to organize something more elegant...

The first fiddle is played by the instance of the Fixture class, which does all the forgery work for us.

What happens inside the static constructor is obvious from the names of the methods, but... Firstly, we get rid of throwing of exceptions in case of facing circular dependencies while building objects; and secondly, we prohibit going into circular dependencies. The last option can be configured, setting the required level of dependencies that might be allowed.

Here and below (in almost all the examples on the site, probably) we are going to use this particular class for creation of data stubs.

Forgery in practice

Let's use the Article class, as an example:

public class ArticleModel
 {
    public int Id { get; set; }
    public string Text { get; set; }
    public string Preview { get; set; }
    public string Title { get; set; }
    public DateTime Created { get; set; }
    public bool Published { get; set; }
 }

and one method in the ArticleRepository:

public class ArticleRepository : IArticleRepository
 {
    public async Task<bool> Delete(int id)
    {
       var entity = await _context.Articles.FirstOrDefaultAsync(e => e.Id == id);
       if (entity == null) return false;
       _context.Articles.Remove(entity);
       await _context.SaveChangesAsync();
       return true;
    }
 }

Our task is to test the removal of an object from the database. But, paraphrasing one famous cat, in order to remove something unnecessary, you must have something unnecessary. That is why we should create a new entity in a virtual database, previously. Let's consider an example of a test.

 public class ArticleRepositoryTests
 {
    private readonly DataContext _context;
    private readonly ArticleRepository _repository;

    public ArticleRepositoryTests()
    {
       _context = ContextFactory.GetContext();
       _repository = new ArticleRepository(_context);
    }

    [Fact]
    public async Task Delete()
    {
       var article = FakeFactory.Fixture.Create<Articlе>();
       _context.Articles.Add(article);
       _context.SaveChanges();
       //
       var result = await _repository.Delete(article.Id);
       //
       var expectedNull = _context.Articles.FirstOrDefault(e => e.Id == article.Id);
       Assert.Null(expectedNull);
       Assert.True(result);
    }
 }

If we take a look at the Article object's values, we would see something like this.

Id = 78
Title = "Titleaa72344b-e0c2-4c9f-89c4-e05937f6ad72"
Text = "Text79ec47ea-83e3-4bfc-8902-91552d4eb931"
Created = 04.07.2020 15:25:52
Published = true
Preview = "Preview3578cc17-96c6-4b2a-ad91-e7a5c2eb228e"

All the values are defined, there are no empty/default or null values anywhere. If a concrete test needs the Created field to have a specific value, we would redefine it inside the test.

article.Created = DateTime.Now.AddDays(-1);

Thus, only data, that is really important for a test, is determined inside it; everything else is filled in by itself, 'behind the scenes' and doesn't interfere with either writing of the test or reading it. In cases where our models have a few levels of nesting, this approach saves us from tons of meaningless code!