Testing code that uses Core Data - preventing inconsistent state - core-data

My app uses Core Data for persistence and I have been writing unit tests to check that view models that touch the persistence layer of the app behave as expected.
I am using an in-memory persistent store for test as recommended and the state of the store varies depending on the test I am performing. For example, in some instances I start with an empty store; in others I want to use a pre-populated store and then check the behaviour of the code that observes the store's state as it is mutated.
Although the "live" code is behaving as expected, I've had issues with the store not being in the expected state before the unit test runs and this would appear to be due to async optimisations of the stack. Changing the test order will influence which tests pass or fail and this certainly supports my suspicion that concurrency is causing the problem.
I can eliminate the errors by saving the view context after CRUD operations and wrapping this in a test expectation that waits for the NSManagedObjectContextDidSave notification thereby preventing concurrent operations. However, this makes me feel uncomfortable as it's not the way the code operates in the production app - saving the view context is expensive and so only happens when the app is terminated / pushed to background. As the tests do not accurately reflect what's happening in the live app, there's a chance that bugs could be missed despite apparent coverage.
I've been unable to find any solutions or recommendations about managing this problem in unit tests beyond using expectations. Is there a better solution?
Example tests - removing the 'executeAndWaitForSave' call in the second test method will cause the first test to fail - the MOC will contain more objects than expected:
class ParameterGridViewModelTests: XCTestCase {
var dependencies: AppDependencies?
var viewContext: NSManagedObjectContext?
var patient: Patient?
var parameter: Parameter?
override func setUp() {
dependencies = AppDependencies.emptyPersistentStore
viewContext = dependencies!.persistenceService.viewContext
patient = Patient.instanceForTest(using: dependencies!.persistenceService.viewContext)
parameter = Parameter.instanceForTest(using: dependencies!.persistenceService.viewContext)
}
override func tearDown() {
dependencies = nil
viewContext = nil
patient = nil
parameter = nil
}
func testEnvironmentPropertiesMatchExpectations() {
let persistenceService = dependencies!.persistenceService
XCTAssertEqual(persistenceService.count(for: Observation.fetchRequest()), 0, "There should be no Observation instances in store")
XCTAssertEqual(persistenceService.count(for: Parameter.fetchRequest()), 1, "There shouldn't be ONE Parameter instance in store")
}
func testObservationsPropertyIsUpdatedWhenAnObservationIsMade() {
// given
let sut = ParameterGridView.ViewModel(patient: patient!, dependencies: dependencies!)
let sutMirror = ParameterGridViewModelMirror(viewModel: sut)
// when
let numberOfObservations = 5
for _ in 1...numberOfObservations {
_ = Observation.create(
in: viewContext!,
patient: patient!,
parameter: parameter!,
numericValue: Double.random(in: 5...50)
)
}
// then
XCTAssertEqual(sutMirror.observations!.count, numberOfObservations, "Observations property of view model does not contain correct number of elements")
}
func testHistoricalObservationsEmptyWhenObservationTimeExceedsFourHoursInPast() {
// given
let sut = ParameterGridView.ViewModel(patient: patient!, dependencies: dependencies!)
// when
executeAndWaitForSave(managedObjectContext: viewContext!) {
let firstObservationTime = Date.now
.offset(.hour, direction: .past, value: 4)!
.offset(.minute, direction: .past, value: 10)!
let secondObservationTime = Date.now
.offset(.hour, direction: .past, value: 6)!
let observationOne = Observation(context: viewContext!)
observationOne.timestamp = firstObservationTime
observationOne.cd_Parameter = parameter!
observationOne.cd_Patient = patient!
observationOne.numericValue = 10
let observationTwo = Observation(context: viewContext!)
observationTwo.timestamp = secondObservationTime
observationTwo.cd_Parameter = parameter!
observationTwo.cd_Patient = patient!
observationTwo.numericValue = 100
}
// then
XCTAssertTrue(sut.lastObservation.isEmpty, "lastObservations should be empty")
XCTAssertTrue(sut.recentObservation.isEmpty, "recentObservation should be empty")
}
}
Helper function used to ensure synchronous mutation of Core Data stack:
extension XCTestCase {
/// Helper function to which attempts to mitigate test failures relating
/// to asynchronous Core Data operations leaving the MOC in an unpredictale
/// state.
/// Although saving the MOC is expensive in the production app and should only
/// occur when absolutely necessary, the associated notification can be used to
/// ensure the MOC in in the expected state before test execution proceeds
/// - Parameters:
/// - managedObjectContext: MOC to use for the operation
/// - block: code to be executed (typically creation, mutation or deletion of a MOM linked to the MOC)
func executeAndWaitForSave(managedObjectContext: NSManagedObjectContext, block: () -> Void) {
expectation(forNotification: .NSManagedObjectContextDidSave, object: managedObjectContext) { _ in
return true
}
block()
try! managedObjectContext.save()
waitForExpectations(timeout: 2.0) { error in
XCTAssertNil(error, "Save did not occur: \(error!.localizedDescription)")
}
}
}

