I need to shim a static generic method for a unit test. However, I can't seem to get Fakes to create the shim object. Does anyone know how to accomplish this?
In particular, I want to shim Newtonsoft's JsonConvert.DeserializeObject<>()
For each return type that you would expect register a delegate like so:
With this code in the Unit Test:
using (var context = ShimsContext.Create())
{
ShimJsonConvert.DeserializeObjectOf1String<SomeJSonObject>(s => new SomeJSonObject() { Name = "Foo" });
SomeJSonObject o = ConsoleApplication3.Program.Deserialize();
Assert.IsNotNull(o);
Assert.AreSame(o.Name, "Foo");
}
And this code under test:
return JsonConvert.DeserializeObject<SomeJSonObject>("");
It works as expected for me.
If needed also register the other overloads. So if you're using some of the other overloads, you must also register their corresponding delegates on the Shim:
Like:
ShimJsonConvert.DeserializeObjectOf1String<SomeJSonObject>(s => new SomeJSonObject() { Name = "Foo" });
ShimJsonConvert.DeserializeObjectOf1StringJsonConverterArray((s, convertors) => new SomeJSonObject() {Name = "Bar"});
ShimJsonConvert.DeserializeObjectOf1StringJsonSerializerSettings((s, settings) => new SomeJSonObject() { Name = "Bar" });
Related
For mocking uuidv4, I am using this :
import { v4 as uuidv4 } from "uuid";
jest.mock("uuid");
uuidv4.mockReturnValue("uuid123");
And for mocking window.confirm, I am using this:
window.confirm = jest.fn().mockImplementation(() => true);
These both are working all right.
But when i try doing this i.e
const uuidv4 = jest.fn.mockImplementation(() => "uuid123");
I get this error
TypeError: jest.fn.mockImplementation is not a function
I am confused between jest.fn() and jest.mock().
Can someone please elaborate on which one to use and when to use, with suitable example?
Just a quick explanation for you:
jest.mock is to mock a certain module. Once you write jest.mock('uuid') then it means all exported things would be turned to a jest.Mock type, that's why you can mock v4 method: v4.mockReturnValue('yourV4Id');
jest.mock('aModule');
import {aMember} from "aModule";
// is now a jest mock type <=> jest.fn()
aMember.mockReturnValue('a value');
jest.fn is a function which returns a jest.Mock type which can be considered as an function to create whatever you want:
const aMock = jest.fn().mockReturnValue(1) // <=> const aMock = () => 1;
// The difference is jest mock type can be used to assert then. Most of cases is to check
// whether it gets called or not
Have I written an untestable method? As the library I am using has an important method which is implemented as an extension method, it seems like I am unable to fake it. And thus, unable to test my method.
First, I'll set out a truncated version of the method I want to test.
Then, I'll set out the attempt I have made to fake it using FakeItEasy.
The method uses caching and it is the call to the static method in the caching library LazyCache which I am struggling to fake:
public async Task<BassRuleEditModel> GetBassRuleEditModel(
int facilityId,
int criteriaId,
int bassRuleId,
BassRuleEditDto bassRuleEditDto)
{
var url = _bassRuleService.GetServiceConnectionForFacility(facilityId).Url;
var dto = bassRuleEditDto ?? _bassRuleService.GetBassRuleEditDto(bassRuleId);
var bassRuleEditModel = new BassRuleEditModel
{
...
LocationList = await GetLocations(url),
...
};
...
return bassRuleEditModel;
}
private async Task<IEnumerable<SelectListItem>> GetLocations(string url)
{
var cacheKey = string.Concat(CacheKeys.Location, url);
var selectList = await _appCache.GetOrAddAsync(cacheKey, async () =>
{
return new SelectList(await _tasksAndPrioritiesService.ReturnLocationsAsync(url), NameProperty, NameProperty);
}
, CacheKeys.DefaultCacheLifetime);
return selectList;
}
It is the GetOrAddAsync method which is an extension method.
I just want the fake to return from the cache an empty SelectList.
Note, the AppCache and all dependencies are injected using constructor injection.
The unit test which I have written, where I have tried to fake the AppCache is:
[Fact]
public async Task Un_Named_Test_Does_Stuff()
{
var url = "http://somesite.com";
var referrer = new Uri(url);
var facilityId = GetRandom.Id();
var serviceConnectionDto = new ServiceConnectionDto
{
Url = "http://google.com" // this url does not matter
};
var cacheKey = string.Concat(CacheKeys.Location, serviceConnectionDto.Url);
A.CallTo(() => _bassRuleService.GetServiceConnectionForFacility(facilityId)).Returns(serviceConnectionDto);
A.CallTo(() => _urlHelper.Content("~/ServiceSpec/ListView")).Returns(url);
A.CallTo(() => _appViewService.GetReferrer(url)).Returns(referrer);
A.CallTo(() => _appCache.GetOrAddAsync(cacheKey, A<Func<Task<SelectList>>>.Ignored))
.Returns(Task.FromResult(new SelectList(Enumerable.Empty<SelectListItem>().ToList())));
var editModel = await
_bassRuleService.GetBassRuleEditModel(GetRandom.Int32(),
GetRandom.Int32(),
GetRandom.Int32(),
null
);
var path = editModel.Referrer.AbsolutePath;
editModel.Referrer.AbsolutePath.ShouldBe(referrer.AbsolutePath);
}
I create the fakes in the constructor of the test (using xUnit):
public BassRuleQueryServiceTests()
{
_currentUser = A.Fake<ICurrentUser>();
_bassRuleService = A.Fake<IBassRuleService>();
_tasksAndPrioritiesService = A.Fake<ITasksAndPrioritiesService>();
_appViewService = A.Fake<IAppViewService>();
_urlHelper = A.Fake<IUrlHelper>();
_applicationDateTime = A.Fake<IApplicationDateTime>();
_appCache = new MockCacheService();
}
The error from running the test is:
Message:
FakeItEasy.Configuration.FakeConfigurationException :
The current proxy generator can not intercept the method LazyCache.AppCacheExtenions.GetOrAddAsync1[Microsoft.AspNetCore.Mvc.Rendering.SelectList](LazyCache.IAppCache cache, System.String key,
System.Func1[System.Threading.Tasks.Task`1[Microsoft.AspNetCore.Mvc.Rendering.SelectList]] addItemFactory) for the following reason:
- Extension methods can not be intercepted since they're static.>
I get the fact that faking a static method is not on. I'm looking for solutions.
Do I need to pressure library authors to not use extension methods? (Facetious question)
Cheers
As you have correctly noted, extensions are static methods, and static methods can't be faked.
Extension methods are often just wrappers to simplify operations on the type they extend; it appears to be the case here. The GetOrAddAsync extension method you're calling ends up calling the IAppCache.GetOrAddAsync method. So you should fake that method instead.
A.CallTo(() => _appCache.GetOrAddAsync(cacheKey, A<Func<ICacheEntry, Task<SelectList>>>.Ignored))
.Returns(new SelectList(Enumerable.Empty<SelectListItem>().ToList()));
It's not very convenient, because it means you need to know what the extension method does, but there's no way around it (short of creating an abstraction layer around the library, but LazyCache is already an abstraction around Microsoft.Extensions.Caching.Memory...)
(btw, you don't need Task.FromResult; the Returns method has an overload that accepts a T when you configure a method returning a Task<T>)
Also, if you're going to return an empty sequence anyway, you don't need to configure the method at all. The default behavior of FakeItEasy will be to return a dummy IEnumerable<SelectListItem> which is empty.
As an alternaive to the excellent answer by #Thomas Levesque, two other alternatives would be:
not to mock the cache at all - use the a real CachingService as it runs in memory and so would be perfectly reasonable to include in the tests.
Use the mock instance MockCachingService cache that ships with LazyCache for this purpose.
See https://github.com/alastairtree/LazyCache/wiki/Unit-testing-code-using-LazyCache for an example.
I want to spyOn a promise and fake that promise in my unit test but the problem is that if I run first the contructor that the problem that he first run the promise and then run the Spyon.
But when i first run the spyOn and then the constructor it gives a error that storage is undefined.
Does someone know how to fix this?
Spec file:
describe('Settings Service', () => {
beforeEach(() => {
settingsService = new SettingsService(); // This gives a error beceause it runs the promise
spyOn(settingsService.storage, 'get').and.callFake((key: String): Promise<string> => {
return new Promise((resolve, reject) => { resolve('url'); });
});
});
constructor:
constructor() {
this.storage = new Storage(LocalStorage);
this.storage.get('url').then(data => {
this.setLink(data);
});
}
UPDATE:
I tried also this:
let injector: any = ReflectiveInjector.resolveAndCreate([SettingsService]);
settingsService = injector.get(SettingsService);
spyOn(settingsService.storage, 'get').and.callFake((key: String): Promise<string> => {
return new Promise((resolve, reject) => { resolve('https://secure.info/pascal'); });
});
The problem you have is that you are instantiating Storage within the constructor, so you have no access to it from the outside. That means that you cannot mock it.
Setting spyOn before calling settingsService = new SettingsService(); doesn't work either because the field storage has not been created yet.
You have two ways to solve this:
Mocking the service $httpBackend using the following code. Take a look at this post as an example
beforeEach(inject(function($injector) {
service = $injector.get('carService');
$httpBackend = $injector.get('$httpBackend');
$httpBackend.when('GET', "/api/cars/types").respond(["Toyota", "Honda", "Tesla"]);
}));
This way you can mock the promise you get when calling this.storage.get('url') and test the behaviour.
Making Storage a service and injecting it mocked: If you use this approach you could moke Storage and therefore mock the behaviour of this.storage.get('url'). The code of your class `` would look like this
static $inject = ['Storage'];
constructor(storage: Storage) {
this.storage = storage;
this.storage.get('url').then(data => {
this.setLink(data);
});
}
But this way depends on how do you define and use Storage so generally the first way will be better
I'm trying to get my controller in a unit test to return a mock file when Request.Files[0] is called
From other posts on this site I've put together:
[TestMethod]
public void CreateFileInDatabase()
{
var repository = new MocRepository();
var controller = GetController(repository);
HttpContextBase mockHttpContext = MockRepository.GenerateMock<HttpContextBase>();
HttpRequestBase mockRequest = MockRepository.GenerateMock<HttpRequestBase>();
mockHttpContext.Stub(x => x.Request).Return(mockRequest);
mockRequest.Stub(x => x.HttpMethod).Return("GET");
var filesMock = MockRepository.GenerateMock<HttpFileCollectionBase>();
var fileMock = MockRepository.GenerateMock<HttpPostedFileBase>();
filesMock.Stub(x => x.Count).Return(1);
mockRequest.Stub(x => x.Files).Return(filesMock);
var t = mockHttpContext.Request;
var automobile = new Automobile{ automobileNumber = "1234" };
controller.ControllerContext = new ControllerContext(mockHttpContext, new RouteData(), controller);
controller.Create(automobile);
}
When I'm in the controller during a test and call Request.Files I get the filesMock great.
However I want to be able to call Request.Files[0] and get a mock File which I can pass as a parameter to a method.
I haven't done much mocking before so any help would be appreciated!
Your filesMock object is a mock, therefore it have no idea how to resolve Files[0]. You need to tell it what to return, if someone asks for the first file:
filesMock.Stub(x => x[0]).Return(fileMock);
Add the above line after the creation of the fileMock object and the code works :)
The easiest way is to add an abstraction over the Request.Files[0] via an interface and object that actually calls out to Request.Files[0]
Note: I'm not actually sure what the datatype is for Request.Files[0], just using IFile as an example.
public interface IFileRetriever
{
IFile GetFile();
}
public class FileRetriever
{
public IFile GetFile()
{
return Request.Files[0];
}
}
Obviously suit the interface and real implementation to your use case, it will probably not be what is above...
In your class that currently calls out to Request.Files[0] just take in the IFileRetriever as a dependency, which is straightforward to mock out in Rhino Mocks (or any mocking/faking framework)
I've isolated the behaviour into the following test case. I'd be grateful to anyone who can tell me how to expect/verify a property set for a List<T> property - it appears there's something going on inside It.Is<T>(predicate) that isn't making a whole lot of sense to me right now. Sample code will run as a console app from VS2008 - you'll need to add a reference to Moq 2.6 (I'm on 2.6.1014.1) - please try uncommenting the different ExpectSet statements to see what's happening...
using System;
using Moq;
using System.Collections.Generic;
namespace MoqDemo {
public interface IView {
List<string> Names { get; set; }
}
public class Controller {
private IView view;
public Controller(IView view) {
this.view = view;
}
public void PopulateView() {
List<string> names = new List<string>() { "Hugh", "Pugh", "Barney McGrew" };
view.Names = names;
}
public class MyApp {
public static void Main() {
Mock<IView> mockView = new Mock<IView>();
// This works - and the expectation is verifiable.
mockView.ExpectSet(mv => mv.Names);
// None of the following can be verified.
// mockView.ExpectSet(mv => mv.Names, It.Is<Object>(o => o != null));
// mockView.ExpectSet(mv => mv.Names, It.Is<List<string>>(names => names.Count == 3));
// mockView.ExpectSet(mv => mv.Names, It.IsAny<IList<String>>());
Controller controller = new Controller(mockView.Object);
controller.PopulateView();
try {
mockView.VerifyAll();
Console.WriteLine("Verified OK!");
} catch (MockException ex) {
Console.WriteLine("Verification failed!");
Console.WriteLine(ex.Message);
}
Console.ReadKey(false);
}
}
}
}
I'm not using the very latest version of Moq, so I don't have an overload of ExpectSet that takes two parameters, but I've had some success with this pattern:
mockView.ExpectSet(mv => mv.Names).Callback(n => Assert.That(n != null));
The Assert (from NUnit) call in the callback will throw an exception if the value assigned to .Names doesn't match the predicate. It does make it hard to trace when a test fails, though. I agree that the ability to pass an It.Is or It.IsAny as the second parameter would be handy.
The second parameter of ExpectSet() is the value you're expecting. You can't use It.Is<T> in this case as there's no overload that takes a predicate - though it would be nice ;) Here's a (simplified) excerpt from your sample, illustrating the use of a value:
var mockView = new Mock<IView>();
var list = new List<string> { "Hugh", "Pugh", "Barney McGrew" };
mockView.ExpectSet(mv => mv.Names, list);
mockView.Object.Names = list;
Hope that helps.
Edit: fixed typo.
BTW, It.Is is not supported on ExpectSet. Your code compiles just because they are regular method invocations when used as values (as opposed to expressions), whereas when used in an Expect expression they are pre-processed by Moq and given specific meaning (rather than the null/default value that all It.Is members actually return).
You could use the stub behavior on the given property (mockView.Stub(mv => mv.Names)) and later assert directly for its value after execution.
Moq doesn't provide an overload receiving It.IsAny as it's effectively the same as calling ExpectSet without passing an expected value ;)