How to mock a static method in Flutter with Mockito? - unit-testing

I have a file a function fetchPosts() which is in charge of getting new Posts from a server and store them in a local sqlite database.
As recommended on the sqflite doc, I store a single ref to my database.
Here is the content of my database.dart file:
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DBProvider {
DBProvider._();
static final DBProvider db = DBProvider._();
static Database _database;
static Future<Database> get database async {
if (_database != null) return _database;
// if _database is null, we instantiate it
_database = await _initDB();
return _database;
}
static Future<Database> _initDB() async {
final dbPath = await getDatabasesPath();
String path = join(dbPath, 'demo.db');
return await openDatabase(path, version: 1, onCreate: _onCreate);
}
static Future<String> insert(String table, Map<String, dynamic> values) async { /* insert the record*/ }
// Other functions like update, delete etc.
}
Then I use it as such in my fetchPosts.dart file
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../services/database.dart';
const url = 'https://myapp.herokuapp.com';
Future<void> fetchPosts() {
final client = http.Client();
return fetchPostsUsingClient(client);
}
Future<void> fetchPostsUsingClient(http.Client client) async {
final res = await client.get(url);
final posts await Post.fromJson(json.decode(response.body));
for (var i = 0; i < posts.length; i++) {
await DBProvider.insert('posts', posts[i]);
}
}
In my test, how can I verify that DBProvider.insert() has been called?
fetchPosts_test.dart
import 'package:test/test.dart';
import 'package:http/http.dart' as http;
import 'package:mockito/mockito.dart';
import 'package:../services/fetchPosts.dart';
// Create a MockClient using the Mock class provided by the Mockito package.
// Create new instances of this class in each test.
class MockClient extends Mock implements http.Client {}
void main() {
group('fetchPosts', () {
test('update local db', () async {
final client = MockClient();
// Use Mockito to return a successful response when it calls the provided http.Client.
when(client.get()).thenAnswer((_) async => http.Response('{"title": "Test"}', 200));
await fetchPostsWithClient(client);
verify(/* DBProvider.insert has been called ?*/);
});
});
}

The question was some while ago, but here is another solution. You can refactor calls to that static function to be called from a class "wrapper" method. This is a pattern I often use to mock requests to third party services.
Let me give you an example. To make it simple lets say Engine has 3 static methods that need to be mocked: brake() and accelerate() and speed().
class Car {
int currentSpeed;
void accelerateTo(int speed) {
while(currentSpeed > speed) {
Engine.brake();
currentSpeed = Engine.speed();
}
while(currentSpeed < speed) {
Engine.accelerate();
currentSpeed = Engine.speed();
}
}
}
Now you want to mock all calls to the engine, to do so we could refactor the code to:
class Car {
int currentSpeed;
void accelerateTo(int speed) {
while(currentSpeed > speed) {
brake();
currentSpeed = speed();
}
while(currentSpeed < speed) {
accelerate();
currentSpeed = speed();
}
}
/// wrapper to mock Engine calls during test
void brake() {
Engine.brake();
}
/// wrapper to mock Engine calls during test
int speed() {
Engine.speed();
}
/// wrapper to mock Engine calls during test
void accelerate() {
Engine.accelerate();
}
}
In the integration test you can now mock the 3 methods that interact with the static methods directly but you can now test your main method. While you could here also refactor the Engine class itself, often that class would be within a third party service.
This example is not based on the Volkswagen scandal ;).

Eventually, I had to rewrite my database.dart to make it testable / mockable.
Here's the new file:
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DBProvider {
static final DBProvider _singleton = DBProvider._internal();
factory DBProvider() {
return _singleton;
}
DBProvider._internal();
static Database _db;
static Future<Database> _getDatabase() async {
if (_db != null) return _db;
// if _database is null, we instantiate it
_db = await _initDB();
return _db;
}
static Future<Database> _initDB() async {
final dbPath = await getDatabasesPath();
String path = join(dbPath, 'demo.db');
return openDatabase(path, version: 1, onCreate: _onCreate);
}
Future<String> insert(String table, Map<String, dynamic> values) async {
final db = await _getDatabase();
return db.insert(table, values);
}
// ...
}
Now I can use the same trick as with the http.Client.
Thank you #RĂ©miRousselet

