W poprzednim wpisie przedstawiłem framework PACT.NET. Zdołaliśmy zaimplementować testy po stronie konsumentów. Wynikiem tego, był wygenerowany plik JSON zawierający nagrane testy. Dla przypomnienia, test po stronie konsumenta wyglądał następująco:
[TestFixture] public class ConsumerTests { private IMockProviderService _mockProviderService; private IPactBuilder _pactProvider; [OneTimeSetUp] public void OneTimeSetUp() { _pactProvider = new PactBuilder() .ServiceConsumer("ConsumerName") .HasPactWith("ProviderName"); _mockProviderService = _pactProvider.MockService(3423,false); } [OneTimeTearDown] public void OneTimeTearDown() { _pactProvider.Build(); } [Test] public void Should_Return_FirstName() { //Arrange _mockProviderService.Given("there are some persons data") .UponReceiving("a request to retrieve all persons") .With(new ProviderServiceRequest { Method = HttpVerb.Get, Path = "/api/persons" }) .WillRespondWith(new ProviderServiceResponse { Status = 200, Headers = new Dictionary<string, string> { { "Content-Type", "application/json; charset=utf-8" } }, Body = new[] { new { FirstName = "Piotr", } } }); var consumer = new PersonsApiClient("http://localhost:3423"); //Act var persons = consumer.GetAllPersons(); //Assert CollectionAssert.IsNotEmpty(persons); CollectionAssert.AllItemsAreNotNull(persons.Select(x=>x.FirstName)); _mockProviderService.VerifyInteractions(); } }
Z kolei wygenerowany JSON:
{ "provider": { "name": "ProviderName" }, "consumer": { "name": "ConsumerName" }, "interactions": [ { "description": "a request to retrieve all persons", "provider_state": "there are some persons data", "request": { "method": "get", "path": "/api/persons" }, "response": { "status": 200, "headers": { "Content-Type": "application/json; charset=utf-8" }, "body": [ { "FirstName": "Piotr" } ] } } ], "metadata": { "pactSpecificationVersion": "1.1.0" } }
Przejdźmy zatem do testu po stronie dostawcy:
[TestFixture] public class ProducerTests { [Test] public void Ensure_That_Consumer_Requirements_Are_Met() { var config = new PactVerifierConfig(); IPactVerifier pactVerifier = new PactVerifier(() => { }, () => { }, config); pactVerifier .ProviderState( "there are some persons data", setUp: DataSetup,tearDown:DataTearDown); using (var testServer = TestServer.Create<Startup>()) { pactVerifier .ServiceProvider("Persons API", testServer.HttpClient) .HonoursPactWith("Consumer") .PactUri(@"consumername-providername.json") .Verify(); } } private void DataTearDown() { } private void DataSetup() { } }
W celu uruchomienia serwera, używamy Microsoft.Owin.Testing czyli klasy TestServer. W momencie odpalenia testu, zostanie zatem stworzony serwer z naszą aplikacją. Następnie “odgrywany” będzie test wygenerowany przez konsumenta. Operujemy na prawdziwych danych, a nie na mock’ach, stąd metody DataSetup oraz DataTearDown w praktyce zawierają odpowiedni kod, dodający oraz usuwający dane testowe.
Po wykonaniu testu, zostanie wygenerowany plik persons_api_verifier.log:
2016-06-01 19:54:01.894 +01:00 [Debug] Verifying a Pact between ConsumerName and ProviderName Given there are some persons data a request to retrieve all persons with GET /api/persons returns a response which has status code 200 includes headers 'Content-Type' with value application/json; charset=utf-8 has a matching body (FAILED - 1) Failures: 1) Expected: [ { "FirstName": "Piotr" } ], Actual: [ { "FirstName": "Piotr", "LastName": "Zielinski" }, { "FirstName": "first name", "LastName": "last name" } ]
Widzimy, że test zakończył się niepowodzeniem, ponieważ oczekiwana zawartość Content jest inna niż ta zwrócona przez prawdziwy serwis.
Za pomocą PathVerifier definiujemy, które testy chcemy wykonać. W naszym przypadku jest tylko jeden. W praktyce definiuje się wiele stanów i kilka plików JSON.