Getting Started: Writing your Unit Test using TestDriver
In this section we will explain how to use RavenDB.TestDriver in order to write unit tests for working with RavenDB. TestDriver uses an Embedded package with the same set of prerequisites to run the Server.
RavenTestDriver
First, we define a class that derives from Raven's TestDriver. Let's start with reviewing the TestDriver's methods and properties and later we will get into implementation (a complete code sample of a RavenTestDriver can be found at the bottom of the page).
Properties and Methods
Signature | Description |
---|---|
protected virtual string DatabaseDumpFilePath => null; | Allows you to override the path to the database dump file that will be loaded when calling ImportDatabase. |
protected virtual Stream DatabaseDumpFileStream => null; | Allows you to override the stream containing the database dump that will be loaded when calling ImportDatabase. |
public IDocumentStore GetDocumentStore([CallerMemberName] string database = null, TimeSpan? waitForIndexingTimeout = null) | Gets you an IDocumentStore instance for the requested database. |
protected virtual void PreInitialize(IDocumentStore documentStore) | Allows you to pre-initialize the IDocumentStore. |
protected virtual void SetupDatabase(IDocumentStore documentStore) | Allows you to initialize the database. |
protected event EventHandler DriverDisposed; | An event that is raised when the test driver has been disposed of. |
public static void ConfigureServer(TestServerOptions options) | Allows you to configure your server before running it |
public void WaitForIndexing(IDocumentStore store, string database = null, TimeSpan? timeout = null) | Allows you to wait for indexes to become non-stale. |
public void WaitForUserToContinueTheTest(IDocumentStore store) | Allows you to break the test and launch the Studio to examine the state of the database. |
protected virtual void OpenBrowser(string url) | Allows you to open the browser. |
public virtual void Dispose() | Allows you to dispose of the server. |
PreInitialize
Pre-Initializing the IDocumentStore allows you to mutate the conventions used by the document store.
Example
//This allows us to modify the conventions of the store we get from 'GetDocumentStore'
protected override void PreInitialize(IDocumentStore documentStore)
{
documentStore.Conventions.MaxNumberOfRequestsPerSession = 50;
}
UnitTest
TestDocumentByName
index and TestDocument
class that can be seen in the full example
Example
[Fact]
public void MyFirstTest()
{
using (var store = GetDocumentStore())
{
store.ExecuteIndex(new TestDocumentByName());
using (var session = store.OpenSession())
{
session.Store(new TestDocument { Name = "Hello world!" });
session.Store(new TestDocument { Name = "Goodbye..." });
session.SaveChanges();
}
WaitForIndexing(store); //If we want to query documents sometime we need to wait for the indexes to catch up
WaitForUserToContinueTheTest(store);//Sometimes we want to debug the test itself, this redirect us to the studio
using (var session = store.OpenSession())
{
var query = session.Query<TestDocument, TestDocumentByName>().Where(x => x.Name == "hello").ToList();
Assert.Single(query);
}
}
}
In the test we get an IDocumentStore to our test database. Deploy an index and insert two documents into it. We then wait for the indexing to complete and launch the Studio so we can verify that the documents and index are deployed (we can remove this line once the test is working). At the end of the test we query for TestDocument where their name contains the world 'hello' and assert that we have only one such document.
ConfigureServer
ConfigureServer
method allows you to be more in control on your server.
You can use it with TestServerOptions
to change the path to the Raven server binaries or to specify where your RavenDB data is stored, security, etc.
TestServerOptions
TestServerOptions
inherits from ServerOptions. In that way you can be more in control of how the embedded server is going to run
with just a minor change. Here you can change your ServerDirectory.
Example
var testServerOptions = new TestServerOptions
{
ServerDirectory = "PATH_TO_RAVENDB_SERVER", // Specify where ravendb server binaries are located (Optional)
DataDirectory = "PATH_TO_RAVENDB_DATADIR", // Specify where ravendb data will be placed/located (Optional)
};
ConfigureServer(testServerOptions);
Complete Example
using Raven.Client.Documents;
using Raven.TestDriver;
using Xunit;
using System.Linq;
using Raven.Client.Documents.Indexes;
namespace RavenDBTestDriverFullExample
{
public class RavenDBTestDriver : RavenTestDriver
{
//This allows us to modify the conventions of the store we get from 'GetDocumentStore'
protected override void PreInitialize(IDocumentStore documentStore)
{
documentStore.Conventions.MaxNumberOfRequestsPerSession = 50;
}
[Fact]
public void MyFirstTest()
{
ConfigureServer(new TestServerOptions
{
DataDirectory = "C:\\RavenDBTestDir"
});
using (var store = GetDocumentStore())
{
store.ExecuteIndex(new TestDocumentByName());
using (var session = store.OpenSession())
{
session.Store(new TestDocument { Name = "Hello world!" });
session.Store(new TestDocument { Name = "Goodbye..." });
session.SaveChanges();
}
WaitForIndexing(store); //If we want to query documents sometime we need to wait for the indexes to catch up
WaitForUserToContinueTheTest(store);//Sometimes we want to debug the test itself, this redirect us to the studio
using (var session = store.OpenSession())
{
var query = session.Query<TestDocument, TestDocumentByName>().Where(x => x.Name == "hello").ToList();
Assert.Single(query);
}
}
}
}
public class TestDocumentByName : AbstractIndexCreationTask<TestDocument>
{
public TestDocumentByName()
{
Map = docs => from doc in docs select new { doc.Name };
Indexes.Add(x => x.Name, FieldIndexing.Search);
}
}
public class TestDocument
{
public string Name { get; set; }
}
}
Continuous Integration Servers
Best practice is to use a CI/CD server to help automate the testing and deployment of your new code. Popular CI/CD products are AppVeyor or Visual Studio Team Services (aka. VSTS).