locked
Structuring a project with Unit Tests RRS feed

  • Question

  • User-1526035670 posted

    Hi all

    Im using EF6 and Repository Pattern. I would like to involve Unit Testing (NUnit) so have read and gone through some tutorials.

    My aim to to have separate projects but not sure at which stage i should add separation into different classes.

    1.

    I add an .EDMX file, add connection to my database. Add 2 tables (Customer and Orders)

    Add a folder for partial classes (overriding Customers and Orders and adding additional methods)

    2.

    IRepository

    public interface IRepository<TEntity> where TEntity : class

    This is used to create other repositories e.g.

    ICustomer (public interface ICustomer : IRepository<Customer>

    3.

    I create a UoW Interface (inherits IDisposable) which has code to the Repositories (ICustomer and IOrders) and then add a class to inherit from it.

    4.

    Finally a web application (webforms).

    Could someone explain how i could break these steps into separate classes so when i implement NUnit i have clean separate layers (I would have a separate project for Unit Testing)? I know step 4 can be any application but would love an example of where i would put those interfaces and classes to separate my solution.

    Thanks

    Saturday, July 4, 2020 11:10 AM

Answers

  • User475983607 posted

    The problem you are trying to solve is not clear.  Unit tests are for exercising your business logic but there is no business logic in your original post.  

    I think it is important to mention that wrapping Entity Framework, which is a unit of work pattern, in another unit of work pattern is a really really bad idea.  In order to pull this off you need to be an advanced programmer otherwise you'll create dependencies on EF and more queries than expected to populate the Entities.  I'm pretty sure you'll figure this out when you start building and unit testing business logic.  the first thing you'll find is the IRepository is limiting.  You'll start to question where to put methods that filter by fields other than an Id or require multiple entities.

    The concept is pretty simple though.  Create a new implementation where you control the result sets.   Keep in mind, your sample code is incomplete and has syntax errors.  I tried to use your code to create a basic pattern.  

    public interface IRepository<TEntity> where TEntity : class
    {
        public List<TEntity> Get(TEntity entity);
    }
    public class MockRepository : IRepository<MockCustomer>
    {
        public List<MockCustomer> Get(MockCustomer entity)
        {
            return new List<MockCustomer>
            {
                new MockCustomer()
                {
                    Id = 1,
                    Name = "Name 1"
                },
                new MockCustomer()
                {
                    Id = 2,
                    Name = "Name 2"
                }
            };
        }
    }
    public class MockCustomer
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    Basically, you'll create a mock repository implementation for each type.  That allows you to return any result set you like so you can test how your business logic reacts to the data.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Saturday, July 4, 2020 12:41 PM
  • User475983607 posted

    I think i mentioned what im after above a couple of times. I know how to create a Repository but my question is how to separate this structure into separate projects. 

    Sorry, it's confusing that you created a repository pattern but at the same time it appears like you do not understand Interfaces.  The interface is the separation between the data access layer (EF or ADO) and the UI or business layer.  It is like a switch where you get to point your code to a different data source.   I assumed since you know how to design an generic repository which is a advanced topic that you understand the interface is the hard separation.  

    Create a project for Entity Framework which contains the EDMX.  Create another project for the Unit of Work and repositories,  Create a project for the Web Forms application.  Create a project for the unit tests.  You might create a separate business layer or your business layer might end up in the repo.  Depends on your skill level.  Generally wrapping Entity Framework, which is already a Unit-of-Work, with another Unit-of-Work causes problems when you get to the real work of building the application.  That work is designing and writing code that meets business requirements.  In other words, the UoW is an abstraction that you build before solving the actual business problem with code.

    The references look like the following.

    Web App -> UoW(Repos) -> EF

    If you create a business layer

    Web App -> Business -> UoW(Repos) -> EF

    If you create a service layer on top of the business layer

    Web App -> Service -> Business -> UoW(Repos) -> EF

    Unit test

    Unit test -> business -> UoW(Mock Repos)  (sample shown above above)

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, July 8, 2020 11:23 AM

All replies

  • User475983607 posted

    The problem you are trying to solve is not clear.  Unit tests are for exercising your business logic but there is no business logic in your original post.  

    I think it is important to mention that wrapping Entity Framework, which is a unit of work pattern, in another unit of work pattern is a really really bad idea.  In order to pull this off you need to be an advanced programmer otherwise you'll create dependencies on EF and more queries than expected to populate the Entities.  I'm pretty sure you'll figure this out when you start building and unit testing business logic.  the first thing you'll find is the IRepository is limiting.  You'll start to question where to put methods that filter by fields other than an Id or require multiple entities.

    The concept is pretty simple though.  Create a new implementation where you control the result sets.   Keep in mind, your sample code is incomplete and has syntax errors.  I tried to use your code to create a basic pattern.  

    public interface IRepository<TEntity> where TEntity : class
    {
        public List<TEntity> Get(TEntity entity);
    }
    public class MockRepository : IRepository<MockCustomer>
    {
        public List<MockCustomer> Get(MockCustomer entity)
        {
            return new List<MockCustomer>
            {
                new MockCustomer()
                {
                    Id = 1,
                    Name = "Name 1"
                },
                new MockCustomer()
                {
                    Id = 2,
                    Name = "Name 2"
                }
            };
        }
    }
    public class MockCustomer
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    Basically, you'll create a mock repository implementation for each type.  That allows you to return any result set you like so you can test how your business logic reacts to the data.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Saturday, July 4, 2020 12:41 PM
  • User-1526035670 posted
    Hi

    The problem I'm attempting to resolve (We can leave out NUnit tests for now as I added that in just in case I had to take a different direction) is to have reusability and separation on my projects.

    So the course I'm going through creates one solution with everything.... He mentions this should be separated into different projects but has recommendation of having the projects as below

    Presentation - all forms/controllers etc so webforms takes place here.
    Core/business logic - IRepository, IUnitOfWork (all Interfaces)
    Data Access- implementation of business logic/core

    So I attempted this with the basic structure provided and came across a few problems. In case you need to know I started to get circular dependencies. I'm not sure if this is due to him using the code first approach (as I'm using DB first approach) or simply me missing a critical step.

    A short version is if I can get the idea of separating these projects hopefully that can get me on track. Hopefully this is more clearer?
    Saturday, July 4, 2020 12:59 PM
  • User-1526035670 posted
    I could if you prefer post the demo project somewhere to fill in any gray areas.
    Saturday, July 4, 2020 1:01 PM
  • User1535942433 posted

    Hi JamieP1,

    Accroding to your description,I'm guessing that you need dependency Injection in ASP.Net Web Forms.

    I suggest you could refer to below article:

    https://translate.google.com/translate?hl=zh-CN&sl=en&tl=zh-CN&u=https%3A%2F%2Fjimcute.wordpress.com%2F2017%2F02%2F28%2Fdependency-injection-in-asp-net-web-forms%2F&anno=2&prev=search

    Best regards,

    Yijing Sun

    Tuesday, July 7, 2020 8:45 AM
  • User-1526035670 posted

    Hi

    I dont think thats what im after. The link refers to DI which im not interested in just at this stage. All i would like is a clean approach on how to separate all my projects so they are not dependent.

    As for DI i would look at that once i have the project structure/layers correct or at least working.

    Many thanks

    Wednesday, July 8, 2020 8:50 AM
  • User475983607 posted

    I showed how to create a mock repository using your code.  Not sure what else we can do got you. 

    Wednesday, July 8, 2020 10:22 AM
  • User-1526035670 posted

    I showed how to create a mock repository using your code.  Not sure what else we can do got you. 

    Hi

    I think i mentioned what im after above a couple of times. I know how to create a Repository but my question is how to separate this structure into separate projects. 

    Let me know if you need further clarification.

    Thanks 

    Wednesday, July 8, 2020 10:37 AM
  • User475983607 posted

    I think i mentioned what im after above a couple of times. I know how to create a Repository but my question is how to separate this structure into separate projects. 

    Sorry, it's confusing that you created a repository pattern but at the same time it appears like you do not understand Interfaces.  The interface is the separation between the data access layer (EF or ADO) and the UI or business layer.  It is like a switch where you get to point your code to a different data source.   I assumed since you know how to design an generic repository which is a advanced topic that you understand the interface is the hard separation.  

    Create a project for Entity Framework which contains the EDMX.  Create another project for the Unit of Work and repositories,  Create a project for the Web Forms application.  Create a project for the unit tests.  You might create a separate business layer or your business layer might end up in the repo.  Depends on your skill level.  Generally wrapping Entity Framework, which is already a Unit-of-Work, with another Unit-of-Work causes problems when you get to the real work of building the application.  That work is designing and writing code that meets business requirements.  In other words, the UoW is an abstraction that you build before solving the actual business problem with code.

    The references look like the following.

    Web App -> UoW(Repos) -> EF

    If you create a business layer

    Web App -> Business -> UoW(Repos) -> EF

    If you create a service layer on top of the business layer

    Web App -> Service -> Business -> UoW(Repos) -> EF

    Unit test

    Unit test -> business -> UoW(Mock Repos)  (sample shown above above)

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, July 8, 2020 11:23 AM
  • User-1526035670 posted

    Hi

    I think you're on the right lines of what im after. Yes so i understand some parts and not most which is where im attempting to fill in the gaps. To ensure i grasp this, i will make a small app to ensure i have the structure correct. Overall trying to keep this separate where possible. Could you advise if the below steps are correct in case i am messing up something, somewhere. I know you have detailed some steps above but im trying to mix my understanding with your wording and ensuring im on the right track

    1. I will create a CL and install EF 6. Will call this EntityFramework (using DB first approach), Next i will add ADO .Net Entity Data Model.
    2. Create another CL and call this Core and add only Interfaces (IRepository etc) but no implementation in this CL
    3. Add another CL which has a reference to Core and implement these classes.

    So far am i on the correct lines before i create this test solution?

    Many thanks

    Wednesday, July 8, 2020 11:58 AM
  • User475983607 posted

    I will create a CL and install EF 6. Will call this EntityFramework (using DB first approach), Next i will add ADO .Net Entity Data Model.

    I think so... All of entity Framework is in a class library, Entities, function imports, etc.

    Create another CL and call this Core and add only Interfaces (IRepository etc) but no implementation in this CL

    You can.  I generally place the implementation close to the, usually the same file, the implementation.  I find it easier to organize the code/files namespaces.  Do whatever makes you happy.

    Wednesday, July 8, 2020 12:20 PM
  • User-1526035670 posted

    You can.  I generally place the implementation close to the, usually the same file, the implementation.  I find it easier to organize the code/files namespaces.  Do whatever makes you happy.

    Hi

    Didnt quite understand your recommendation but for now i have only added the Interfaces (IRepository including IUnitOfWork) to this CL. Can always change this later if i see a problem that i dont currently see.

    So the first problem i come with is once i have the Core CL setup with the default/generic Interface i then decide to add my ICustomer interface.

        public interface ICustomerRepository : IRepository<Customer>
        {
            Customer GetCustomer(int id);
        }

    Immediately i have an error as the Customer Entity does not exist (rightly so). If i add a reference to the EntityFramework CL (as in my first CL that contains the ADO .Net Entity Data Model.) then the error goes away - is this ok? Im asking as i dont know if this should or should not be the case in terms of separation?

    If this is ok i can then proceed to the next step:

    Next is another CL, called DataAccess. This is going to hold an instance of all the Interfaces so will add a ref to the CL containing the interfaces.

    Let me know your thoughts.

    Thanks again

    Wednesday, July 8, 2020 1:59 PM
  • User475983607 posted

    Immediately i have an error as the Customer Entity does not exist (rightly so). If i add a reference to the EntityFramework CL (as in my first CL that contains the ADO .Net Entity Data Model.) then the error goes away - is this ok? Im asking as i dont know if this should or should not be the case in terms of separation?

    This issue has to do with not understanding the generic repository pattern.  You've added a concrete class to the Interface definition.  You can do this but you'll to reference the project that contains the Customer class.

    My best recommendation is you drop the repository and simply use Entity Framework as intended. 

    However, if you must shoot yourself in the foot then the Internet is full of Generic Repository examples. 

    https://www.google.com/search?q=entity+framework+generic+repository&rlz=1C1CHBF_enUS838US838&oq=entity+framework+generic+repository&aqs=chrome..69i57j0l7.5487j0j7&sourceid=chrome&ie=UTF-8

     

    Wednesday, July 8, 2020 2:58 PM
  • User-1526035670 posted

    This issue has to do with not understanding the generic repository pattern.  You've added a concrete class to the Interface definition.  You can do this but you'll to reference the project that contains the Customer class.

    In this case what would be the 'correct' approach (if there is one)? Should it be another layer or is this acceptable in this app? 

    My best recommendation is you drop the repository and simply use Entity Framework as intended. 

    However, if you must shoot yourself in the foot then the Internet is full of Generic Repository examples. 

    https://www.google.com/search?q=entity+framework+generic+repository&rlz=1C1CHBF_enUS838US838&oq=entity+framework+generic+repository&aqs=chrome..69i57j0l7.5487j0j7&sourceid=chrome&ie=UTF-8

    Correct me if im wrong but if using EF, is it tightly coupled in most scenarios? What if i wanted to use the same web app with a mobile app? Is there another pattern i could look into? I will check out those links shortly

    Thanks

    Wednesday, July 8, 2020 3:41 PM
  • User475983607 posted

    JamieP1

    Correct me if im wrong but if using EF, is it tightly coupled in most scenarios? What if i wanted to use the same web app with a mobile app? Is there another pattern i could look into? I will check out those links shortly

    The generic repository pattern does not solve this problem.  Web API does.   

    Secondly, EF is just a framework for generating dynamic SQL.  Rather than building SQL from strings like the good old days, we get to use C# types.  Can you explain "tightly coupled"?  Do you mean tightly coupled to SQL Server?  Or do you mean tightly coupled to a type with a method like GetCustomers() where you want different implementations of GetCustomers()?

    Wednesday, July 8, 2020 4:14 PM
  • User-1526035670 posted

    JamieP1

    Correct me if im wrong but if using EF, is it tightly coupled in most scenarios? What if i wanted to use the same web app with a mobile app? Is there another pattern i could look into? I will check out those links shortly

    The generic repository pattern does not solve this problem.  Web API does.   

    Secondly, EF is just a framework for generating dynamic SQL.  Rather than building SQL from strings like the good old days, we get to use C# types.  Can you explain "tightly coupled"?  Do you mean tightly coupled to SQL Server?  Or do you mean tightly coupled to a type with a method like GetCustomers() where you want different implementations of GetCustomers()?

    Hi

    Web API resolves it in the sense if i wanted to introduce a mobile app?

    As for the last part of my question, I would like my CL's to be used by more projects and not just web. In the past when adding some classes i then realise it was depending on other bits and bobs that i didnt realise at the time. Ideally i would like there to be minimum dependencies where possible.

    Finally if i continue with the current structure i would create a new project for UnitOfWork? I will shortly review the link you posted and look at the first couple of links about the intended use of EF (which i assume is you recommending not to break it into CL's like i am attempting).... Will mark your response as an answer too.

    Thanks

    Thursday, July 9, 2020 12:12 PM
  • User475983607 posted

    JamieP1

    Web API resolves it in the sense if i wanted to introduce a mobile app?

    Yes.  but how does you're mobile application work?  Does it make remote DB calls or is the DB local?

    JamieP1

    As for the last part of my question, I would like my CL's to be used by more projects and not just web. In the past when adding some classes i then realise it was depending on other bits and bobs that i didnt realise at the time. Ideally i would like there to be minimum dependencies where possible.

    I build multi-tier applications where public web applications cannot communicate directly with the database.  Data flows through secured web services.  Behind the services are domain objects (APIs) that handle application features.  For example, registration.  All the registration logic is contained in one logical API.  The service is simply an HTTP interface to the API while an IRegistration abstraction allows modifying the implementation.  This is useful for registration unit tests.

    JamieP1

    Finally if i continue with the current structure i would create a new project for UnitOfWork? I will shortly review the link you posted and look at the first couple of links about the intended use of EF (which i assume is you recommending not to break it into CL's like i am attempting).... Will mark your response as an answer too.

    Yeah, Unit-of-Work should be a separate project as I recommended above.

    IMHO, you are creating a maintenance nightmare which you'll figure out later once you start writing application features to meet your customer's requirements.  

    Thursday, July 9, 2020 12:55 PM
  • User-1526035670 posted

    I build multi-tier applications where public web applications cannot communicate directly with the database.  Data flows through secured web services.  Behind the services are domain objects (APIs) that handle application features.  For example, registration.  All the registration logic is contained in one logical API.  The service is simply an HTTP interface to the API while an IRegistration abstraction allows modifying the implementation.  This is useful for registration unit tests.

    No mobile app yet but i need to keep this in mind so i can learn new concepts where possible as this would be required in a future project.

    Do you have a guide/book/ tutorial i could follow as its seems very close to my target? Ive gone through a few courses on RP so i dont know which section im doing wrong/going in the wrong direction? i know you have mentioned some steps above but are they steps for what im asking which could be wrong or are those steps the recommended approach?

    Many thanks

    Thursday, July 9, 2020 4:11 PM