Let's say we want to test [TargetClass.someMethodCallOtherStaticMethod]
Class StaticMethodClass {
static int someStaticMethod() {};
}
Class TargetClass {
int someMethodCallOtherStaticMethod() {
return StaticMethodClass.someStaticMethod();
}
}
We should refactor [[TargetClass.someMethodCallOtherStaticMethod]] for testing,
like this:
Class TargetClass {
int someMethodCallOtherStaticMethod({#visibleForTesting dynamic staticMethodClassForTesting}) {
if (staticMethodClassForTesting != null) {
return staticMethodClassForTesting.someStaticMethod();
} else {
return StaticMethodClass.someStaticMethod();
}
}
}
Now you can write your test case like this:
// MockClass need to implement nothing, just extends Mock
MockClass extends Mock {}
test('someMethodCallOtherStaticMethod', () {
// We MUST define `mocked` as a dynamic type, so that no errors will be reported during compilation
dynamic mocked = MockClass();
TargetClass target = TargetClass();
when(mocked.someStaticMethod()).thenAnswer((realInvocation) => 42);
expect(target.someMethodCallOtherStaticMethod(staticMethodClassForTesting: mocked), 42);
})

Related

Nestjs unit test: TypeError: this.userModel.findById(...).exec is not a function

using nestjs framework and with a repository class that uses mongoose to do the CRUD operations we have a simple users.repository.ts file like this:
#Injectable()
export class UserRepository {
constructor(#InjectModel(User.name) private userModel: Model<UserDocument>) {}
async create(createUserInput: CreateUserInput) {
const createdUser = new this.userModel(createUserInput);
return await createdUser.save();
}
}
async findById(_id: MongooseSchema.Types.ObjectId) {
return await this.userModel.findById(_id).exec();
}
and it works normally when the server is up.
consider this users.repository.spec file :
import { Test, TestingModule } from '#nestjs/testing';
import { getModelToken } from '#nestjs/mongoose';
import { Model } from 'mongoose';
// User is my class and UserDocument is my typescript type
// ie. export type UserDocument = User & Document; <-- Mongoose Type
import { User, UserDocument } from '../domain/user.model';
import { UserRepository } from './users.repository';
//import graphqlScalars from 'graphql-scalar-types';
describe('UsersRepository', () => {
let mockUserModel: Model<UserDocument>;
let mockRepository: UserRepository;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: getModelToken(User.name),
useValue: Model, // <-- Use the Model Class from Mongoose
},
UserRepository,
//graphqlScalars,
],
}).compile();
// Make sure to use the correct Document Type for the 'module.get' func
mockUserModel = module.get<Model<UserDocument>>(getModelToken(User.name));
mockRepository = module.get<UserRepository>(UserRepository);
});
it('should be defined', () => {
expect(mockRepository).toBeDefined();
});
it('should return a user doc', async () => {
// arrange
const user = new User();
const userId = user._id;
const spy = jest
.spyOn(mockUserModel, 'findById') // <- spy
.mockResolvedValue(user as UserDocument); // <- set resolved value
// act
await mockRepository.findById(userId);
// assert
expect(spy).toBeCalled();
});
});
so my question:
for the should return a user doc test i get TypeError: metatype is not a constructor when and i guess
.mockResolvedValue(user as UserDocument);
should be fixed.
Note:graphql is used the query to the API and i have no idea that if the scalars should be provieded or not, if i uncomment the scalar, the expect(mockRepository).toBeDefined(); test would not pass any more
so any idea to fix the test would be apreciated.
to handle a chained .exec we should define it via mockReturnThis():
static findById = jest.fn().mockReturnThis();
I needed the constructor to be called via new so i preferd to define a mock class in this way:
class UserModelMock {
constructor(private data) {}
new = jest.fn().mockResolvedValue(this.data);
save = jest.fn().mockResolvedValue(this.data);
static find = jest.fn().mockResolvedValue(mockUser());
static create = jest.fn().mockResolvedValue(mockUser());
static remove = jest.fn().mockResolvedValueOnce(true);
static exists = jest.fn().mockResolvedValue(false);
static findOne = jest.fn().mockResolvedValue(mockUser());
static findByIdAndUpdate = jest.fn().mockResolvedValue(mockUser());
static findByIdAndDelete = jest.fn().mockReturnThis();
static exec = jest.fn();
static deleteOne = jest.fn().mockResolvedValue(true);
static findById = jest.fn().mockReturnThis();
}

How can I unit test whether the ChangeNotifier's notifyListeners was called in Flutter/Dart?

