MassTransit and WebApplicationFactory functional testing - integration-testing

I'm making use of MassTransit to receive some messages from a client's application, and redistribute the message within our environment with some routing headers added.
Whilst we are processing a high amount of messages, not all consuming applications are going to be interested in the whole set of the messages. As such the various consumers are configured with the SNS/SQS FilterPolicy attribute to ensure that only the messages we care about are consumed.
The issues I'm experiencing comes about when using the WebApplicationFactory and the IServiceCollection AddMassTransitTestHarness extension method.
The first issue might be down to my misunderstanding. As I'm using SQS/SNS specific functionality, the InMemoryTestHarness is unsuitable. I'm calling the below snippet when configuring my MassTransitTestHarness:
services.AddMassTransitTestHarness( x =>
{
RegisterConsumers( x );
x.UsingAmazonSqs( ( context, factoryConfigurator ) =>
{
factoryConfigurator.Host( new Uri( "amazonsqs://localhost:4566" ),
h =>
{
h.Config( new AmazonSimpleNotificationServiceConfig { ServiceURL = serviceUrl } );
h.Config( new AmazonSQSConfig { ServiceURL = serviceUrl } );
h.AccessKey( "test" );
h.SecretKey( "test" );
} );
RegisterReceiveEndpoints( factoryConfigurator, context );
} );
} );
protected override void RegisterConsumers( IRegistrationConfigurator configurator )
{
configurator.AddConsumer<MovementConsumer>();
}
protected override void RegisterReceiveEndpoints( IAmazonSqsBusFactoryConfigurator factoryConfigurator, IRegistrationContext context )
{
factoryConfigurator.ReceiveEndpoint( $"{ServiceConstants.ServiceName}-test-movement", endpointConfigurator =>
{
endpointConfigurator.ConfigureConsumer<MovementConsumer>( context );
endpointConfigurator.QueueSubscriptionAttributes["FilterPolicy"] = $"{{\"RoutingKey\": [\"null\"]}}";
} );
}
My first question with this approach is that is it necessary to re-register consumers? Ideally I'd like to call AddMassTransitTestHarness and just have it replace the already existing Bus with the TestHarness, but I was finding my consumers weren't being called. Having to re-register the endpoints in both the tests project and the actual project is a burden I'd like to avoid any other developers on this project having.
The second question I have is with regards to asserting against what's been published. I'm experiencing inconsistent results with the below code:
await busTestHarness.Start();
await busTestHarness.Bus.Publish( message, CancellationToken.None );
await busTestHarness.InactivityTask;
//await Task.Delay( TimeSpan.FromSeconds( 2 ) );
busTestHarness.Published.Select<T>().Any( publishedMessage => publishedMessage.Context.Headers.Any( headerValue => headerValue.Key == "RoutingKey" && (string) headerValue.Value == expectedHeader ) ).ShouldBeTrue();
Sometimes the above assertion fails. I am expecting my consumer to publish a new message with routing headers (I am not asserting against the message I publish in the test).
I've found that whilst still flakey, the 2 second delay seems to reduce the rate of failure. Is my usage of InactivityTask correct? Is there anything else I should be waiting for.
Thanks for any help, I've scoured the docs and watched the video on testing with WebApplicationFactory. I've mirrored what's done in there as best as I can. The major difference being that I am not expecting any responses from my messages.

Related

CSS missing in the eyes of Google

When I load the website in a browser, it shows up correctly. However, when testing it with search.google.com it shows up without style sheets (and thereby not passing the test to be suitable for mobile devices). I assume it is because of the service worker I am using. However, I don't know that the problem is with it?
Below is the full code of the service worker in place:
const
_ = {
domain: 'https://matchflix.ch/',
name: 'matchflix'
},
cachable = [_.domain, 'https://fonts.googleapis.com/', 'https://ajax.googleapis.com/'],
log = 'ServiceWorker 1.1'
;
self.addEventListener('fetch', event => {
if(event.request.method !== 'GET')
return;
if(!event.request.referrer.startsWith(_.domain))
return;
if(!cachable.some(url => event.request.url.startsWith(url)))
return;
function versionedURL(request){
switch(request.destination){
case 'image':
case 'script':
case 'style':
let
version = self.serviceWorker.scriptURL.split('?')[1]
;
return new Request(request.url + '?' + version);
default:
return request;
}
}
let
internal = event.request.url.startsWith(_.domain),
request = internal ? versionedURL(event.request) : event.request
;
event.respondWith(caches.open(_.name)
.then(cache => fetch(request)
.then(response => {
console.debug(log, 'Caching', internal, response, request);
if(internal)
cache.put(request, response.clone());
return response;
})
.catch(() => cache.match(request))
)
);
});
This is how the website looks in the eyes of Google:
What I tried so far
Commenting out the registration of the service worker, did not change anything unfortunately.
On search.google.com I saw under more information that there was an unknown error loading the script and style sheets. Unfortunately, no further information was given.
Your site is too slow. The entire page, including all assets needs to load within about 5 seconds so that Googlebot doesn't give up on rendering. I'm opening your site now and the spinner is still going after 30 seconds.

