2

Question

I'm working on a C#/.NET Core project, and I'm looking for guidance on organizing interfaces, especially when it comes to using NSubstitute, Moq or other libraries for mocking, because there I need an interface. I've explored different approaches and would like to gather insights from the community.

  1. Inside Definitions namespace at the global level, i.e. a separate directory:

    • Example: The file
      Something/Services/BlubServices/BlaService.cs
      would e.g. have an interface living in
      Definitions/Something/Services/BlubServices/IBlaService.cs.

    • Advantage: Interfaces are neatly segregated, preventing clutter next to class files for those who don't require them.

    • Disadvantage: Accessing the interface from the class may require tools like ReSharper, which might be confusing without them.

  2. In separate IInterface.cs files:

    • Advantage: Interfaces are easily discoverable when implementing a class in the IDE.
    • Disadvantage: With many classes, managing numerous interface files can become cumbersome. There's also the risk of accidentally selecting the interface instead of the class.
  3. Interfaces in the same file as the original class:

    • Idea: Placing interfaces in the same file as their corresponding class.
    • Advantage: Centralized and clutter-free project structure.
    • Disadvantage: This approach might deviate from conventional C# coding styles.

I'm particularly interested in feedback related to using NSubstitute for mocking. Does the placement of interfaces affect your NSubstitute mocking strategy? Are there any specific pros or cons to consider in this context?

Reference: For more insights and historical discussions on interface organization in C#/.NET projects, you can refer to this related question on Software Engineering Stack Exchange. That one is more generic however, and this one is based on an a structure where you group by features, which I don't have in my project, it is more of a layered architecture, where one web request is processed and passed to the next layer to handle it etc.

Your insights and recommendations, especially in the context of NSubstitute, would be highly valuable. Thank you!

Alternative: You can actually use ForPartsOf<> to also mock classes instead of interfaces, so I just don't need them. However, this has several disadvantages too IMHO:

  1. It needs virtual properties/methods and the classes must not be sealed.
  2. When you don't substitute/"configure" the methods explicitly, they run the real implementation, which is dangerous if you want to mock your dependencies in unit tests and accidentally execute totally different ones. This can lead to implementation problems.

Disclaimer/Full disclosure: This question has been improved by ChatGPT based on my own notes, but has been fully reviewed and improved by me. That was just a good writing help.

5
  • 1
    The disadvantages you list are IMO either not really that important (like deviation from "conventional" coding style), or not really a concern (e.g. I don't see why an IDE wouldn't be able to "access the interface from the class"). The disadvantage that's more important is that all of these options will likely result in coupling, or at the very least hinder your ability to layer and componentize, because they destroy cohesion. You should organize them by their design purpose, depending on which class/component "owns" the interface (and this doesn't have to be the implementing class). Commented Sep 1, 2023 at 21:32
  • (The above is irrespective of NSubstitute.) Commented Sep 1, 2023 at 21:32
  • I don't see any reason why you want answers to be "related to using NSubstitute for mocking" - answers should be the same for using any other interface-based mocking library for .Net / C#. I would actually recommend to rewrite the question somwhat more tool-agnostic, along the lines of "Where to put interface files when using a mocking library for tests?". You can still mention NSubstitute in the question as one example among others (see links to alternatives on the NSubstiitute Github page,).
    – Doc Brown
    Commented Sep 2, 2023 at 6:15
  • @DocBrown You're right, I have adjusted it.
    – rklec
    Commented Sep 4, 2023 at 7:41
  • 1
    None of the above - put them in a completely separate library project that's referenced by the concrete classes. Then the mocking framework only needs to reference the libraries for the interfaces to create the mocks rather than the concrete code implementation libraries. Commented Sep 7, 2023 at 23:19

2 Answers 2

4

Decisions like this are usually best guided by the Principle of Least Astonishment: What pattern will other developers recognize and understand quickly?

First, C# projects have a very common convention of using one type per file, so follow that pattern. Don’t put multiple types in one file. Follow the conventions of the language you’re using (Example: I don’t put multiple types per file when writing C#, but I do when writing JavaScript).

Next, namespaces and assemblies. Other .NET developers are likely to recognize the patterns they already use, so look to the framework libraries for guidance. Using logging as an example:

  • ILogger
    • Assembly: Microsoft.Extensions.Logging.Abstractions
    • Namespace: Microsoft.Extensions.Logging
  • Logger (the implementation, not used by application code)
    • Assembly: Microsoft.Extensions.Logging
    • Namespace: Microsoft.Extensions.Logging

The namespace is for business domain categorization. Both ILogger and Logger are in the same business domain, so they share a namespace.

The project/assembly is a mechanism for code sharing and distribution. It is often desirable to reference abstractions without creating a project reference to specific implementations, so the interfaces go in an “Abstractions” assembly that the main assembly references.

However, if you don’t have a need to distribute these types separately, go ahead and keep interfaces and implementations in the same project (in the same directory, since they share a namespace), because it is easier to manage fewer projects.

1

Your first two disadvantages are odd and suggest to me that your whole approach to coding might be different from 'normal'

Disadvantage: Accessing the interface from the class may require tools like ReSharper, which might be confusing without them

No, just use a using this is normal.

Disadvantage: With many classes, managing numerous interface files can become cumbersome. There's also the risk of accidentally selecting the interface instead of the class.

How are you accidentally selecting the class? something is wrong here.

Finally, or perhaps firstly, where you put your interface has no impact on using it for mocking.

Here's how i organise things

  1. Make lots of projects, three for each component in your solution.
  • MyCompany.Component
  • MyCompany.Component.Models
  • MyCompany.Component.Tests
  1. Write your concrete service in MyCompany.Component

  2. Put any exposed Models in:

    MyCompany.Component.Models

    Put the interface in

    MyCompany.Component.Models/Interfaces

Now other components that depend on your component reference MyCompany.Component.Models that means they can use the interface + and models they need to pass to or get returned back from it. Because they don't reference the project with the concrete class, there is no way for them to directly reference it or any internal classes it uses.

Your top level application will reference both MyCompany.Component.Models and MyCompany.Component as it will need to instantiate a new MyCompany.Component.MyComponent

  1. Alternate concrete implementations go in a new project, which references the models project

    MyCompany.Component_AlternateTech

  2. Tests go in the test project and reference models and component. You can never accidentally use MyCompany.OtherComponentImNotTesting.Interfaces.IOtherComponent or any of OtherComponents models

  3. Use solution folders to organise the solution and avoid having a huge list of projects.

This approach neatly subdivides your code up and enforces referencing rules. You never get MyCompany.Component1.HelperModel referencing MyCompany.Component2.SomethingManager. These classes in in projects that are not referenced, plus they are marked internal

As the components mature you can move them off to separate source code repos that never change and just consume the dlls.

Your top level application project is super clean and only has application code in it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.