I'm using the provider package in our app and I want to test my ChangeNotifier class individually to have simple unit tests checking the business logic.
Apart from the values of ChangeNotifier properties, I also want to ensure that in certain cases (where necessary), the notifyListeners has been called, as otherwise, the widgets that rely on up-to-date information from this class would not be updated.
Currently, I'm indirectly testing whether the notifyListeners have been called: I'm using the fact that the ChangeNotifier lets me add a callback using its addListener method. In the callback that I add in our testing suite, I simply increment an integer counter variable and make assertions on that.
Is this the right way to test whether my ChangeNotifier calls its listeners? Is there a more descriptive way of testing this?
Here is the class I'm testing:
import 'package:flutter/foundation.dart';
class ExampleModel extends ChangeNotifier {
int _value = 0;
int get value => _value;
void increment() {
_value++;
notifyListeners();
}
}
and this is how I test it:
import 'package:mobile_app/example_model.dart';
import 'package:test/test.dart';
void main() {
group('$ExampleModel', () {
ExampleModel exampleModel;
int listenerCallCount;
setUp(() {
listenerCallCount = 0;
exampleModel = ExampleModel()
..addListener(() {
listenerCallCount += 1;
});
});
test('increments value and calls listeners', () {
exampleModel.increment();
expect(exampleModel.value, 1);
exampleModel.increment();
expect(listenerCallCount, 2);
});
test('unit tests are independent from each other', () {
exampleModel.increment();
expect(exampleModel.value, 1);
exampleModel.increment();
expect(listenerCallCount, 2);
});
});
}
Your approach seems fine to me but if you want to have a more descriptive way you could also use Mockito to register a mock callback function and test whether and how often the notifier is firing and thus notifying your registered mock instead of incrementing a counter:
import 'package:mobile_app/example_model.dart';
import 'package:test/test.dart';
/// Mocks a callback function on which you can use verify
class MockCallbackFunction extends Mock {
call();
}
void main() {
group('$ExampleModel', () {
late ExampleModel exampleModel;
final notifyListenerCallback = MockCallbackFunction(); // Your callback function mock
setUp(() {
exampleModel = ExampleModel()
..addListener(notifyListenerCallback);
reset(notifyListenerCallback); // resets your mock before each test
});
test('increments value and calls listeners', () {
exampleModel.increment();
expect(exampleModel.value, 1);
exampleModel.increment();
verify(notifyListenerCallback()).called(2); // verify listener were notified twice
});
test('unit tests are independent from each other', () {
exampleModel.increment();
expect(exampleModel.value, 1);
exampleModel.increment();
expect(notifyListenerCallback()).called(2); // verify listener were notified twice. This only works, if you have reset your mocks
});
});
}
Just keep in mind that if you trigger the same mock callback function in multiple tests you have to reset your mock callback function in the setup to reset its counter.
I've ran into the same Issue. It's difficult to test wether notifyListeners was called or not especially for async functions. So I took your Idea with the listenerCallCount and put it to one function you can use.
At first you need a ChangeNotifier:
class Foo extends ChangeNotifier{
int _i = 0;
int get i => _i;
Future<bool> increment2() async{
_i++;
notifyListeners();
_i++;
notifyListeners();
return true;
}
}
Then the function:
Future<R> expectNotifyListenerCalls<T extends ChangeNotifier, R>(
T notifier,
Future<R> Function() testFunction,
Function(T) testValue,
List<dynamic> matcherList) async {
int i = 0;
notifier.addListener(() {
expect(testValue(notifier), matcherList[i]);
i++;
});
final R result = await testFunction();
expect(i, matcherList.length);
return result;
}
Arguments:
The ChangeNotifier you want to test.
The function which should fire notifyListeners (just the reference to the function).
A function to the state you want to test after each notifyListeners.
A List of the expected values of the state you want to test after each notifyListeners (the order is important and the length has to equal the notifyListeners calls).
And this is how to test the ChangeNotifier:
test('should call notifyListeners', () async {
Foo foo = Foo();
expect(foo.i, 0);
bool result = await expectNotifyListenerCalls(
foo,
foo.increment2,
(Foo foo) => foo.i,
<dynamic>[isA<int>(), 2]);
expect(result, true);
});
I have wrap it to the function
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
dynamic checkNotifierCalled(
ChangeNotifier notifier,
Function() action, [
Matcher? matcher,
]) {
var isFired = false;
void setter() {
isFired = true;
notifier.removeListener(setter);
}
notifier.addListener(setter);
final result = action();
// if asynchronous
if (result is Future) {
return result.then((value) {
if (matcher != null) {
expect(value, matcher);
}
return isFired;
});
} else {
if (matcher != null) {
expect(result, matcher);
}
return isFired;
}
}
and call it by:
final isCalled = checkNotifierCalled(counter, () => counter.increment(), equals(2));
expect(isCalled, isTrue);