Mobilefirst Push Notiffication Wrapper WLAuthorizationManager.obtainAccessToken('push.mobileclient')

We are using Offline Login in our application.
We are using the below code to do for un-registering the Device from MFP Server while Logging Out of our application.
function unregisterDevice() {
const deferred = $q.defer();
WLAuthorizationManager.obtainAccessToken('push.mobileclient').then(token => MFPPush.unregisterDevice((successResponse) => {
deferred.resolve(successResponse);
}, (failureResponse) => {
pelLogger.warn('Failed to unregister from push notifications', failureResponse);
deferred.resolve(failureResponse);
}), (error) => {
deferred.resolve(error);
});
return deferred.promise;
}
The Above code WLAuthorizationManager.obtainAccessToken('push.mobileclient') doesn't return anything at some point of time no failure / error / success which causes our application to just look ideal. It was said in Feb 2018 that this code is written because
/*
* NOTE: in the code below MFPPush API calls are wrapped with "WLAuthorizationManager.obtainAccessToken("push.mobileclient")".
* This is due to a defect in the current release of the product.
*/
Do we still need to do that ? even after a year and so many updates.
Invoking WLAuthorizationManager.obtainAccessToken('push.mobileclient') before all MFPPush API calls , is not mandatory.
If you are trying to invoke the unregister API while the device is offline, note that this will not work.

How to implement a simple reply in rebus

public static void SendREsbDx(Job job)
{
using (var adapter = new BuiltinContainerAdapter())
{
adapter.Handle<ReplyMsg>(msg =>
{
string mss = msg.message;
});
Configure.With(adapter)
.Logging(l => l.ColoredConsole(LogLevel.Warn))
.MessageOwnership(o => o.FromRebusConfigurationSection())
.Transport(t => t.UseSqlServer("server=.;initial catalog=rebus_test;integrated security=true","consumerx","error")
.EnsureTableIsCreated())
.CreateBus()
.Start();
adapter.Bus.Send<Job>(job);
}
}
I am using the above code to send a message to a consumer. The consumer will use the bus.Reply, but the above code obviously does not work.
I simply want to be able to receive a reply from the consumer. How would this be accomplished?
Sounds like your consumer does not have a handler for Job messages.
In your case, it sounds like you'll need two bus instances - a consumer instance that has an implementation of IHandleMessages<Job> which will bus.Reply(new ReplyMsg {...}), and a producer instance that has an implementation of IHandleMessages<ReplyMsg> which will bus.Send(new Job{...}) and do whatever needs to be done in the reply handler.
If you're interested in looking at some sample code that demonstrates request/reply, take a look at the integration sample in the Rebus samples repository which has some simple request/reply going on between the Client (which would correspond to the producer in your case) and the IntegrationService (which corresponds to the consumer).
The following code snippet demonstrates how it can be done:
var producer = new BuiltinContainerAdapter();
var consumer = new BuiltinContainerAdapter();
consumer.Handle<Job>(job => {
...
consumer.Bus.Reply(new ReplyMsg {...});
});
producer.Handle<ReplyMsg>(reply => {
....
});
Configure.With(producer)
.Transport(t => t.UseSqlServer(connectionString, "producer.input", "error")
.EnsureTableIsCreated())
.MessageOwnership(o => o.FromRebusConfigurationSection())
.CreateBus()
.Start();
Configure.With(consumer)
.Transport(t => t.UseSqlServer(connectionString, "consumer.input", "error")
.EnsureTableIsCreated())
.MessageOwnership(o => o.FromRebusConfigurationSection())
.CreateBus()
.Start();
// for the duration of the lifetime of your application
producer.Bus.Send(new Job {...});
// when your application shuts down:
consumer.Dispose();
producer.Dispose();
and in your app.config there must be an endpoint mapping that maps Job to consumer.input:
<rebus>
<endpoints>
<add messages="SomeNamespace.Job, SomeAssembly" endpoint="consumer.input"/>
</endpoints>
</rebus>
I hope you can see now why your code does not work. Please let me know if I should elaborate further :)
I've added a request/reply sample to the Rebus samples repository to serve as proof that the code shown above can actually run (provided that you remove the .... etc of course - you need a basic understanding of C# to be able to use this code)

AutoMapper getting maps mixed up when running large number of tests?

