diff --git a/frontend/src/components/export/InstanceExport.vue b/frontend/src/components/export/InstanceExport.vue
index 681e0e6b9958f92c2c2db814a320b2362d90ed1a..844a0ab4d6845c90838cbb7648b9dc895701ef10 100644
--- a/frontend/src/components/export/InstanceExport.vue
+++ b/frontend/src/components/export/InstanceExport.vue
@@ -42,7 +42,7 @@ import { exportMixin, ExportType } from '@/components/mixins/mixins'
 export default class DataExport extends exportMixin {
   exportDialog = true
   mapFile: File | null = null
-  exportType = ExportType.JSON     // instance export is only available as JSON
+  exportType = ExportType.JSON // instance export is only available as JSON
 
   get studentMap () { return getters.state.studentMap }
 
diff --git a/frontend/src/components/mixins/mixins.ts b/frontend/src/components/mixins/mixins.ts
index 972371d08fd6f147cd4264f6836e1b75413c655a..bee6fc96f26736009064cc15d2944432acaedf2d 100644
--- a/frontend/src/components/mixins/mixins.ts
+++ b/frontend/src/components/mixins/mixins.ts
@@ -45,7 +45,7 @@ export class exportMixin extends Vue {
     } else {
       throw new Error('Unsupported export type')
     }
-    
+
     if (this.mapFile || this.mapFileLoaded) {
       this.getMappedExportFile(studentData)
     } else {
@@ -85,7 +85,7 @@ export class exportMixin extends Vue {
     }, '') + '\n' // add trailing newline
   }
 