Mono.doOnError() reactor block unit test

I have a rest controller using spring webflux and reactor, I am writing unit test for the controller. Please find below the code snippets and help me to write the unit test method to test the .doOnError() block.
I have tried to throw an exception by using Mockito
doThrow(CriticalException.class)
.when(myService).myMethod(object);
This is my unit test:
StepVerifier.create(
Mono.just(
webTestClient.post()
.uri("/endpoint")
.accept(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromObject(requestJson)) //Set the body of the request to the given synchronous Object
//Returns:a Mono with the response
//Act
.exchange() //Perform the exchange
//Assert
.expectStatus().isOk()
.expectBody(Agreement.class)
.returnResult()
.getResponseBody()))
.expectNextMatches(agreementResponse -> {
assertNotNull(agreementResponse.getAgreementParticipant());
return true;
})
.expectComplete()
.verify();
This is my controller:
return Mono.fromCallable(() -> {
myService.myMethod(object);
return object;
}).log().subscribeOn(Schedulers.elastic())
.map(p -> ResponseEntity.ok(p))
.defaultIfEmpty(ResponseEntity.notFound().build())
.doOnError(e -> {
LOGGER.error(LOG_FORMAT, e.getMessage(), e.getStackTrace());
});
Mockito is not returning exception while myService.myMethod(object) is been called.
Please suggest proper way to write test for .defaultIfEmpty() and .doOnError() blocks.
Instead of throwing CriticalException.class while mocking your myService.myMethod(object) return an exception wrapped in a Mono
For eg :
Mockito.doReturn(Mono.error(Exception::new)).when(service).callableMethod();
Find the sample code snippet below
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
class Service {
public Mono<String> callableMethod() {
return Mono.just("1");
}
}
class Controller {
private Service service;
public Controller(Service service) {
this.service = service;
}
public Mono<String> endpoint() {
return service.callableMethod().doOnError(throwable -> {
System.out.println("throwable = " + throwable);
});
}
}
public class TestClass {
#Mock
private Service service = Mockito.mock(Service.class);
#Test
public void controllerTest() {
Mockito.doReturn(Mono.error(Exception::new)).when(service).callableMethod();
StepVerifier.create(new Controller(service).endpoint()).verifyError();
}
}

Huh? Where do I put my service so that my Controller and Service aren't impossible to test?

Am I correct to think that I have to create my controller by passing it an instance of my context AND my Service to make it testable?
For example: new Controller(mycontext,myservice)
I'm thinking this is the way I need to change my code, but I don't want to if I don't have to. Since for MVC3 to work out of the box it requires controller constructors to be parameterless, I think this means I'm going to have to go down the path of IoC. Otherwise the code in my Wizard action saves to a real DBContext even during testing.
namespace mvc3test.Controllers
{
public class WizardController : Controller
{
private DR405DBContext db;
public WizardController(DR405DBContext dbContext)
{
db = dbContext;
}
public WizardController()
{
db = new DR405DBContext();
}
public ActionResult Index()
{
var model = new WizardViewModel();
model.Initialize();
return View(model);
}
[HttpPost]
public ActionResult Index([Deserialize] WizardViewModel wizard)
{
//wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
//Always save.
var obj = new dr405();
//wire up to domain model;
foreach (var s in wizard.Steps)
{
Mapper.Map(s,obj,s.GetType(), typeof(dr405));
}
using (var service = new DR405Service())
{
//Do something with a service here.
service.Save(db, obj);
}
if (!string.IsNullOrEmpty(Request.QueryString["next"]))
{
wizard.CurrentStepIndex++;
}
else if (!string.IsNullOrEmpty(Request.QueryString["prev"]))
{
wizard.CurrentStepIndex--;
}
else
{
return View("Review", wizard);
}
}
else if (!string.IsNullOrEmpty(Request.QueryString["prev"]))
{
wizard.CurrentStepIndex--;
}
return View(wizard);
}
public ActionResult Review(int id)
{
var service = new DR405Service();
var dr405 = service.GetDR405ById(db, id);
var wizard = new WizardViewModel();
if (dr405 != null)
{
wizard.Initialize();
foreach (var s in wizard.Steps)
{
Mapper.Map(dr405, s, typeof(dr405), s.GetType());
}
}
return View(wizard);
}
public ActionResult Transmit()
{
return View();
}
[HttpPost]
public String Upload(HttpPostedFileBase FileData)
{
var saveLocation = Path.Combine(Server.MapPath("\\"), "returns\\" + DR405Profile.CurrentUser.TaxPayerID);
System.IO.Directory.CreateDirectory(saveLocation);
FileData.SaveAs(Path.Combine(saveLocation, FileData.FileName));
ViewBag.Message = String.Format("File name: {0}, {1}Kb Uploaded Successfully.", FileData.FileName, (int)FileData.ContentLength / 1024);
return ViewBag.Message;
}
}
}
Am I correct to think that I have to create my controller by passing it an instance of my context AND my Service to make it testable?
Kind of. That's only half of the work you need to do. The second half is to weaken the coupling between your layers by using abstractions. Your service layer needs to implement an interface which you would inject into the constructor of your controller enforcing the contract between those two and explicitly stating that the controller needs a service layer obeying this contract:
public WizardController(IMyService service)
{
this._service = service;
}
Now in your unit test go ahead and mock it using one of the multiple mocking frameworks out there (Rhino Mocks, NSubstitute, Moq, NMock, ...).
You can use setter injection instead of constructor injection on the Controller.
public class WizardController : Controller
{
public void setDBContext( DR405DBContext db) {
this.db = db;
}
}
or
You can get the database using a Service Locator and add a setter to that.
public class DBServiceLocator
{
private static DR405DBContext db = new DR405DBContext();
public static DR405DBContext locate() {
return db;
}
public static setContext(DR405DBContext db) {
DBServiceLocator.db = db;
}
}
In the setup() part of your unit test, use setters to 'inject' your mock/stub database.
Also, using an interface rather than DR405DBContext will make mocking easier.

