I have a ReactiveList with view models with a 'Selected' property. When I try to set this property from a unit-test, the subscription on ReactiveList.ItemChanged is not invoked. It works, however, when running the application and triggering the 'Selected' property from a check-box in the UI. Is there anything magic I have to do to make ReactiveList.ItemChanged work in a unit-test environment? I am using ReactiveUI 6.5 by the way.
WellsViewModels = new ReactiveList<WellViewModel> { ChangeTrackingEnabled = true };
var selectedWellsObservable =
Observable.Merge(
this.WhenAnyValue(vm => vm.WellSamplesFunc)
.Select(_ => Enumerable.Empty<IPropertyModelingWell>()),
WellsViewModels
.ItemChanged
.Where(x => x.PropertyName == nameof(WellViewModel.Selected))
.Throttle(TimeSpan.FromMilliseconds(100), RxApp.MainThreadScheduler)
.Select(_ => WellsViewModels.Where(vm => vm.Selected)
.Select(vm => vm.Well).ToList())
)
.Publish()
.RefCount();
selectedWellsObservable
.Select(_ => ComputeAllCheckedState(WellsViewModels))
.Subscribe(b => CheckAllWells = b) // <--- This is never invoked when unit-testing!?!
[Test]
public void CheckAllWells_UpdatedWhenWellsViewModelsSelectionChanges()
{
new TestScheduler().With(scheduler =>
{
// Arrange
...
...
// Act
_viewModel.WellsViewModels[0].Selected = true;
scheduler.AdvanceBy(TimeSpan.FromMilliseconds(200).Ticks);
// Assert
Assert.IsFalse(_viewModel.CheckAllWells.HasValue);
});
}
Related
I am attempting to mock out my SignalR hub for some unit tests. I am running into an issue on my Hubs OnConnectedAsync() call due to using a header to auto join a group if it exists. My issue is lying with the fact that the HubCallerContext.GetHttpContext() method is an extension method and cant be mocked. I dunno if there is a work around to this and I cant seem to find any similarly posted question about this.
OnConnectedAsync() Segment
Context.GetHttpContext().Request.Headers.TryGetValue(SignalRHeaders.GroupHeader, out StringValues header);
if (header.Any())
{
await Groups.AddToGroupAsync(Context.ConnectionId, SignalRConstants.Group);
}
Base Test Class
public DefaultHubBaseTest()
{
var memberId = Guid.NewGuid().ToString();
var orgId = Guid.NewGuid().ToString();
MockClients = new Mock<IHubCallerClients>();
MockClientProxy = new Mock<IClientProxy>();
MockClients.Setup(clients => clients.Group(It.IsAny<string>()))
.Returns(MockClientProxy.Object);
MockGroups = new Mock<IGroupManager>();
MockGroups.Setup(x => x.AddToGroupAsync(It.IsAny<string>(), It.IsAny<string>(), default(CancellationToken))).Returns(Task.CompletedTask);
MockGroups.Setup(x => x.RemoveFromGroupAsync(It.IsAny<string>(), It.IsAny<string>(), default(CancellationToken))).Returns(Task.CompletedTask);
Mock<HttpRequest> MockRequest = new Mock<HttpRequest>();
MockRequest.Setup(x => x.Headers).Returns(new HeaderDictionary()
{
{ SignalRHeaders.GroupHeader, orgId },
{ SignalRHeaders.GroupAdminHeader, "t" },
});
Mock<HttpContext> MockHttpContext = new Mock<HttpContext>();
MockHttpContext.Setup(x => x.Request).Returns(MockRequest.Object);
MockContext = new Mock<HubCallerContext>();
MockContext.Setup(x => x.ConnectionId).Returns("1");
MockContext.Setup(x => x.User.Claims).Returns(new List<Claim>() { new Claim(SignalRConstants.AzureAuthOID, memberId) });
MockContext.Setup(x => x.GetHttpContext()).Returns(MockHttpContext.Object);
DefaultHub = new DefaultHub()
{
Context = MockContext.Object,
Groups = MockGroups.Object,
Clients = MockClients.Object,
};
}
If anyone could help me with this, I would greatly appreciate it. Thanks!
We have some TypeScript code using the Aurelia framework and Dialog plugin that we are trying to test with Jasmine, but can't work out how to do properly.
This is the source function:
openDialog(action: string) {
this._dialogService.open({ viewModel: AddAccountWizard })
.whenClosed(result => {
if (!result.wasCancelled && result.output) {
const step = this.steps.find((i) => i.action === action);
if (step) {
step.isCompleted = true;
}
}
});
}
We can create a DialogService spy, and verify the open method easily - but we can't work out how to make the spy invoke the whenClosed method with a mocked result parameter so that we can then assert that the step is completed.
This is the current Jasmine code:
it("opens a dialog when clicking on incomplete bank account", async done => {
// arrange
arrangeMemberVerificationStatus();
await component.create(bootstrap);
const vm = component.viewModel as GettingStartedCustomElement;
dialogService.open.and.callFake(() => {
return { whenClosed: () => Promise.resolve({})};
});
// act
$(".link, .-arrow")[0].click();
// assert
expect(dialogService.open).toHaveBeenCalledWith({ viewModel: AddAccountWizard });
expect(vm.steps[2].isCompleted).toBeTruthy(); // FAILS
done();
});
We've just recently updated our DialogService and ran into the same issue, so we've made this primitive mock that suited our purposes so far. It's fairly limited and doesn't do well for mocking multiple calls with different results, but should work for your above case:
export class DialogServiceMock {
shouldCancelDialog = false;
leaveDialogOpen = false;
desiredOutput = {};
open = () => {
let result = { wasCancelled: this.shouldCancelDialog, output: this.desiredOutput };
let closedPromise = this.leaveDialogOpen ? new Promise((r) => { }) : Promise.resolve(result);
let resultPromise = Promise.resolve({ closeResult: closedPromise });
resultPromise.whenClosed = (callback) => {
return this.leaveDialogOpen ? new Promise((r) => { }) : Promise.resolve(typeof callback == "function" ? callback(result) : null);
};
return resultPromise;
};
}
This mock can be configured to test various responses, when a user cancels the dialog, and scenarios where the dialog is still open.
We haven't done e2e testing yet, so I don't know of a good way to make sure you wait until the .click() call finishes so you don't have a race condition between your expect()s and the whenClosed() logic, but I think you should be able to use the mock in the test like so:
it("opens a dialog when clicking on incomplete bank account", async done => {
// arrange
arrangeMemberVerificationStatus();
await component.create(bootstrap);
const vm = component.viewModel as GettingStartedCustomElement;
let mockDialogService = new MockDialogService();
vm.dialogService = mockDialogService; //Or however you're injecting the mock into the constructor; I don't see the code where you're doing that right now.
spyOn(mockDialogService, 'open').and.callThrough();
// act
$(".link, .-arrow")[0].click();
browser.sleep(100)//I'm guessing there's a better way to verify that it's finished with e2e testing, but the point is to make sure it finishes before we assert.
// assert
expect(mockDialogService.open).toHaveBeenCalledWith({ viewModel: AddAccountWizard });
expect(vm.steps[2].isCompleted).toBeTruthy(); // FAILS
done();
});
I have a route in akka-http app which is integrated with a 3rd party service via Http().cachedHostConnectionPoolHttps. I want to test it in a right way. But not sure how it should be :(
Here is how this route looks like:
val routes: Route = pathPrefix("access-tokens") {
pathPrefix(Segment) { userId =>
parameters('refreshToken) { refreshToken =>
onSuccess(accessTokenActor ? GetAccessToken(userId, refreshToken)) {
case token: AccessToken => complete(ok(token.toJson))
case AccessTokenError => complete(internalServerError("There was problems while retriving the access token"))
}
}
}
}
Behind this route hides accessTokenActor where all logic happens, here it is:
class AccessTokenActor extends Actor with ActorLogging with APIConfig {
implicit val actorSystem = context.system
import context.dispatcher
implicit val materializer = ActorMaterializer()
import AccessTokenActor._
val connectionFlow = Http().cachedHostConnectionPoolHttps[String]("www.service.token.provider.com")
override def receive: Receive = {
case get: GetAccessToken => {
val senderActor = sender()
Source.fromFuture(Future.successful(
HttpRequest(
HttpMethods.GET,
"/oauth2/token",
Nil,
FormData(Map(
"clientId" -> youtubeClientId,"clientSecret" -> youtubeSecret,"refreshToken" -> get.refreshToken))
.toEntity(HttpCharsets.`UTF-8`)) -> get.channelId
)
)
.via(connectionFlow)
.map {
case (Success(resp), id) => resp.status match {
case StatusCodes.OK => Unmarshal(resp.entity).to[AccessTokenModel]
.map(senderActor ! AccessToken(_.access_token))
case _ => senderActor ! AccessTokenError
}
case _ => senderActor ! AccessTokenError
}
}.runWith(Sink.head)
case _ => log.info("Unknown message")
}
}
So the question is how it's better to test this route, keeping in mind that the actor with the stream also exist under its hood.
Composition
One difficulty with testing your route logic, as currently organized, is that it is hard to isolate functionality. It is impossible to test your Route logic without an Actor, and it is hard to test your Actor querying without a Route.
I think you would be better served with function composition, that way you can isolate what it is you're trying to test.
First abstract away the Actor querying (ask):
sealed trait TokenResponse
case class AccessToken() extends TokenResponse {...}
case object AccessTokenError extends TokenResponse
val queryActorForToken : (ActorRef) => (GetAccessToken) => Future[TokenResponse] =
(ref) => (getAccessToken) => (ref ? getAccessToken).mapTo[TokenResponse]
Now convert your routes value into a higher-order method which takes in the query function as a parameter:
val actorRef : ActorRef = ??? //not shown in question
type TokenQuery = GetAccessToken => Future[TokenResponse]
val actorTokenQuery : TokenQuery = queryActorForToken(actorRef)
val errorMsg = "There was problems while retriving the access token"
def createRoute(getToken : TokenQuery = actorTokenQuery) : Route =
pathPrefix("access-tokens") {
pathPrefix(Segment) { userId =>
parameters('refreshToken) { refreshToken =>
onSuccess(getToken(GetAccessToken(userId, refreshToken))) {
case token: AccessToken => complete(ok(token.toJson))
case AccessTokenError => complete(internalServerError(errorMsg))
}
}
}
}
//original routes
val routes = createRoute()
Testing
Now you can test queryActorForToken without needing a Route and you can test the createRoute method without needing an actor!
You can test createRoute with an injected function that always returns a pre-defined token:
val testToken : AccessToken = ???
val alwaysSuccceedsRoute = createRoute(_ => Success(testToken))
Get("/access-tokens/fooUser?refreshToken=bar" ~> alwaysSucceedsRoute ~> check {
status shouldEqual StatusCodes.Ok
responseAs[String] shouldEqual testToken.toJson
}
Or, you can test createRoute with an injected function that never returns a token:
val alwaysFailsRoute = createRoute(_ => Success(AccessTokenError))
Get("/access-tokens/fooUser?refreshToken=bar" ~> alwaysFailsRoute ~> check {
status shouldEqual StatusCodes.InternalServerError
responseAs[String] shouldEqual errorMsg
}
Simple question:
I've followed the tutorial: here where I have the following code:
this.term.valueChanges
.debounceTime(400)
.distinctUntilChanged()
.switchMap(term => this.wikipediaService.search(term));
Where term is a Control inheriting from AbstractControl, how can I trigger the valueChanges observable property, so that I can perform unit tests?
I think I was experiencing the same issue.
I was trying to test that heroService.searchHeroes was called:
ngOnInit() {
this.heroes$ = this.searchTerms
.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap((term: string) => this.heroService.searchHeroes(term))
);
}
I was able to fix it by only calling tick(500) once
it('should call HeroService.searchHeroes', fakeAsync(() => {
spyOn(heroService, 'searchHeroes').and.returnValue(component.heroes$);
let searchField: DebugElement = fixture.debugElement.query(By.css('#search-box'));
searchField.nativeElement.value = 'i';
searchField.nativeElement.dispatchEvent(new Event('keyup'));
tick(500);
expect(heroService.searchHeroes).toHaveBeenCalled();
}));
I found a work around in the following issue:
https://github.com/angular/angular/issues/8251
Essentially there's buggy behavior, but you can get a basic test working with code like this:
it("filter input changed should update filter string",
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
let fixture = tcb.createFakeAsync(MyComponent);
let component = fixture.componentInstance as MyComponent;
fixture.detectChanges();
// Trigger a new value to emit on the valueChanges observable
//
component.filter.updateValue("testing");
// There's buggy behavior with multiple async events so we
// need to run tick() twice to get our desired behavior
// https://github.com/angular/angular/issues/8251
//
tick();
tick(500);
// Now we can inspect the results of the subscription in the
// original component
//
expect(component.filterString).toBe("testing");
})));
...and this is with a component that looks like this:
export class MyComponent {
filter = new Control();
/** The current filter string */
filterString: string;
constructor() {
// Subscribe to changes in the filter string
//
this.filter.valueChanges
.debounceTime(200)
.distinctUntilChanged()
.subscribe((filterString: string) => {
this.filterString = filterString;
});
}
}
I got this really cool Moq method that fakes out my GetService, looks like this
private Mock<IGetService<TEntity>> FakeGetServiceFactory<TEntity>(List<TEntity> fakeList) where TEntity : class, IPrimaryKey, new()
{
var mockGet = new Mock<IGetService<TEntity>>();
mockGet.Setup(mock => mock.GetAll()).Returns(fakeList);
mockGet.Setup(mock => mock.Get(It.IsAny<int>())).Returns((int i) => fakeList.Find(fake => fake.Id.ToString() == i.ToString()));
mockGet.Setup(mock => mock.Get(It.IsAny<Expression<Func<TEntity, bool>>>())).Returns((Expression<Func<TEntity, bool>> expression) => fakeList.AsQueryable().Where(expression));
return mockGet;
}
and to use this...
var fakeList = new List<Something>();
fakeList.Add(new Something { Whatever = "blah" });
//do this a buncha times
_mockGetService = FakeGetServiceFactory(fakeList);
_fakeGetServiceToInject = _mockGetService.Object;
How do I toss this into Rhino.Mock?
Something along these lines (sorry, I don't have VS handy, so I can't test it):
private IGetService<TEntity> FakeGetServiceFactory<TEntity>(List<TEntity> fakeList) where TEntity : class, IPrimaryKey, new()
{
var mockGet = MockRepository.GenerateMock<IGetService<TEntity>>();
mockGet.Expect(mock => mock.GetAll()).Return(fakeList);
mockGet.Expect(mock => mock.Get(Arg<int>.Is.Anything)).Do((int i) => fakeList.Find(fake => fake.Id.ToString() == i.ToString()));
mockGet.Expect(mock => mock.Get(Arg<Expression<Func<TEntity, bool>>>.Is.Anything)).Do((Expression<Func<TEntity, bool>> expression) => fakeList.AsQueryable().Where(expression));
return mockGet;
}