18 January 2012 HowTo, Index, NoSQL, RavenDB, Unit Test Robert Muehsig

Wer mit RavenDB arbeitet kommt automatisch zu einem sehr mächtigen Mittel: Den Indexen. Der Hauptfokus des Posts liegt hierbei auf dem Unit-Testen von RavenDB. Unit-Testing in Datenbank-Projekten ist mehr als anstrengend und zeitfressend. RavenDB lässt sich allerdings recht einfach in einen “Test” Modus versetzen, sodass die Funktionalität erhalten bleibt.

Achtung: Streng genommen darf ein Unit-Test auch keine Datenbank berühren, da man damit mehr als die eigentliche Test-Einheit testet. Richtiger wäre Integrationstests, allerdings redet die halbe Softwarewelt generell von Unit-Tests. Daher belassen wir es mal bei der unschärfe.

Der Code selbst ist unter Mithilfe von Daniel Lang entwickelt, als er mir bei einem RavenDB Index geholfen hat.

Was ist ein RavenDB Index?

Ein RavenDB Index kann man sich als gespeicherte Abfrage vorstellen, welche von RavenDB im Hintergrund ausgeführt wird und das entsprechende Result zwischenspeichert. Über RavenDB Indexe können Abfragen über mehrere Dokumente gemacht werden und über Map/Reduce die Ergebnismenge angepasst werden.

Bsp:

Ein ganz simpler Index sieht so aus:

    public class SearchIndex : AbstractIndexCreationTask<Term>
    {
        public SearchIndex()
        {
            Map = terms => from term in terms
                            select new { term.Title };

            Index(x => x.Title, FieldIndexing.Analyzed);
        }
    }

 

Was das ganze überhaupt macht, ist hier gut erklärt. Wenn ich den Code anwenden möchte:

Session.Query<Term, SearchIndex>().Where(x => x.Title.StartsWith(searchTerm)).ToList();

 

Unit-Test dazu (gemacht mit xUnit)

 

public abstract class RavenTest
    {
        protected IDocumentStore GetDatabase()
        {
            var documentStore = new EmbeddableDocumentStore
                                    {
                                        RunInMemory = true
                                    };
            documentStore.Initialize();

            return documentStore;
        }
    } 

public class SearchIndexTest : RavenTest
    {
        [Fact]
        public void TitleContainsSearch()
        {
            using (var documentStore = GetDatabase())
            {
                IndexCreation.CreateIndexes(typeof(UserActivityFeedIndex).Assembly, documentStore);

                using (var documentSession = documentStore.OpenSession())
                {
                    documentSession.Store(new Term
                                              {
                                                  Title = "RavenDB",
                                              });
                    documentSession.Store(new Term
                                              {
                                                  Title = "ASP.NET MVC",
                                              });

                    documentSession.Store(new Term
                                            {
                                                Title = "Twitter Bootstrap",
                                            });

                    documentSession.SaveChanges();

                    var result = documentSession.Query<Term, SearchIndex>().Where(x => x.Title.StartsWith("Boot"))
                                                                           .Customize(x => x.WaitForNonStaleResults())
                                                                           .ToList();

                    Assert.Equal(1, result.Count);

                    Assert.Equal("Twitter Bootstrap", result[0].Title);
                }
            }
        }
    }

 

Die RavenTest Klasse erstellt die Connection zur RavenDB “embedded” Datenbank. Über “RunInMemory” wird dies auch nur im Arbeitsspeicher gehalten. Vorteil: Sehr schnell und kein Cleanup nach dem Test.

In der Testmethode wird erst der Index angelegt (über Reflection wird die Assembly durchsucht) und dann werden Testdaten in diese DB abgespeichert. Am Ende erfolgt die Abfrage und das Ergebnis wird überprüft. Bei der Abfrage wird noch ein WaitForNonStaleResults dazugehangen um auch die gerade eben gespeicherten Daten mit abzufragen (RavenDB speichert die Index-Ergebnisse zwischen, sodass es zu einer kurzen Verzögerung kommen kann. Ist im Unit-Testing allerdings ungünstig.)

Sehr einfach und wesentlich eleganter als bei einer klassischen DB.


Written by Robert Muehsig

Software Developer - from Dresden, Germany, now living & working in Switzerland. Microsoft MVP & Web Geek.
Other Projects: KnowYourStack.com | ExpensiveMeeting | EinKofferVollerReisen.de

If you like the content and want to support me you could buy me a beer or a coffee via Litecoin or Bitcoin - thanks for reading!