I currently have a solution with about 1000 unit/integration tests using MSTest runner. The problem Im experiencing with AutoMapper is that when I have VS run all tests in the solution, I'll randomly get a few unit tests fail due to the following automapper exception:
AutoMapper.AutoMapperMappingException:
Mapping types:
String -> String
System.String -> System.String
Destination path:
CatalogResource.CultureCode
Source value:
en-US ---> System.InvalidCastException: Unable to cast object of type 'Model.Catalog' to type 'Model.CatalogResource'.
In the code I dont have a Catalog to CatalogResource map nor am I trying to map them to each other. This type of exception doesnt happen in PROD, doesnt happen for the integration tests, doesnt happen every time I run the tests and when it does happen, it is always different tests that fail. Also, if I run only the tests that just failed, they always pass. I have a hard time consistently reproducing this locally but it happens more frequently on the build server, which is a pain because it prevents the build from getting automatically promoted to the next environment.
I've also tried adding this to the tests but it has not solved the problem:
[ClassInitialize]
public static void ClassInitialize(TestContext context)
{
Mapper.Reset();
}
Any ideas? We've had 4 different people take a stab at this to no avail so Im resorting to you great folks for help now.
Thanks!
EDIT:
I currently initialize the configuration using a static class that gets called inside a static constructor for the service. I.E.
static MyService()
{
AutoMapperBootstrapper.Initialize();
}
...
internal static class AutoMapperBootstrapper
{
public static void Initialize()
{
ConfigureMappings();
#if DEBUG
Mapper.AssertConfigurationIsValid();
#endif
}
}
EDIT #2:
While doing some more tinkering today, I notice that these random failures happen only for <IDataReader, something else> maps. For example:
Mapper.CreateMap<IDataReader, Catalog>()
.ForMember(m => m.CatalogID, opt => opt.MapFrom(src => src["CatalogID"]))
.ForMember(m => m.Title, opt => opt.MapFrom(src => src["Title"]))
.ForMember(m => m.DateCreatedUTC, opt => opt.MapFrom(src => src["DateCreatedUTC"]))
.ForMember(m => m.DateModifiedUTC, opt => opt.MapFrom(src => src["DateModifiedUTC"]))
...
Not sure what to make of it yet but it seemed relevant.
I presume you call AutoMapperBootstrapper.Initialize from your unit tests, and the ONLY location you have the mapping configuration is in ConfigureMappings? I can't explain your issue, but you obviously have some leakage between your unit tests somewhere. I have a similar setup, and this is what I do...
My initialisation code is
public static class MappingConfiguration
{
public static void RegisterMappings()
{
Mapper.Initialize(x =>
{
x.AddProfile<MapperProfileOne>();
x.AddProfile<MapperProfileTwo>();
});
}
}
This is only ever called by the application (I dont bother testing the RegisterMappings method as it only has the single Initialize call and I trust that AutoMapper works. My unit tests (to test my mapping configuration) are as follows:
[Test]
public void Initialise_ProfileOne_GeneratesNoErrors()
{
Mapper.Initialize(x => x.AddProfile<MapperProfileOne>());
Mapper.AssertConfigurationIsValid();
}
[Test]
public void Initialise_ProfileTwo_GeneratesNoErrors()
{
Mapper.Initialize(x => x.AddProfile<MapperProfileTwo>());
Mapper.AssertConfigurationIsValid();
}
An example of a profile is:
public class MapperProfileOne : Profile
{
protected override void Configure()
{
Mapper.CreateMap<Source, Destination>();
}
}
I'm wondering that if you're calling the AutoMapperBootstrapper method from your unit tests that its somehow getting confused (there's not enough detail in the question to determine this for certain). I don't think it should, but hopefully there's at least something in the code above that might magically fix the issue for you.

Any way to to use PHPUnit for testing API requests and responses using just PHP?

The responses are in JSON and I am using a custom-built MVC framework which I'm not sure how the request and response process is produced. Service methods are created using the following syntax.
public function getSessionsMethod()
{
// data auto encoded as JSON
return array('hello', 'world');
}
A request from JavaScript would look like this /svc/api/getSessions.
My initial thought was to simply use a streams approach are there best practices for this form of testing?
public function testCanGetSessionsForAGivenId()
{
$params = http_build_query(
array(
'id' => 3,
)
);
$options = array(
'http' => array(
'method' => 'GET',
'content' => $params,
)
);
$context = stream_context_create($options);
$response = file_get_contents(
'http://vbates/svc/api/getSessions', false, $context
);
$json = json_decode($response);
$this->assertEquals(3, $json->response);
}
This doesn't look like unit testing to me but rather integration testing. You can use PHPUnit to do it, but you should understand the difference first.
There are many components involved in getting the response for a given service method:
The dispatcher: Extracts the parameters from the URL and dispatches to the appropriate service method.
The service method: Does the real work to be tested here.
The JSON encoder: Turns the service method's return value into a JSON response.
You should first test these individually in isolation. Once you've verified that the dispatcher and encoder work for general URLs and return values, there's no point in wasting cycles testing that they work with every service method.
Instead, focus your effort on testing each service method without involving these other components. Your test case should instantiate and call the service methods directly with various inputs and make assertions on their return values. Not only will this require less effort on your part, it will make tracking down problems easier because each failure will be limited to a single component.

Resources