TypeMock – Erste Schritte
Sunday, February 17th, 2008Aufgrund der Diskussionen während der ALT.NET UK Konferenz habe ich beschlossen mir TypeMock mal genauer anzuschauen. Vor allem der Support für Method Chaining interessiert mich. Aber immer der Reihe nach…
TypeMock ist ein kommerzielles Mock Framework. Es ist in drei Varianten verfügbar: Community, Professional und Enterprise (siehe Gegenüberstellung der Features). TypeMock verwendet das Profiler API um Aufrufe von Methoden abzufangen. Bisher arbeite ich mit Rhino.Mocks das für diesen Zweck Castle DynamicProxy verwendet. Mock Frameworks die auf Proxys basieren können lediglich Interfaces und virtuelle Methoden mocken. Das Ersetzen einer konkreten Klasse die kein Interface implementiert oder das Ersetzen nicht-virtueller oder statischer Methoden (inkl. Extension Methods) oder LINQ Querys ist mit solchen Frameworks technisch bedingt nicht möglich.
Bislang ging ich davon aus dass dies in der Praxis keine wirkliche Einschränkung für mich ist da es dazu führt, Klassen lose zu koppeln damit sie getestet werden können. Während der ALT.NET UK Konferenz wurde von Roy Osherove die Befürchtung geäußert dass man als Software Entwickler Designentscheidungen zur losen Kopplung von Komponenten weiter treibt als eigentlich sinnvoll, “nur” damit die Klassen getestet werden können. TypeMock verspricht diese technische Einschränkung aufzuheben.
In den Beispielen verwende ich ein weiteres Framework zum ersten mal: MSTest. Bislang habe ich NUnit eingesetzt (und werde das wohl auch weiterhin tun). Da MSTest in Visual Studio integriert ist und Code Coverage Analyse mitbringt bin ich sehr gespannt wie es sich für einen NUnit Nutzer “anfühlt”.
Vorbereitungen:
- TypeMock Download von hier.
- Installation
- Tools | Add-in Manager… | TypeMock Isolator aktivieren
Erster Test:
using Microsoft.VisualStudio.TestTools.UnitTesting; using TypeMock; namespace Model { [TestClass] public class CustomerTests { [TestMethod] public void Orders_can_be_mocked() { Order order = RecorderManager.CreateMockedObject<Order>(); Customer customer = new Customer(); using(RecordExpectations mock = RecorderManager.StartRecording()) { mock.ExpectAndReturn(customer.Orders, new Order[] { order }); } Assert.AreSame(order, customer.Orders[0]); } } }
Wenn TypeMock nicht aktiviert sein sollte erhält man folgende Fehlermeldung. Vorbildlich dass in der Meldung gleich beschrieben ist wie man das Problem abstellt!
Test method Orders_can_be_mocked threw exception: TypeMock.TypeMockException: *** Typemock Isolator is not currently enabled. To enable do one of the following: * To run Typemock Isolator as part of an automated process you can: - run tests via TMockRunner.exe command line tool - use 'TypeMockStart' tasks for MSBuild or NAnt * To work with Typemock Isolator inside Visual Studio.NET: set Tools->Enable Typemock Isolator from within Visual Studio For more information consult the documentation (see 'Running' topic).
Kein Fluent Interface
Da ich die Syntax von Rhino.Mocks gewohnt bin vermisse ich das Fluent Interface mit seiner compiletime Typsicherheit (siehe dazu hier):
// TypeMock mock.ExpectAndReturn(customer.Orders, new Order[] { order }); // Rhino.Mocks Expect.Call(customer.Orders).Return(new Order[] { order });
[TestMethod] public void WrongReturnType() { Customer customer = new Customer(); using (RecordExpectations mock = RecorderManager.StartRecording()) { mock.ExpectAndReturn(customer.Orders, 42); } }
Test method WrongReturnType threw exception: TypeMock.TypeMockException: *** Method get_Orders in type Model.Customer cannot return System.Int32.
Method Chaining
[TestMethod] public void MethodChaining() { Customer customer = new Customer(); using (RecordExpectations r = RecorderManager.StartRecording()) { r.ExpectAndReturn(customer.Orders[0].OrderItems[0].Product.Color, "Red"); } Assert.AreSame("Red", customer.Orders[0].OrderItems[0].Product.Color); }
[TestMethod] public void MethodChaining_Rhino() { MockRepository mocks = new MockRepository(); IOrder order = mocks.DynamicMock<IOrder>(); IOrderItem orderItem = mocks.DynamicMock<IOrderItem>(); IProduct product = mocks.DynamicMock<IProduct>(); Customer customer = new Customer(); customer.Orders.Add(order); using (mocks.Record()) { Expect.Call(order.OrderItems).Return(new IOrderItem[] {orderItem}); Expect.Call(orderItem.Product).Return(product); Expect.Call(product.Color).Return("Red"); } using (mocks.Playback()) { Assert.AreSame("Red", customer.Orders[0].OrderItems[0].Product.Color); } }