Related

On Hyperledger Fabric Context

I would appreciate some help with this small issue.
According to the comercial paper smart contract, present in Fabric-samples, one can define a custom Context, which allows handling logic across different transactions.
Can we define a variable in a Context, initialized as 0, and increment it at each transaction? I do not seem able to increment it, i.e., the counter always resets at each transation:
class CustomContext extends Context {
constructor() {
super();
this.comercialPaperList = new PaperList(this);
this.counter = 0;
}
generateLogId() {
return this.numberLogs++;
}
getLatestLogId() {
return this.numberLogs;
}
}
class MyContract extends Contract {
constructor() {
// Unique namespace when multiple contracts per chaincode file
super('...');
}
/**
* Define a custom context for a citius log
*/
createContext() {
return new CustomContext();
I have a test transaction which increments the counter:
async incrementC (ctx) {
let before = await ctx.getLatestLogId();
console.log(before);
let new = await ctx.generateLogId();
console.log(new);
console.log("============== after inc")
let after = await ctx.getLatestLogId();
console.log(after);
}
On the first time I execute the incrementC transaction, I obtain 0, 1, 1, as expected.
On the following times, I obtain exacly the same, as if context did not store the updates.
Any insights?
A Custom context has the same scope as a non custom context, it is the context for the currently executing transaction only. It is not able to span across multiple transaction requests. If the documentation implies this then I would suggest that there is something wrong with the documentation and a jira should be raised at https://jira.hyperledger.org to raise this as an issue.
So unfortunately what you are trying to do will not work.

passing around NSManagedObjects

I get strange errors when I am trying to pass around NSManagedObject through several functions. (all are in the same VC).
Here are the two functions in question:
func syncLocal(item:NSManagedObject,completionHandler:(NSManagedObject!,SyncResponse)->Void) {
let savedValues = item.dictionaryWithValuesForKeys([
"score",
"progress",
"player"])
doUpload(savedParams) { //do a POST request using params with Alamofire
(success) in
if success {
completionHandler(item,.Success)
} else {
completionHandler(item,.Failure)
}
}
}
func getSavedScores() {
do {
debugPrint("TRYING TO FETCH LOCAL SCORES")
try frc.performFetch()
if let results = frc.sections?[0].objects as? [NSManagedObject] {
if results.count > 0 {
print("TOTAL SCORE COUNT: \(results.count)")
let incomplete = results.filter({$0.valueForKey("success") as! Bool == false })
print("INCOMPLETE COUNT: \(incomplete.count)")
let complete = results.filter({$0.valueForKey("success") as! Bool == true })
print("COMPLETE COUNT: \(complete.count)")
if incomplete.count > 0 {
for pendingItem in incomplete {
self.syncScoring(pendingItem) {
(returnItem,response) in
let footest = returnItem.valueForKey("player") //only works if stripping syncScoring blank
switch response { //response is an enum
case .Success:
print("SUCCESS")
case .Duplicate:
print("DUPLICATE")
case .Failure:
print("FAIL")
}
}
} //sorry for this pyramid of doom
}
}
}
} catch {
print("ERROR FETCHING RESULTS")
}
}
What I am trying to achieve:
1. Look for locally saved scores that could not submitted to the server.
2. If there are unsubmitted scores, start the POST call to the server.
3. If POST gets 200:ok mark item.key "success" with value "true"
For some odd reason I can not access returnItem at all in the code editor - only if I completely delete any code in syncLocal so it looks like
func syncLocal(item:NSManagedObject,completionHandler:(NSManagedObject!,SyncResponse)->Void) {
completionHandler(item,.Success)
}
If I do that I can access .syntax properties in the returning block down in the for loop.
Weirdly if I paste the stuff back in, in syncLocal the completion block keeps being functional, the app compiles and it will be executed properly.
Is this some kind of strange XCode7 Bug? Intended NSManagedObject behaviour?
line 1 was written with stripped, line 2 pasted rest call back in
There is thread confinement in Core Data managed object contexts. That means that you can use a particular managed object and its context only in one and the same thread.
In your code, you seem to be using controller-wide variables, such as item. I am assuming the item is a NSManagedObject or subclass thereof, and that its context is just one single context you are using in your app. The FRC context must be the main thread context (a NSManagedObjectContext with concurrency type NSMainThreadConcurrencyType).
Obviously, the callback from the server request will be on a background thread. So you cannot use your managed objects.
You have two solutions. Either you create a child context, do the updates you need to do, save, and then save the main context. This is a bit more involved and you can look for numerous examples and tutorials out there to get started. This is the standard and most robust solution.
Alternatively, inside your background callback, you simply make sure the context updates occur on the main thread.
dispatch_async(dispatch_get_main_queue()) {
// update your managed objects & save
}

Logging with akka. app design

Suppose I have scala app that is based on Futures, on scala.concurrent to handle the asyn / concurency (no actors were used so far).
In many places I use log4j for loggining stuff into log file.
Since it is I/O I suppose I may improve performance by sending the log message to LoggingActor
something like this:
def stuffTodo(arg:String)(implicit ex:ExecutionContext) : Future[Result] = {
// important performant work
// ..
logAcrot ! LogMessage("message-1")
// ...
}
where msg: case class LogMessage(msg:String, implicit ex:ExecutionContext)
then in ActorLog
def receive = {
case LogMessage(msg:String, ex:ExecutionContext) ⇒ {log.info(msg + ex)}
}
I've seen other approaches that basically wrap scala.concurent.ExecutionContext (with current thread) and use Mapped Diagnostic Context-magic (log4j) and do logging by attaching the thread-id to the log-message. But ultimately it blocks/slows down the thread/execution (as far as I understand) and makes the app slower.
In this case with this Actor, the logging stays independent / async and sequential at the same time.
Is it a good idea to go this way? Sharing experience story? pros/cons/concerns? Asking before trying on heavy load..
Akka already had good support for logging, which is documentated on the page Logging - Akka Documentation. I don't think it's necessary or desirable to create a logger actor in your system particularly when this mechanism already exists.
You may already use the LoggingAdaptor class that performs logging asynchronously, it does this by posting it onto an event-bus. You should be able to use this same mechanism in an actor or outside.
Take a look at Logging.scala
Logging Mixin
Scala has a mixin for actors, that creates the logger for the actor and allows the MDC to be set. From documentation:
import Logging.MDC
final case class Req(work: String, visitorId: Int)
class MdcActorMixin extends Actor with akka.actor.DiagnosticActorLogging {
var reqId = 0
override def mdc(currentMessage: Any): MDC = {
reqId += 1
val always = Map("requestId" -> reqId)
val perMessage = currentMessage match {
case r: Req => Map("visitorId" -> r.visitorId)
case _ => Map()
}
always ++ perMessage
}
def receive: Receive = {
case r: Req => {
log.info(s"Starting new request: ${r.work}")
}
}
}
Logging outside an actor
There are two examples in the Logging.scala file of createing a LoggingSource outside of an actor:
trait MyType { // as an example
def name: String
}
implicit val myLogSourceType: LogSource[MyType] = new LogSource[MyType] {
def genString(a: MyType) = a.name
}
class MyClass extends MyType {
val log = Logging(eventStream, this) // will use "hallo" as logSource
def name = "hallo"
}
The second variant is used for including the actor system’s address:
trait MyType { // as an example
def name: String
}
implicit val myLogSourceType: LogSource[MyType] = new LogSource[MyType] {
def genString(a: MyType) = a.name
def genString(a: MyType, s: ActorSystem) = a.name + "," + s
}
class MyClass extends MyType {
val sys = ActorSystem("sys")
val log = Logging(sys, this) // will use "hallo,akka://sys" as logSource
def name = "hallo"
}
The MDC can be set on the logging adaptor if you need it.
Logging Thread and MDC
The documentation also covers your point on the logging thread, when the logging is asynchronously performed by another thread.
Since the logging is done asynchronously the thread in which the
logging was performed is captured in Mapped Diagnostic Context (MDC)
with attribute name sourceThread. With Logback the thread name is
available with %X{sourceThread} specifier within the pattern layout
configuration.
It looks like over engineering.
I would go with logback, and implement custom asynchonous Appender, if I would do something like this, so I would not have to change my code, if I decide not to discontinue.
Otherwise logging libraries are pretty good bet, I would try maybe tuning it to get better performance, and rely on others experience in implementing this feature.

Using Squeryl with Akka Actors

So I'm trying to work with both Squeryl and Akka Actors. I've done a lot of searching and all I've been able to find is the following Google Group post:
https://groups.google.com/forum/#!topic/squeryl/M0iftMlYfpQ
I think I might have shot myself in the foot as I originally created this factory pattern so I could toss around Database objects.
object DatabaseType extends Enumeration {
type DatabaseType = Value
val Postgres = Value(1,"Postgres")
val H2 = Value(2,"H2")
}
object Database {
def getInstance(dbType : DatabaseType, jdbcUrl : String, username : String, password : String) : Database = {
Class.forName(jdbcDriver(dbType))
new Database(Session.create(
_root_.java.sql.DriverManager.getConnection(jdbcUrl,username,password),
squerylAdapter(dbType)))
}
private def jdbcDriver(db : DatabaseType) = {
db match {
case DatabaseType.Postgres => "org.postgresql.Driver"
case DatabaseType.H2 => "org.h2.Driver"
}
}
private def squerylAdapter(db : DatabaseType) = {
db match {
case DatabaseType.Postgres => new PostgreSqlAdapter
case DatabaseType.H2 => new H2Adapter
}
}
}
Originally in my implementation, I tried surrounding all my statements in using(session), but I'd keep getting the dreaded "No session is bound to the current thread" error, so I added the session.bindToCuirrentThread to the constructor.
class Database(session: Session) {
session.bindToCurrent
def failedBatch(filename : String, message : String, start : Timestamp = now, end : Timestamp = now) =
batch.insert(new Batch(0,filename,Some(start),Some(end),ProvisioningStatus.Fail,Some(message)))
def startBatch(batch_id : Long, start : Timestamp = now) =
batch update (b => where (b.id === batch_id) set (b.start := Some(start)))
...more functions
This worked reasonably well, until I got to Scala Actors.
class TransferActor() extends Actor {
def databaseInstance() = {
val dbConfig = config.getConfig("provisioning.database")
Database.getInstance(DatabaseType.Postgres,
dbConfig.getString("jdbcUrl"),
dbConfig.getString("username"),
dbConfig.getString("password"))
}
lazy val config = ConfigManager.current
override def receive: Actor.Receive = { /* .. do some work */
I constantly get the following:
[ERROR] [03/11/2014 17:02:57.720] [provisioning-system-akka.actor.default-dispatcher-4] [akka://provisioning-system/user/$c] No session is bound to current thread, a session must be created via Session.create
and bound to the thread via 'work' or 'bindToCurrentThread'
Usually this error occurs when a statement is executed outside of a transaction/inTrasaction block
java.lang.RuntimeException: No session is bound to current thread, a session must be created via Session.create
and bound to the thread via 'work' or 'bindToCurrentThread'
I'm getting a fresh Database object each time, not caching it with a lazy val, so shouldn't that constructor always get called and attach to my current thread? Does Akka attach different threads to different actors and swap them around? Should I just add a function to call session.bindToCurrentThread each time I'm in an actor? Seems kinda hacky.
Does Akka attach different threads to different actors and swap them around?
That's exactly how the actor model works. The idea is that you can have a small thread pool servicing a very large number of threads because actors only need to use a thread when they have a message waiting to be processed.
Some general tips for Squeryl.... A session is a one to one association with a JDBC connection. The main advantage of keeping Sessions open is that you can have a transaction open that gives you a consistent view of the database as you perform multiple operations. If you don't need that, make your session/transaction code granular to avoid these types of issues. If you do need it, don't rely on Sessions being available in a thread local context. Use the transaction(session){} or transaction(sessionFactory){} methods to explicitly tell Squeryl where you want your Session to come from.

Mocking DbEntityEntry

I am writing unit tests for my Generic Repository layer but i have some problems with DbEntityEntry.
My Update method is like below.
public virtual void Update(TEntity entityToUpdate) {
var entity = dbSet.Find(context.Entry<ITrackedEntity>(entityToUpdate).Entity.Id);
context.Entry(entity).CurrentValues.SetValues(entityToUpdate);
}
As you can see, context.Entry<TEntity>(TEntity entity) method is invoked by caller. So while unit tests this code throws null exception.
I had tried to mock context.Entry method but i couldn't because i couldn't provide return value.
My current test method is below
[Fact]
public void UpdateTest() {
AssetType _sample1 = new AssetType() {
AssetTypeID = 1,
IsContainer = true,
Name = "Sample 1"
};
IDbSet<AssetType> assetTypeDbSet = new FakeDbSet<AssetType>(_sample1);
Mock<IDbContext> mockContext = new Mock<IDbContext>();
mockContext.Setup(pc => pc.Set<AssetType>()).Returns(assetTypeDbSet);
_sample1.Name = "Sample 1.1";
var dbEntity = mockContext.Object.Entry<ITrackedEntity>(_sample1);
mockContext.Setup(p => p.Entry<ITrackedEntity>(It.IsAny<AssetType>())).Returns(dbEntity);
using(GenericRepository<AssetType> repo = new GenericRepository<AssetType>(mockContext.Object)) {
repo.Update(_sample1);
var result = repo.GetAll(0, 0);
Assert.Equal(1, result.Count);
var singleResult = repo.GetByID(1);
Assert.Equal(_sample1.Name, singleResult.Name);
}
}
I tried to get DbEntityEntry value by calling context.Entry<>() method at test but that wa returned null, too.
Also this class cannot be iniated with new operator. So i stucked..
And what is the point of unit testing this code? You fake the Find method, then you fake DbEntityEntry and there will be no real logic to test.
Anyway EF code is not unit testable in general. That is one reason why people believe that repository will help them because they mock / fake their repository when testing upper layer. Repository is then tested with integration tests to make sure that everything in EF and database works as expected.
You will not fake DbEntityEntry with Moq. Maybe with TypeMock Isolator or MS Fakes you will be able to do more but it is not needed in your sample code.

Resources