{"id":2258,"date":"2025-09-18T13:13:52","date_gmt":"2025-09-18T13:13:52","guid":{"rendered":"https:\/\/www.cmarix.com\/qanda\/?p=2258"},"modified":"2026-02-05T11:59:18","modified_gmt":"2026-02-05T11:59:18","slug":"unit-integration-testing-modular-monolith-dotnet","status":"publish","type":"post","link":"https:\/\/www.cmarix.com\/qanda\/unit-integration-testing-modular-monolith-dotnet\/","title":{"rendered":"How to Perform Unit and Integration Testing in Modular Monoliths (.NET)?"},"content":{"rendered":"\n<p>Modular monoliths in .NET let you structure your application as distinct modules like Product, Order, and User without breaking it into separate services. Testing in this setup isn&#8217;t just helpful, it&#8217;s necessary. Unit tests are important to check how each module behaves in isolation, while integration tests are important to see if they work together as intended in real scenarios.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Types of Tests in Modular Monolith<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Test Type<\/strong><\/td><td><strong>What it Tests<\/strong><\/td><td><strong>Scope<\/strong><\/td><\/tr><tr><td>Unit Test<\/td><td>Individual classes\/methods<\/td><td>Internal logic<\/td><\/tr><tr><td>Integration Test<\/td><td>Interaction between modules or with real database<\/td><td>System workflows<\/td><\/tr><tr><td>End-to-End Test (optional)<\/td><td>Full app (UI\/API to DB)<\/td><td>External behavior<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Example App Folder Structure<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>MyApp\/\n\u251c\u2500\u2500 Modules\/\n\u2502   \u251c\u2500\u2500 Product\/\n\u2502   \u2502   \u251c\u2500\u2500 ProductService.cs\n\u2502   \u2502   \u2514\u2500\u2500 ProductRepository.cs\n\u2502   \u251c\u2500\u2500 Order\/\n\u2502   \u2502   \u2514\u2500\u2500 OrderService.cs\n\u251c\u2500\u2500 API\/\n\u2502   \u2514\u2500\u2500 Controllers\/\n\u251c\u2500\u2500 Tests\/\n\u2502   \u251c\u2500\u2500 UnitTests\/\n\u2502   \u2514\u2500\u2500 IntegrationTests\/<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How to Conduct Unit Testing in Modular Monolith .NET (Step-by-Step)<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Add xUnit and Moq to your test project<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>dotnet add package xunit\ndotnet add package Moq<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Create Unit Test for Product Module<\/h3>\n\n\n\n<p><strong>ProductService.cs<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class ProductService\n{\n    private readonly IProductRepository _repo;\n    public ProductService(IProductRepository repo)\n    {\n        _repo = repo;\n    }\n\n    public Product GetById(int id) => _repo.GetById(id);\n}<\/code><\/pre>\n\n\n\n<p><strong>ProductServiceTests.cs<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class ProductServiceTests\n{\n    &#91;Fact]\n    public void GetById_ShouldReturnProduct()\n    {\n        \/\/ Arrange\n        var mockRepo = new Mock&lt;IProductRepository>();\n        mockRepo.Setup(x => x.GetById(1)).Returns(new Product { Id = 1, Name = \"Test\" });\n\n        var service = new ProductService(mockRepo.Object);\n\n        \/\/ Act\n        var result = service.GetById(1);\n\n        \/\/ Assert\n        Assert.NotNull(result);\n        Assert.Equal(\"Test\", result.Name);\n    }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Integration Testing in Modular Monolith (Step-by-Step)<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Add Required NuGet Packages<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>dotnet add package Microsoft.AspNetCore.Mvc.Testing\ndotnet add package Microsoft.EntityFrameworkCore.InMemory\ndotnet add package FluentAssertions<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Create In-Memory Test Server<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">\u00a0ProductIntegrationTests.cs<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>public class ProductIntegrationTests : IClassFixture&lt;WebApplicationFactory&lt;Program>>\n{\n    private readonly HttpClient _client;\n\n    public ProductIntegrationTests(WebApplicationFactory&lt;Program> factory)\n    {\n        _client = factory.CreateClient(); \/\/ In-memory test server\n    }\n\n    &#91;Fact]\n    public async Task GetProductById_ReturnsProduct()\n    {\n        \/\/ Act\n        var response = await _client.GetAsync(\"\/api\/products\/1\");\n        response.EnsureSuccessStatusCode();\n\n        var json = await response.Content.ReadAsStringAsync();\n        var product = JsonConvert.DeserializeObject&lt;Product>(json);\n\n        \/\/ Assert\n        product.Should().NotBeNull();\n        product.Id.Should().Be(1);\n    }\n}<\/code><\/pre>\n\n\n\n<p><strong>Purpose:<\/strong> This tests the real behavior of the app, with routing, controllers, and DB (in-memory).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3: Configure In-Memory DB in Program.cs for testing<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>if (app.Environment.IsEnvironment(\"Testing\"))\n{\n    builder.Services.AddDbContext&lt;AppDbContext>(options =>\n        options.UseInMemoryDatabase(\"TestDb\"));\n}<\/code><\/pre>\n\n\n\n<p>Or override it using WebApplicationFactory&lt;T&gt; in your test setup.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Best Practices for all Testing in Modular Monolith<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Aspect<\/strong><\/td><td><strong>Recommendation<\/strong><\/td><\/tr><tr><td>Unit Tests<\/td><td>Fast, mock dependencies, and cover edge cases<\/td><\/tr><tr><td>Integration Tests<\/td><td>Use TestServer, real or in-memory databases<\/td><\/tr><tr><td>Foldering<\/td><td>Organize tests by module for clarity<\/td><\/tr><tr><td><strong>Test Boundaries<\/strong><\/td><td>Verify communication between modules is correct<\/td><\/tr><tr><td>Isolation<\/td><td>Clean the database before each integration test<\/td><\/tr><tr><td>Performance<\/td><td>Keep integration tests fewer, but focused and meaningful<\/td><\/tr><tr><td>Naming<\/td><td>Follow naming like ProductServiceTests.cs or OrderIntegrationTests.cs<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Final Thought<\/h2>\n\n\n\n<p>Unit tests help confirm that each module works as intended, while integration tests ensure different modules interact properly in real-world scenarios. If you&#8217;re building a modular monolith and want reliable, scalable testing strategies, it\u2019s a smart move to <a href=\"https:\/\/www.cmarix.com\/hire-aspdotnet-developers.html\">hire .NET developers<\/a> who understand how to balance isolation with real-world validation. That\u2019s how you keep your application modular, maintainable, and production-ready.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Modular monoliths in .NET let you structure your application as distinct modules like Product, Order, and User without breaking it into separate services. Testing in this setup isn&#8217;t just helpful, it&#8217;s necessary. Unit tests are important to check how each module behaves in isolation, while integration tests are important to see if they work together [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":2259,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[4,3],"tags":[],"class_list":["post-2258","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dot-net","category-web"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/2258","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/comments?post=2258"}],"version-history":[{"count":2,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/2258\/revisions"}],"predecessor-version":[{"id":2262,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/2258\/revisions\/2262"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/media\/2259"}],"wp:attachment":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/media?parent=2258"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/categories?post=2258"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/tags?post=2258"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}