From d0b68335b8168b58e9409acb2f114685b4c2f9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Lachowski?= Date: Wed, 17 Jun 2026 12:11:40 +0200 Subject: [PATCH] RDBTC-221 Migrate Technical Guides: Writing unit tests with RavenDB .NET Test Driver --- guides/assets/net-test-driver1.webp | Bin 0 -> 19196 bytes ...nit-tests-with-ravendb-net-test-driver.mdx | 320 +++++++++++++++++- 2 files changed, 316 insertions(+), 4 deletions(-) create mode 100644 guides/assets/net-test-driver1.webp diff --git a/guides/assets/net-test-driver1.webp b/guides/assets/net-test-driver1.webp new file mode 100644 index 0000000000000000000000000000000000000000..8c5685ee8a225b516d6982e357e5bc3c8570042b GIT binary patch literal 19196 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 z zUct;cEMjV}D`n;-x)+0&_iQwt<5{?}Z8 zmGCQc%2w0nUajLH-n$s2JpH_A9djle@4^frh;RkiY#i3p%ctd2NaJlHG1PaMEPPHO zFoEFVY!WG}T{KZ|q7yu_=jX$?NxF8({v~oei#@$wEHeU4NMlsKP=d9lRx_-V%0w8&E6*l4U#0 z^R5A$g)^m=*$l#_{!5RGU`ZRxbLji6E*llFiKcmL{^9}Z+R(umyU<>MtVd}vep%Fg z=Z8r*6R<@Wtl6A>`#yk2PVEW>k2HSHJqK0at!3tcFcw5nLnAE^hB$IQH+ljvl27~# z_?K=t$!8Z5+Dmh$4yVU2oGK(Iz!L@576#+~&LlRA0C4vCqf)#+6B{z{6z2s?;;#O{ zRIciu@ZP;?nKt|8kLQ`KNV7&S?>d6d=iSg=Q-8`<~`A%q}chPYzADtB4b1 z5ni7gGM_Nv$;ge+7bXeO)8bb7tO5B6g4c3Oe(1#nVe`h)`tBKFie_oZ!${&Pn{og# z;99O{H0s{u9!i(y#Y)@~9Bq4FkY^BtDxV(=qm#)s7iauGsAi zio($Aj(Z?KZt=ae1C~?noJlNnm-Lgvut+X?Z5*tS0(5%O+zRji7M{)M^~WxH82>Kx z`2NZ$iEW-zjBK&9#eqr`98^PBr7#1{`HS~2@o#&Ro(90fJ#x$i7yOR@_RXNRetNj3 z$DB0AFtx^a^Lu>)S9j36L%JRen-Hzo=)@QNp<8uJ)e@Nh;it|M{l4hwVg&EdV3q+l zU!EM(tg?w9?aVDuVq&zMY}e4c;}_l~iwz>&m1@sKaYo|o&ZEQ19%r_#DpsA077{#u z=tHtk42zGIm**WvVB(_neMxYO6;4x&OzuO~%5agn^lF1XAwT}xITujT$EvHYF9o`S z{$CUIl%E4kBW-2HVpuJZk*&tHJFG4V^p6oR3ufS_VY6k#_#FcvEq=6yftR^hXmkhs z4OrbmV8ExfN%4mu;yxo6nq?v9vDr$T87$r-Yb?jAO^?WI{?c;|!~8&b@jP__7^4~B z$)+7{*Z>_}U9F~99Bl#M0{jj?W%H4~UGsE8ORK;RRanet9oBM)8ptF;d=nGiXYS&1 z8xQ;DH7O!aLt+1X#-$`l$ZS9FnAD_+ISq&X^BRvo?nw&HhXA0caD5C8xGw5P&| literal 0 HcmV?d00001 diff --git a/guides/writing-unit-tests-with-ravendb-net-test-driver.mdx b/guides/writing-unit-tests-with-ravendb-net-test-driver.mdx index 8b72c4704e..f4c594de3c 100644 --- a/guides/writing-unit-tests-with-ravendb-net-test-driver.mdx +++ b/guides/writing-unit-tests-with-ravendb-net-test-driver.mdx @@ -1,9 +1,321 @@ --- title: "Writing unit tests with RavenDB .NET Test Driver" -tags: [csharp, testing] -description: "Read about Writing unit tests with RavenDB .NET Test Driver on the RavenDB.net news section" -external_url: "https://ravendb.net/articles/writing-unit-tests-with-ravendb-net-test-driver" -published_at: 2024-08-28 +tags: [csharp, testing, getting-started] image: "https://ravendb.net/wp-content/uploads/2024/08/csharp-in-mem.png" +description: "Learn how to use RavenDB.TestDriver to run integration tests against a real in-memory RavenDB instance instead of mocks, with full support for indexing, Studio debugging, and CI/CD pipelines." +published_at: 2024-08-28 +see_also: + - title: "Test Driver" + link: "start/test-driver" + source: "docs" + path: "Start" + - title: "Creating a Document Store" + link: "client-api/creating-document-store" + source: "docs" + path: "Client API" + - title: "Stale Indexes" + link: "indexes/stale-indexes" + source: "docs" + path: "Indexes" +author: "Gracjan Sadowicz" proficiency_level: "Beginner" --- + +import Admonition from '@theme/Admonition'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; +import LanguageSwitcher from "@site/src/components/LanguageSwitcher"; +import LanguageContent from "@site/src/components/LanguageContent"; +import Image from "@theme/IdealImage"; + +There are many advantages of testing with real databases over mocks. You can test real query flow, ensure that your results match what the database engine will do and avoid complicated and often fragile setup. The major disadvantage for wanting to avoid using a real database in tests is speed and complexity. Databases are inherently stateful, while you want to start each test as a new slate. + + + We assume that you are familiar with both RavenDB and unit testing in .NET in this article. We’ll cover how you can use RavenDB in your tests with ease, without giving up the rapid feedback that fast tests give you. + + +## What you'll learn + +- How to avoid shortfalls of mocking the database +- How to integrate *RavenDB.TestDriver* into your .NET project with no effort +- How to create integration tests that use a real, in-memory database, far more similar to production environment + +## Introduction + +Mocking a database is a commonly used strategy for testing of database-driven applications. While mocks offer convenience and low effort, many bugs and erroneous program states might happen due to interactions with a database in a real production system. This discrepancy can lead to unexpected issues when transitioning from testing to deployment, ultimately undermining the effectiveness of the testing process. + +It's crucial to recognize that the effectiveness of your testing process heavily depends on how closely it mirrors real-life production conditions. Testing in an environment that is similar to production helps to detect potential issues early on, ensuring a smoother transition to deployment and a more reliable application in the hands of users. + +Being aware of it, we've decided to provide a simple solution that developers may take leverage of to use the real database instead of mocks with no effort, while working with it in the various types of tests, especially in the integration tests. We've created test drivers, that let users instantiate the database component in no time. + +We will be running RavenDB in-memory, ensuring that each test receives its own separated and isolated database instance. These instances will be automatically disposed, which solves the problem with a state of a database. Provisioning in-memory databases is fast and easy, making the testing seamlessly integrable into CI/CD pipelines. This approach not only simplifies the testing process but also ensures that developers can work with a database environment that closely mimics production conditions, leading to more reliable and accurate test results. + +In this article we'll take a closer look upon NuGet [RavenDB.TestDriver](https://www.nuget.org/packages/RavenDB.TestDriver) package. + +We'll go quickly through the simple setup, to focus on the usage. Let's dive into it. + +## Step-by-Step guide + +### What we'll use + +- **C# Tests Framework**: We'll utilize the XUnit package for writing our test cases. It's not mandatory, use your own favorite framework. +- **RavenDB**: We'll integrate the RavenDB.TestDriver package to interact with RavenDB. + +### 1. Installing RavenDB Test Driver + +Begin by including the *RavenDB.TestDriver* package in your project: + +```bash +dotnet add package RavenDB.TestDriver +``` + +It will install the test driver and the [embedded server](https://www.nuget.org/packages/RavenDB.Embedded). + +### 2. Integrating Test Driver into your tests + +Your test class like `BasicTest` should be inheriting `RavenTestDriver` from `Raven.TestDriver`. + +#### Step 2: Creating a test case equipped with RavenTestDriver + +```csharp +public class BasicTest : RavenTestDriver +{ + [Fact] + public void BasicTest() + { + // your test code here + } +} +``` + +You can override RavenTestDriver methods to customize its behavior. +- The *PreInitialize()* method is called right **before** the DocumentStore is initialized. Pre-configure your `DocumentStore` here, e.g. change the store `DocumentConventions` +- The *SetupDatabase()* method is called right **after** the `DocumentStore` is initialized. Here you can already work with a running server - store new documents, setup and configure features like revisions or expiration, prepare ongoing tasks like ETLs or subscriptions, execute various operations, etc. + +#### Overriding RavenTestDriver PreInitialize() and SetupDatabase() methods + +```csharp +class BasicTest : RavenTestDriver +{ + protected override void PreInitialize(IDocumentStore documentStore) + { + documentStore.Conventions.FindCollectionName = t => + { + if (t == typeof(Company)) + return "BrewingCompanies"; + + return null; + }; + } + + protected override void SetupDatabase(IDocumentStore documentStore) + { + documentStore.Maintenance.Send(new ConfigureRevisionsOperation(new RevisionsConfiguration + { + Default = new RevisionsCollectionConfiguration + { + Disabled = false, + PurgeOnDelete = true, + MinimumRevisionsToKeep = 1, + MinimumRevisionAgeToKeep = TimeSpan.FromDays(14), + } + })); + } + + [Fact] + public void BasicTest() + { + // your test code here + } +} +``` + +### 3. Using Test Driver to get database instance + +Now, you can write new tests or replace mocks in existing tests with real database instances. Your [document store](/7.2/client-api/what-is-a-document-store) *IDocumentStore* is available by calling the GetDocumentStore() method. It's the one from *RavenDB.Client* NuGet package, our .NET database client with powerful API. Interact with the docs and other entities in your database instance, like in the .NET C# client. + +#### Step 3: Writing your test + +```csharp +public class BreweryTest : RavenTestDriver +{ + [Fact] + public void TestBasicContractor() + { + using (var store = GetDocumentStore()) + { + using (var session = store.OpenSession()) + { + session.Store(new Contractor + { + FirstName = "Andy", + LastName = "Paulaner" + }); + session.SaveChanges(); + } + + // the rest of your test + } + + } +} +``` + +### 4. Debugging the database + +In some cases, you might want to check what's under the hood, working with the database. We've provided a *WaitForUserToContinueTheTest(store)* method, that freezes the test and opens-up the Raven Studio UI in the background, which lets you investigate the issues and influence the databases/server state in the meantime. It works only when the debugger is attached, so you don't have to add and remove the method call every time you want to run the code after debugging. + +#### Step 4: Using 'wait for user to continue the test' + +```csharp +public class BreweryTest : RavenTestDriver +{ + [Fact] + public void TestBasicContractor() + { + using (var store = GetDocumentStore()) + { + using (var session = store.OpenSession()) + { + session.Store(new Contractor + { + FirstName = "Andy", + LastName = "Paulaner" + }); + session.SaveChanges(); + } + + WaitForUserToContinueTheTest(store); + + using (var session = store.OpenSession()) + { + // Do something else if needed + } + + // Assert + } + } +} + +``` + +The studio pops out in the browser: +RavenDB Studio opened during test debugging + +After you're done reviewing your data in a database, click *Continue test* button located on the bottom UI bar, to continue the execution of a test. + +### 5. Address indexing delays if needed + +RavenDB works on indexing in the background. You can find more about it in the RavenDB docs on [indexing process](/7.2/studio/database/indexes/indexes-overview#indexing-process) and [stale indexes](/7.2/indexes/stale-indexes). +Usually, the delay from indexing is so small that it can be ignored. But it's crucial during the tests. + +In real life, programmers should know about this delay and how it can affect results. But here, unlike in real life, we use *WaitForIndexing()* to ensure our tests won't fail to interact with index which did not process the documents yet. We need to wait for indexes to finish to avoid test failures. Even single or couple of documents can cause a false-negative test result. + +In the example below, we're going a little bit further. We define the test driver that creates *BreweryCustomersByShippingAddressLocation* [spatial index](/7.2/indexes/indexing-spatial-data) (which can be [queried](/7.2/indexes/querying/spatial) later on) before every test using *SetupDatabase()* override. In the test we'll insert the data, and then we'll instruct the driver to wait for index to finish the processing. + +#### Step 5: Instructing driver to wait for index to process the new data + +```csharp + +public class Customer +{ + public string Name { get; set; } + public Location ShippedTo { get; set; } +} + +public class Location +{ + public Location(double lat, double lng) { Lat = lat; Lng = lng; } + public double Lat { get; set; } + public double Lng { get; set; } +} + +public class BreweryCustomers_ByShippingAddressLocation : AbstractIndexCreationTask +{ + public BreweryCustomers_ByShippingAddressLocation() + { + Map = customers => from c in customers + select new + { + Name = c.Name, + ShippingLocation = CreateSpatialField(c.ShippedTo.Lat, c.ShippedTo.Lng) + }; + } +} + +class AdvancedBreweryTest : RavenTestDriver +{ + protected override void SetupDatabase(IDocumentStore documentStore) + { + new BreweryCustomers_ByShippingAddressLocation().Execute(documentStore); + } + + [Fact] + public void TestCustomerDistributionStatistics() + { + using (var store = GetDocumentStore()) + { + using (var session = store.OpenSession()) + { + Customer customer1 = new Customer(); + customer1.Name = "Paul Becks"; + customer1.ShippedTo = new Location(51.509865, -0.118092); + + Customer customer2 = new Customer(); + customer2.Name = "Arjen Heineken"; + customer2.ShippedTo = new Location(52.377956, 4.897070); + + Customer customer3 = new Customer(); + customer3.Name = "Tom Grodziski"; + customer3.ShippedTo = new Location(52.105124, 20.591883); + + session.Store(customer1, "customers/1"); + session.Store(customer2, "customers/2"); + session.Store(customer3, "customers/3"); + session.SaveChanges(); + } + + // Let's wait for indexes to process the docs before asserting + WaitForIndexing(store); + } + + // your code and assert + } +} + +``` + +The *WaitForIndexing(store)* method ensures that the database indexes are up-to-date before proceeding with the test. Stale indexes, which occur when queries are executed before indexing is complete, can lead to incorrect or outdated results in tests. + +### Registering a license + +The *RavenDB.TestDriver* package offers the option to register a license by providing an environmental variable called *RAVEN\_LICENSE*. While not mandatory, registering a license can unlock additional features and performance enhancements, particularly for advanced operations. + +Enabling a license may increase the number of cores available, resulting in significant performance boosts for tests that heavily utilize the database. Consider registering a license for optimal performance in your testing environment. + +**To request a developer license, which is suitable for CI/CD purposes, visit the [RavenDB license request page](https://ravendb.net/license/request/dev)**. + +## FAQ + +### Q: I'm getting this error after trying to run the test: + +``` +Unable to start the RavenDB Server +Error: +It was not possible to find any compatible framework version +The framework 'Microsoft.AspNetCore.App', version '5.0.13' (arm64) was not found. + - The following frameworks were found: + 6.0.1 at [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] + +``` + +### It says that dotnet framework installed on my machine isn't compatible. Why the package doesn't include dotnet included to actually run the server? + +**A:** In order to maintain a lightweight package, we cannot include multiple framework versions with the server files. RavenDB typically aligns with the newest dotnet version on each release. It's the responsibility of the user to ensure they have a compatible dotnet version installed. If you encounter errors like the one above, you can obtain the necessary dotnet version from the [official dotnet website](https://dotnet.microsoft.com/en-us/download/dotnet). + +### Conclusion + +Integrating RavenDB into your .NET integration tests using the *RavenDB.TestDriver* package offers a convenient way to move beyond mocking and interact with a real database instance. By following the steps outlined in this guide, you can seamlessly incorporate RavenDB into your testing workflow, leading to more robust and reliable tests. + +The ability to work with real database instances provides a more accurate representation of your application's behavior in a production environment, ultimately improving the quality of your software. Embrace the power of real database testing with RavenDB.TestDriver and elevate your testing practices to new heights.