0

Currently, I've written a repository in which entities are being stored. That entity is an object and is inheriting from the base class "Entity".

public class Entity
{
    #region Constructors

    public Entity() : this(DateTime.Now, DateTime.Now) { }

    public Entity(DateTime creationDate, DateTime updateDate)
    {
        DateCreated = creationDate;
        DateUpdated = updateDate;
    }

    #endregion

    #region Properties

    [Key]
    [Required]
    [Column(Order= 0)]
    public int Id { get; private set; }

    [Required]
    [Column(Order = 998)]
    public DateTime DateCreated { get; private set; }

    [Required]
    [Column(Order = 999)]
    public DateTime DateUpdated { get; internal set; }

    #endregion
}

Now I want to unit test (not integration test) the repository holding an object that implements an Entity.

I've created a fake repository that uses a HashSet to store objects in memory. Now, the problem is that, when I add entities to my repository, all the Id values are assigned '0' which is normal.

On Entity Framework, they are assigned a unique int value (starting from 0), which is also normal due to the data annotations.

Now I would like to know how I can mimic the same behaviour for the Data Annotations when running unit tests, because right now, I can have multiple records in my repository which the same 'Key' value, which should off course not be possible under any circumstances.

Thanks.

Robert Levy
  • 28,747
  • 6
  • 62
  • 94
Complexity
  • 5,682
  • 6
  • 41
  • 84

2 Answers2

0

You're already using constructor chaining to pass in default DateTime values for creationDate and updateDate -- why not add one more?

public class Entity
{
    #region Constructors

    public Entity() : this(DateTime.Now, DateTime.Now, default(int)) { }

    public Entity(DateTime creationDate, DateTime updateDate, int id)
    {
        DateCreated = creationDate;
        DateUpdated = updateDate;
        Id = id
    }

    #endregion

    #region Properties

    [Key]
    [Required]
    [Column(Order= 0)]
    public int Id { get; private set; }

    [Required]
    [Column(Order = 998)]
    public DateTime DateCreated { get; private set; }

    [Required]
    [Column(Order = 999)]
    public DateTime DateUpdated { get; internal set; }

    #endregion
}

Then you can generate a unique ID for each entity as necessary.

Daniel Mann
  • 57,011
  • 13
  • 100
  • 120
  • I've thought about that to be honest. But I just wanted to check if there were other ways to test it. Maybe those attributes have some hidden magic power in them... – Complexity Jun 19 '14 at 18:32
  • That's one of the reasons I don't like EF, TBQH. It encourages you to dirty your beautiful, pristine POCOs with a bunch of EF attributes. – Daniel Mann Jun 19 '14 at 18:34
  • 1
    @DanielMann One does not have to since there is the fluent api: http://msdn.microsoft.com/en-us/data/jj591617.aspx – paulroho Jun 19 '14 at 20:53
0

Sounds like you are trying to test the wrong thing. What exactly are you trying to accomplish? KeyAttribute is an implementation detail of your storage layer, and you don't want to test that -- it's the vendor's job to ensure it works.

A fake repository is a means of testing something else that depends on the repository interface in isolation. Thus, what you probably should be doing is creating a test case wherein your repository is set up to throw an exception appropriate for a duplicate key scenario, then verifying that your system under test does the right thing when it encounters that exception.

However, you may wish to ensure that all your entities have KeyAttribute on their Id property. You can do that as a single unit test that enumerates your entity types and uses reflection to verify the existence of the attribute.

Rytmis
  • 31,467
  • 8
  • 60
  • 69
  • My repository holds a collection of objects and has a method GetById which I would like to test. Therfore all entities should have different id's. – Complexity Jun 19 '14 at 18:39
  • Help me understand: under which circumstances does it make sense for you to be testing an implementation of a method of a *fake* class? – Rytmis Jun 19 '14 at 18:41
  • There you have a point. But I'd like to achieve almost 100% code coverage so therefore it's needed to test. It might not be needed but I like those numbers. And I might have a class that takes an implementation of a repository. So to unit test it I need to pass an implementation of it which is a fake. Makes that more sense? – Complexity Jun 19 '14 at 18:46
  • 2
    Code coverage is a meaningless metric. It doesn't prove anything about the quality of your code or the number of bugs present. It's more important to test the right things. – Daniel Mann Jun 19 '14 at 18:48
  • I know, but I think I'm just fanisacted by those numbers. – Complexity Jun 19 '14 at 19:04
  • For the cases where you have a SUT that depends on the repository interface, pass in either a mock or a fake that is tailored for that specific test case. – Rytmis Jun 19 '14 at 19:05
  • If you do that, you don't have to test the test-fake, because it will be as dumb as it gets -- yet you can still get 100% coverage. – Rytmis Jun 19 '14 at 19:05
  • And what exactely is the difference between a mock and a fake from a technical point of view? – Complexity Jun 19 '14 at 19:08
  • Mocks tend to be dynamically generated code where you can declaratively specify behavior on a case-by-case basis to the tune of "when the repository method Add() is called with any object, throw a duplicate key exception". I tend to prefer mocks, and when I don't use mocks, I write stubs. Check out http://stackoverflow.com/questions/6808070/testing-software-fake-vs-stub for a discussion about the different terms. :) – Rytmis Jun 19 '14 at 19:14
  • I just want to second what @Rytmis is saying. Seems pointless to test a fake repository. Makes sense to test a service/controller/something using a fake repository. If you wanted to test your repository, you would want to mock your data context. I usually wouldn't go that far. Roy Osherove's The Art of Unit Testing is a good read also. – Neil Smith Jun 19 '14 at 20:16