I have a service proxy class that makes asyn call to service operation. I use a callback method to pass results back to my view model.
Doing functional testing of view model, I can mock service proxy to ensure methods are called on the proxy, but how can I ensure that callback method is called as well?
With RhinoMocks I can test that events are handled and event raise events on the mocked object, but how can I test callbacks?
ViewModel:
public class MyViewModel
{
public void GetDataAsync()
{
// Use DI framework to get the object
IMyServiceClient myServiceClient = IoC.Resolve<IMyServiceClient>();
myServiceClient.GetData(GetDataAsyncCallback);
}
private void GetDataAsyncCallback(Entity entity, ServiceError error)
{
// do something here...
}
}
ServiceProxy:
public class MyService : ClientBase<IMyService>, IMyServiceClient
{
// Constructor
public NertiAdminServiceClient(string endpointConfigurationName, string remoteAddress)
:
base(endpointConfigurationName, remoteAddress)
{
}
// IMyServiceClient member.
public void GetData(Action<Entity, ServiceError> callback)
{
Channel.BeginGetData(EndGetData, callback);
}
private void EndGetData(IAsyncResult result)
{
Action<Entity, ServiceError> callback =
result.AsyncState as Action<Entity, ServiceError>;
ServiceError error;
Entity results = Channel.EndGetData(out error, result);
if (callback != null)
callback(results, error);
}
}
Thanks
Played around with this a bit and I think I may have what you're looking for. First, I'll display the MSTest code I did to verify this:
[TestClass]
public class UnitTest3
{
private delegate void MakeCallbackDelegate(Action<Entity, ServiceError> callback);
[TestMethod]
public void CallbackIntoViewModel()
{
var service = MockRepository.GenerateStub<IMyServiceClient>();
var model = new MyViewModel(service);
service.Stub(s => s.GetData(null)).Do(
new MakeCallbackDelegate(c => model.GetDataCallback(new Entity(), new ServiceError())));
model.GetDataAsync(null);
}
}
public class MyViewModel
{
private readonly IMyServiceClient client;
public MyViewModel(IMyServiceClient client)
{
this.client = client;
}
public virtual void GetDataAsync(Action<Entity, ServiceError> callback)
{
this.client.GetData(callback);
}
internal void GetDataCallback(Entity entity, ServiceError serviceError)
{
}
}
public interface IMyServiceClient
{
void GetData(Action<Entity, ServiceError> callback);
}
public class Entity
{
}
public class ServiceError
{
}
You'll notice a few things:
I made your callback internal. You'll need to use the InternalsVisisbleTo() attribute so your ViewModel assembly exposes internals to your unit tests (I'm not crazy about this, but it happens in rare cases like this).
I use Rhino.Mocks "Do" to execute the callback whenever the GetData is called. It's not using the callback supplied, but this is really more of an integration test. I assume you've got a ViewModel unit test to make sure that the real callback passed in to GetData is executed at the appropriate time.
Obviously, you'll want to create mock/stub Entity and ServiceError objects instead of just new'ing up like I did.
Related
I want to inject my strongly typed hub in a service, but I don't like certain thing in the example shown by Microsoft - https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-2.2 (Inject a strongly-typed HubContext)
public class ChatController : Controller
{
public IHubContext<ChatHub, IChatClient> _strongChatHubContext { get; }
public ChatController(IHubContext<ChatHub, IChatClient> chatHubContext)
{
_strongChatHubContext = chatHubContext;
}
public async Task SendMessage(string message)
{
await _strongChatHubContext.Clients.All.ReceiveMessage(message);
}
}
In this example ChatHub is coupled to ChatController.
So I want to inject the hub itself
defined with generic interface parameter and no concrete implementation of it will be defined in my service.
This is sample code
public interface IReportProcessingClient
{
Task SendReportInfo(ReportProgressModel report);
}
public class ReportProcessingHub : Hub<IReportProcessingClient>
{
public async Task SendMessage(ReportProgressModel report)
{
await Clients.All.SendReportInfo(report);
}
}
public class ReportInfoHostedService : IHostedService, IDisposable
{
private readonly Hub<IReportProcessingClient> _hub;
private readonly IReportGenerationProgressService _reportService;
public ReportInfoHostedService(Hub<IReportProcessingClient> hub, IReportGenerationProgressService reportService)
{
_hub = hub;
_reportService = reportService;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_reportService.SubscribeForChange(async x =>
{
await _hub.Clients.All.SendReportInfo(x);
});
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public void Dispose()
{
}
}
This approach obviously will need additional registration of the hub in Startup.cs as it is not called by the context api provided by Microsoft.
services.AddSingleton<Hub<IReportProcessingClient>, ReportProcessingHub>();
app.UseSignalR(route => {
route.MapHub<ReportProcessingHub>("/reportProcessingHub");
});
All done and working until the hub is trying to send messages to Clients. Then I get the exception
_hub.Clients.All threw an exception of System.NullReferenceException: 'Object reference not set to an instance of an object.'
So to summerize:
1. Is this the right way to inject strongly typed hubs and what am I doing wrong(e.g. wrong registration of the hub in services, wrong usage of app.UseSingleR)?
2. If not, what is the correct way?
NOTE:
I know there is a lot easier way injecting IHubContext<Hub<IReportProcessingClient>>, but this is not a solution for me, because I have to call the hub method name passed as string parameter.
I want to ... and no concrete implementation of it will be defined in my service
If you don't want to expose a concrete hub implementation, you should at least expose a base class or interface. However, since a hub should inherit from the Hub class, we can't use an interface here. So let's create a public base hub ReportProcessingHubBase as well as an internal concrete ReportProcessingHub:
public abstract class ReportProcessingHubBase : Hub<IReportProcessingClient>
{
public abstract Task SendMessage(ReportProgressModel report);
}
// the concrete hub will NOT be exposed
internal class ReportProcessingHub : ReportProcessingHubBase
{
public override async Task SendMessage(ReportProgressModel report)
{
await Clients.All.SendReportInfo(report);
}
}
Make sure you've registered the two related service:
services.AddScoped<ReportProcessingHubBase, ReportProcessingHub>();
services.AddHostedService<ReportInfoHostedService>();
Make sure you're mapping the Base Hub (MOST IMPORTANT!):
endpoints.MapHub<ReportProcessingHubBase>("/report");
Finally, you can get the base hub by injecting IHubContext<ReportProcessingHubBase,IReportProcessingClient> :
public class ReportInfoHostedService : IHostedService, IDisposable
{
private readonly IHubContext<ReportProcessingHubBase,IReportProcessingClient> _hub;
public ReportInfoHostedService(IHubContext<ReportProcessingHubBase,IReportProcessingClient> hub)
{
_hub = hub;
}
...
}
Now, you can invoking the hub method in a strongly-typed way.
I am very new to Autofac and not able to understand the syntax for registration. I have following calsses/interfaces :
//Interface
public interface ISender
{
void Send();
}
//implementations
public class Post : ISender
{
public void Send()
{
//Post implementation
}
}
public class Email : ISender
{
public void Send()
{
//Email implementation
}
}
And a class that calls these implementations
public class Consumer
{
ISender Sender;
public Consumer(ISender sender)
{
Sender = sender
}
public void Act()
{
Sender.Send();
}
}
Now, which implementation to call is to be decided in a controller, so I tried using IIndex from this page like:
public calss PostController
{
Consumer ConsumerObject;
public PostController(IIndex<string, Consumer> consumer)
{
ConsumerObject = consumer["post"];
}
}
public class EmailController
{
Consumer ConsumerObject;
public PostController(IIndex<string, Consumer> consumer)
{
ConsumerObject = consumer["email"];
}
}
Firstly, is it correct or doable? Now the problem is I don't understand how to register in Autofac. So, How can we register Consumer and ISender in Autofac ?? Please suggest if there is any better/alternative way.
The way of registering components in Autofac is described widely in the documentation here. And how to use keyed services is described in the documentation you linked.
Basically you have to create ContainerBuilder, register all your components and build the container itself, based on the project type you have.
In your situation you need to use the following registrations:
var builder = new ContainerBuilder();
builder.RegisterType<Post>().Keyed<ISender>("post");
builder.RegisterType<Email>().Keyed<ISender>("email");
builder.RegisterType<Consumer>();
If you use ASP.NET WebApi (I assume that based on the fact you are using Controllers), you need to register your controllers
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
or for MVC
builder.RegisterControllers(typeof(MvcApplication).Assembly);
Now, there are various way to pick the Consumer with proper ISender implementation (I assume you want to pick proper implementation in controller).
One would be to inject IIndex<string, ISender> to Consumer class and pass the proper key string to it as well:
public class Consumer
{
ISender Sender;
public Consumer(string type, IIndex<string, ISender> sender)
{
Sender = sender[type];
}
public void Act()
{
Sender.Send();
}
}
Then, in controller you could use Func injection:
public class PostController
{
Consumer ConsumerObject;
public PostController(Func<string, Consumer> consumer)
{
ConsumerObject = consumer("post");
}
}
public class EmailController
{
Consumer ConsumerObject;
public EmailController(Func<string, Consumer> consumer)
{
ConsumerObject = consumer("email");
}
}
Another could be registering Consumer twice with Register method and resolving ISender in registration time.
This is my client side code to get the string "get-image-data" through RPC calls and getting byte[] from the server.
CommandMessage msg = new CommandMessage(itemId, "get-image-data");
cmain.ivClient.execute(msg, new AsyncCallback<ResponseMessage>() {
#Override
public void onFailure(Throwable caught) {
}
#Override
public void onSuccess(ResponseMessage result) {
if (result.result) {
result.data is byte[].
}
}
});
From the server side I got the length of the data is 241336.
But I could not get the value in onSuccess method. It is always goes to onFailure method.
And I got log on Apache:
com.google.gwt.user.client.rpc.SerializationException: Type '[B' was
not included in the set of types which can be serialized by this
SerializationPolicy or its Class object could not be loaded.
How can I do serialisation in GWT?
1) Create a pojo which implements Serializable interface
Let this pojo has all the data you want in the response of RPC service, in this case image-data
2) Pass this pojo in the response for your RPC service.
The below tutorial has enough information for creating RPC service
http://www.gwtproject.org/doc/latest/tutorial/RPC.html
The objects you transfer to and from the server has to implement IsSerializable.
All your custom Objects within the Object you are transferring also needs to implement IsSerializable.
Your objects cannot have final fields and needs an no argument constructor.
You need getters and setters.
A common serialize object in GWT:
public class MyClass implements IsSerializable {
private String txt;
private MyOtherClass myOtherClass; // Also implements IsSerializable
public MyClass() {
}
public String getTxt() {
return this.txt;
}
public void setTxt(String txt) {
return this.txt = txt;
}
public String getMyOtherClass() {
return this.myOtherClass;
}
public void setMyOtherClass(MyOtherClass myOtherClass) {
return this.myOtherClass = myOtherClass;
}
}
I have a simply interface:
public interface ITest
{
void Method1();
void Method2();
}
and implementation:
public class Test:ITest
{
public void Method1()
{
}
public void Method2()
{
//Method1();
}
}
The custom interceptor:
public class CustomInterceptor:IInterceptor
{
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
}
}
Now, when I execute there two methods:
ITest obj = getting through ninject
obj.Method1();
obj.Method2();
my interceptor is calling twice what is ok.
But when I uncomment the body of Method2(), then the interceptor for the Method1() is not called. I'm looking for what to do, because I want the interceptor to be fired.
When I call the Method1 from the second, I understand this is not called by the generated proxy and that's why it doesn't work. But is it possible to do it in same way?
Ninject creates a proxy object around the actual instance of the Test class. Your methods aren't virtual, so any override for the proxy should be created with 'new' rather than 'override'. Thus, if you call Method1 from Method2, there is no virtual lookup to find the proxy and invoke it.
I'm having a really hard time trying to figure how I can do .SetupXXX() calls on the underlying Mock<T> that has been generated inside the MockingKernel. Anyone who can shed some light on how it is supposed to work?
You need to call the GetMock<T> method on the MoqMockingKernel which will return the generated Mock<T> on which you can call your .SetupXXX()/VerifyXXX() methods.
Here is an example unit test which demonstrates the GetMock<T> usage:
[Test]
public void Test()
{
var mockingKernel = new MoqMockingKernel();
var serviceMock = mockingKernel.GetMock<IService>();
serviceMock.Setup(m => m.GetGreetings()).Returns("World");
var sut = mockingKernel.Get<MyClass>();
Assert.AreEqual("Hello World", sut.SayHello());
}
Where the involved types are the following:
public interface IService { string GetGreetings(); }
public class MyClass
{
private readonly IService service;
public MyClass(IService service) { this.service = service; }
public string SayHello()
{
return string.Format("Hello {0}", service.GetGreetings());
}
}
Note that you can access the generated Moq.MockRepository (if you prefer it over the SetupXXX methods) with the MoqMockingKernel.MockRepository property.