Unit Test a CSLA Asynchronous Validation Rule

I have a validation rule on a CSLA Business Base stereotyped class. I'm having trouble figuring out how to unit test the validation rule as it includes an asynchronous callback lambda expression. Here's some example code:
using System;
using System.Collections.Generic;
using Csla;
using Csla.Validation;
namespace UnitTestCSLAAsyncValidationRule
{
public class BusinessObject : BusinessBase<BusinessObject>
{
protected static PropertyInfo<string> CodeProperty = RegisterProperty<string>(p => p.Code);
public string Code
{
get { return GetProperty(CodeProperty); }
set { SetProperty(CodeProperty, value); }
}
protected override void AddBusinessRules()
{
ValidationRules.AddRule(CodeValidator, new AsyncRuleArgs(CodeProperty));
}
public static void CodeValidator(AsyncValidationRuleContext context)
{
var code = (string) context.PropertyValues["Code"];
CodeList codeList;
CodeList.GetCodeList((o, l) =>
{
codeList = l.Object;
if (codeList.Contains(code))
{
context.OutArgs.Result = false;
context.OutArgs.Description = "Code already in use.";
}
else
{
context.OutArgs.Result = true;
}
});
context.Complete();
}
}
public class CodeList : List<string>
{
public static void GetCodeList(EventHandler<DataPortalResult<CodeList>> handler)
{
DataPortal<CodeList> dp = new DataPortal<CodeList>();
dp.FetchCompleted += handler;
dp.BeginFetch();
}
private void DataPortal_Fetch()
{
// some existing codes..
Add("123");
Add("456");
}
}
}
I would like to test this with a test similar to the following:
using NUnit.Framework;
namespace UnitTestCSLAAsyncValidationRule.Test
{
[TestFixture]
public class BusinessObjectTest
{
[Test]
public void CodeValidationTest()
{
var bo = new BusinessObject();
bo.Code = "123";
Assert.IsNotEmpty(bo.BrokenRulesCollection);
}
}
}
However, the test Assert runs before the async callback. Is this something UnitDriven could help with? I've had a look at it but can't see how to use it in this scenario.
Thanks,
Tom
Answered by JonnyBee on http://forums.lhotka.net/forums/p/10023/47030.aspx#47030:
using NUnit.Framework;
using UnitDriven;
namespace UnitTestCSLAAsyncValidationRule.Test
{
[TestFixture]
public class BusinessObjectTest : TestBase
{
[Test]
public void CodeValidationTest()
{
UnitTestContext context = GetContext();
var bo = new BusinessObject();
bo.ValidationComplete += (o, e) =>
{
context.Assert.IsFalse(bo.IsValid);
context.Assert.Success();
//Assert.IsNotEmpty(bo.BrokenRulesCollection);
};
bo.Code = "123";
context.Complete();
}
}
}
Please not there was a small bug in my validation rule method - the call to AsyncValidationRuleContext.Complete() needs to be inside the lambda.
Thanks,
Tom