-  optionalConvertAndCreatePopup(studentData: StudentExportItem[] | InstanceExportData) {
+  optionalConvertAndCreatePopup (studentData: StudentExportItem[] | InstanceExportData) {
     const convertedData = this.exportType === ExportType.CSV
       ? this.jsonToCSV(studentData as StudentExportItem[]) : studentData
       // we have a cast here because only student export may be converted to csv
@@ -136,6 +136,6 @@ export class exportMixin extends Vue {
     this.exportDialog = true
   }
 
-  applyMapping (exportData: StudentExportItem[] | InstanceExportData): void { throw new Error("Not implemented.") }
-  createDownloadPopup (content: string | StudentExportItem[] | InstanceExportData, fileType: ExportType): void { throw new Error("Not implemented.") }
+  applyMapping (exportData: StudentExportItem[] | InstanceExportData): void { throw new Error('Not implemented.') }
+  createDownloadPopup (content: string | StudentExportItem[] | InstanceExportData, fileType: ExportType): void { throw new Error('Not implemented.') }
 }
diff --git a/frontend/tests/unit/components/AutoLogout.spec.ts b/frontend/tests/unit/components/AutoLogout.spec.ts
index 5a5730f68f8b1ca55742d89365a85c028793c863..57501f45fdc2419a49dd0fa8f1e39cceae74fdf0 100644
--- a/frontend/tests/unit/components/AutoLogout.spec.ts
+++ b/frontend/tests/unit/components/AutoLogout.spec.ts
@@ -15,15 +15,15 @@ describe('Auto Logout Unit Tests', () => {
   let store: any = null
   let consoleTemp = {
     warn: console.warn,
-    error: console.error,
+    error: console.error
   }
 
-  before(function() {
-    console.warn = function() {}
-    console.error = function() {}
+  before(function () {
+    console.warn = function () {}
+    console.error = function () {}
   })
 
-  after(function() {
+  after(function () {
     console.warn = consoleTemp.warn
     console.error = consoleTemp.error
   })
@@ -37,7 +37,7 @@ describe('Auto Logout Unit Tests', () => {
   afterEach(() => {
     sinon.restore()
   })
-  
+
   it('should be hidden by default', () => {
     let wrapper = mount(AutoLogout, { localVue: localVue, store })
     wrapper.vm.$data.logoutDialog.should.equal(false)
diff --git a/frontend/tests/unit/mixins/exportMixin.spec.ts b/frontend/tests/unit/mixins/exportMixin.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a6c6cdd0570469bbbaf6cac9f04a927bc57eb4d1
--- /dev/null
+++ b/frontend/tests/unit/mixins/exportMixin.spec.ts
@@ -0,0 +1,153 @@
+import { exportMixin, ExportType } from '@/components/mixins/mixins'
+import sinon from 'sinon'
+import ax from '@/api'
+import testUtils, { FakeFileReader } from '@/../tests/utils/testUtils'
+import chai from 'chai'
+import { mutations as mut } from '@/store/mutations'
+chai.should()
+
+describe('Export Mixin Unit Tests', () => {
+  describe('getExportFile()', () => {
+    let e = new exportMixin()
+
+    afterEach(() => {
+      sinon.restore()
+    })
+
+    it('should fetch from the dataExport endpoint when called with "data"', async () => {
+      let stub = sinon.stub().resolves({ data: testUtils.studentExports })
+      sinon.replace(ax, 'post', stub)
+      sinon.replace(e, 'optionalConvertAndCreatePopup', () => {})
+      await e.getExportFile('data')
+      stub.calledWith('/api/export/json/').should.equal(true)
+    })
+    it('should fetch from instanceExport endpoint when called with "instance"', async () => {
+      let stub = sinon.stub().resolves({ data: testUtils.instanceExports })
+      sinon.replace(ax, 'get', stub)
+      sinon.replace(e, 'optionalConvertAndCreatePopup', () => {})
+      await e.getExportFile('instance')
+      stub.calledWith('/api/instance/export').should.equal(true)
+    })
+    it('should try to apply mapping when map file is given', async () => {
+      let spy = sinon.spy()
+      e.mapFile = testUtils.fakeFile
+      sinon.replace(ax, 'post', sinon.stub().resolves({ data: testUtils.studentExports }))
+      sinon.replace(e, 'getMappedExportFile', spy)
+      await e.getExportFile('data')
+      e.mapFile = null
+      spy.calledOnce.should.equal(true)
+      spy.calledWith(testUtils.studentExports).should.equal(true)
+    })
+    it('should call popup creation method with proper arguments when no mapping file is given', async () => {
+      let spy = sinon.spy()
+      sinon.replace(ax, 'post', sinon.stub().resolves({ data: testUtils.studentExports }))
+      sinon.replace(e, 'optionalConvertAndCreatePopup', spy)
+      await e.getExportFile('data')
+      spy.calledOnce.should.equal(true)
+      spy.calledWith(testUtils.studentExports).should.equal(true)
+    })
+  })
+  describe('optionalConvertAndCreatePopup()', () => {
+    let e = new exportMixin()
+
+    afterEach(() => {
+      sinon.restore()
+    })
+
+    it('should convert data to csv when csv is selected in dropdown', () => {
+      let stub = sinon.stub().returns('students as csv')
+      let spy = sinon.spy()
+      e.exportType = ExportType.CSV
+      sinon.replace(e, 'jsonToCSV', stub)
+      sinon.replace(e, 'createDownloadPopup', spy)
+      e.optionalConvertAndCreatePopup(testUtils.studentExports)
+      stub.calledWith(testUtils.studentExports).should.equal(true)
+      spy.calledWith('students as csv', ExportType.CSV).should.equal(true)
+    })
+  })
+  describe('readMapFileAndCommit', () => {
+    let e = new exportMixin()
+    // @ts-ignore
+    global.FileReader = FakeFileReader
+
+    afterEach(() => {
+      sinon.restore()
+    })
+
+    it('should throw an error when no mapfile was given', () => {
+      e.readMapFileAndCommit().catch((res) => {
+        res.should.not.equal(undefined)
+      })
+    })
+    it('should read selected mapFile', async () => {
+      e.mapFile = testUtils.fakeFile
+      let stub = sinon.stub()
+      // @ts-ignore
+      sinon.replace(global.FileReader.prototype, 'readAsText', stub)
+      e.readMapFileAndCommit()
+      e.mapFile = null
+      stub.args[0][0].name.should.equal(testUtils.fakeFile.name)
+    })
+    it('should parse file and trigger mutation when file was read', async () => {
+      e.mapFile = testUtils.fakeFile
+      let stub = sinon.stub().returns(testUtils.studentMap)
+      let mutSpy = sinon.spy()
+      sinon.replace(e, 'parseCSVMap', stub)
+      sinon.replace(mut, 'SET_STUDENT_MAP', mutSpy)
+      await e.readMapFileAndCommit()
+      e.mapFile = null
+      stub.calledOnce.should.equal(true)
+      stub.calledWithExactly('Grady testUtils fake result').should.equal(true)
+      mutSpy.calledWithExactly(testUtils.studentMap).should.equal(true)
+    })
+  })
+  describe('getMappedExportFile', () => {
+    let e = new exportMixin
+
+    afterEach(() => {
+      sinon.restore()
+    })
+
+    it('should read mapFile if it is not already loaded when used with instanceExports then apply mapping and create popup', async () => {
+      e.mapFile = testUtils.fakeFile
+      let spy = sinon.spy()
+      sinon.replace(e, 'readMapFileAndCommit', spy)
+      sinon.replace(e, 'applyMapping', () => {})
+      sinon.replace(e, 'optionalConvertAndCreatePopup', () => {})
+      await e.getMappedExportFile(testUtils.instanceExports)
+      e.mapFile = null
+      spy.called.should.equal(true)
+    })
+    it('should read mapFile if it is not already loaded when used with dataExport then apply mapping and create popup', async () => {
+      e.mapFile = testUtils.fakeFile
+      let spy = sinon.spy()
+      sinon.replace(e, 'readMapFileAndCommit', spy)
+      sinon.replace(e, 'applyMapping', () => {})
+      sinon.replace(e, 'optionalConvertAndCreatePopup', () => {})
+      await e.getMappedExportFile(testUtils.studentExports)
+      e.mapFile = null
+      spy.called.should.equal(true)
+    })
+    it('should apply the mapping and then open popup when mapFile is loaded', async () => {
+      sinon.replaceGetter(e, 'mapFileLoaded', () => { return true })
+      let mappingSpy = sinon.spy()
+      let popupSpy = sinon.spy()
+      sinon.replace(e, 'applyMapping', mappingSpy)
+      sinon.replace(e, 'optionalConvertAndCreatePopup', popupSpy)
+      await e.getMappedExportFile(testUtils.instanceExports)
+      e.mapFile = null
+      mappingSpy.calledWithExactly(testUtils.instanceExports).should.equal(true)
+      popupSpy.calledWithExactly(testUtils.instanceExports).should.equal(true)
+    })
+  })
+  describe('jsonToCSV()', () => {
+    let e = new exportMixin
+
+    it('should correctly parse JSON input to CSV with ; delimiter', () => {
+      let csv = e.jsonToCSV(testUtils.studentExports)
+      let expected =  'Matrikel;Name;Username;Sum;Exam;Password;test01\n' + 
+      '1000000;name;username;100;exam;pwd;100\n'
+      csv.should.equal(expected)
+    })
+  })
+})
diff --git a/frontend/tests/utils/testUtils.ts b/frontend/tests/utils/testUtils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..599cb750c818286d2d087ec554b6456d16dcec9e
--- /dev/null
+++ b/frontend/tests/utils/testUtils.ts
@@ -0,0 +1,40 @@
+import { StudentExportItem, InstanceExportData } from '@/api'
+
+export class FakeFileReader {
+  result: String = 'Grady testUtils fake result'
+
+  constructor () {}
+  // readAsText will execute the callback that was provided to onload
+  readAsText (file: File) { this.onload({ target: this }) }
+  onload: Function = () => { }
+}
+
+export default {
+  studentExports: <StudentExportItem[]> [{
+    Matrikel: '1000000',
+    Name: 'name',
+    Username: 'username',
+    Sum: 100,
+    Exam: 'exam',
+    Password: 'pwd',
+    Scores: [{ type: 'test01', score: 100 }]
+  }],
+  instanceExports: <InstanceExportData> {
+    students: [{
+      name: 'test',
+      matrikelNo: '1000000'
+    }]
+  },
+  fakeFile: <File> {
+    lastModified: 1,
+    name: 'Grady testUtils fake file',
+    size: 1,
+    slice: (start?: number | undefined, end?: number | undefined, contentType?: string | undefined): Blob => { return new Blob() },
+    type: 'fake file'
+  },
+  studentMap: {
+    '1000000': {
+      matrikelNo: '1000000', name: 'test'
+    }
+  }
+}